DATACOUCH-71 - Add support for JSR 303 validation.

This changeset adds validation support for entities, and since this depends
on events, the event functionality has been added as well. Now also generic
event listeners can be attached.
This commit is contained in:
Michael Nitschinger
2014-03-10 13:07:05 +01:00
parent 5a176a5b68
commit bef4d52ace
17 changed files with 724 additions and 7 deletions

16
pom.xml
View File

@@ -21,6 +21,7 @@
<couchbase>1.3.1</couchbase>
<jackson>2.3.1</jackson>
<springdata.commons>1.6.3.RELEASE</springdata.commons>
<validation>1.0.0.GA</validation>
</properties>
<dependencies>
@@ -74,6 +75,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
@@ -94,6 +102,14 @@
<optional>true</optional>
</dependency>
<!-- JSR 303 Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>

View File

@@ -28,6 +28,8 @@ import net.spy.memcached.ReplicateTo;
import net.spy.memcached.internal.OperationFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.QueryTimeoutException;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
@@ -39,6 +41,12 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.mapping.CouchbaseStorable;
import org.springframework.data.couchbase.core.mapping.event.AfterDeleteEvent;
import org.springframework.data.couchbase.core.mapping.event.AfterSaveEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeConvertEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeDeleteEvent;
import org.springframework.data.couchbase.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
@@ -56,7 +64,7 @@ import java.util.concurrent.TimeoutException;
/**
* @author Michael Nitschinger
*/
public class CouchbaseTemplate implements CouchbaseOperations {
public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventPublisherAware {
private static final Logger LOGGER = LoggerFactory.getLogger(CouchbaseTemplate.class);
private static final Collection<String> ITERABLE_CLASSES;
@@ -66,6 +74,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
protected final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
private final CouchbaseExceptionTranslator exceptionTranslator = new CouchbaseExceptionTranslator();
private final TranslationService translationService;
private ApplicationEventPublisher eventPublisher;
private CouchbaseConverter couchbaseConverter;
private WriteResultChecking writeResultChecking = DEFAULT_WRITE_RESULT_CHECKING;
@@ -86,6 +95,10 @@ public class CouchbaseTemplate implements CouchbaseOperations {
this(client, null, null);
}
public CouchbaseTemplate(final CouchbaseClient client, final TranslationService translationService) {
this(client, null, translationService);
}
public CouchbaseTemplate(final CouchbaseClient client, final CouchbaseConverter couchbaseConverter, final TranslationService translationService) {
this.client = client;
this.couchbaseConverter = couchbaseConverter == null ? getDefaultConverter() : couchbaseConverter;
@@ -93,11 +106,9 @@ public class CouchbaseTemplate implements CouchbaseOperations {
mappingContext = this.couchbaseConverter.getMappingContext();
}
public CouchbaseTemplate(final CouchbaseClient client, final TranslationService translationService) {
this.client = client;
couchbaseConverter = couchbaseConverter == null ? getDefaultConverter() : couchbaseConverter;
this.translationService = translationService == null ? getDefaultTranslationService() : translationService;
mappingContext = couchbaseConverter.getMappingContext();
@Override
public void setApplicationEventPublisher(final ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
private static CouchbaseConverter getDefaultConverter() {
@@ -282,9 +293,11 @@ public class CouchbaseTemplate implements CouchbaseOperations {
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
final Long version = versionProperty != null ? beanWrapper.getProperty(versionProperty, Long.class, true) : null;
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToSave));
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToSave, converted);
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToSave, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
@@ -310,6 +323,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
}
}
});
maybeEmitEvent(new AfterSaveEvent<Object>(objectToSave, converted));
}
@Override
@@ -334,9 +348,11 @@ public class CouchbaseTemplate implements CouchbaseOperations {
}
}
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToInsert));
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToInsert, converted);
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToInsert, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
@@ -354,6 +370,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
return result;
}
});
maybeEmitEvent(new AfterSaveEvent<Object>(objectToInsert, converted));
}
@Override
@@ -373,9 +390,11 @@ public class CouchbaseTemplate implements CouchbaseOperations {
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
final Long version = versionProperty != null ? beanWrapper.getProperty(versionProperty, Long.class, true) : null;
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToUpdate));
final CouchbaseDocument converted = new CouchbaseDocument();
couchbaseConverter.write(objectToUpdate, converted);
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToUpdate, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
@@ -397,6 +416,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
}
}
});
maybeEmitEvent(new AfterSaveEvent<Object>(objectToUpdate, converted));
}
@Override
@@ -410,6 +430,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
public void remove(final Object objectToRemove, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToRemove);
maybeEmitEvent(new BeforeDeleteEvent<Object>(objectToRemove));
if (objectToRemove instanceof String) {
execute(new BucketCallback<Boolean>() {
@Override
@@ -417,6 +438,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
return client.delete((String) objectToRemove, persistTo, replicateTo).get();
}
});
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
return;
}
@@ -429,6 +451,7 @@ public class CouchbaseTemplate implements CouchbaseOperations {
return client.delete(converted.getId());
}
});
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
}
@Override
@@ -455,4 +478,16 @@ public class CouchbaseTemplate implements CouchbaseOperations {
}
}
/**
* Helper method to publish an event if the event publisher is set.
*
* @param event the event to emit.
* @param <T> the enclosed type.
*/
protected <T> void maybeEmitEvent(final CouchbaseMappingEvent<T> event) {
if (eventPublisher != null) {
eventPublisher.publishEvent(event);
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* Base class to implement domain class specific {@link ApplicationListener}s.
*
* @author Jon Brisbin
* @author Oliver Gierke
* @author Martin Baumgartner
* @author Michael Nitschinger
*/
public class AbstractCouchbaseEventListener<E> implements ApplicationListener<CouchbaseMappingEvent<?>> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractCouchbaseEventListener.class);
private final Class<?> domainClass;
public AbstractCouchbaseEventListener() {
Class<?> typeArgument = GenericTypeResolver.resolveTypeArgument(getClass(), AbstractCouchbaseEventListener.class);
domainClass = typeArgument == null ? Object.class : typeArgument;
}
@SuppressWarnings("rawtypes")
public void onApplicationEvent(CouchbaseMappingEvent<?> event) {
if (event instanceof BeforeDeleteEvent) {
onBeforeDelete(event.getDocument());
return;
} else if (event instanceof AfterDeleteEvent) {
onAfterDelete(event.getDocument());
return;
}
E source = (E) event.getSource();
// Check for matching domain type and invoke callbacks
if (source != null && !domainClass.isAssignableFrom(source.getClass())) {
return;
}
if (event instanceof BeforeConvertEvent) {
onBeforeConvert(source);
} else if (event instanceof BeforeSaveEvent) {
onBeforeSave(source, event.getDocument());
} else if (event instanceof AfterSaveEvent) {
onAfterSave(source, event.getDocument());
}
}
public void onBeforeConvert(E source) {
if (LOG.isDebugEnabled()) {
LOG.debug("onBeforeConvert({})", source);
}
}
public void onBeforeSave(E source, CouchbaseDocument doc) {
if (LOG.isDebugEnabled()) {
LOG.debug("onBeforeSave({}, {})", source, doc);
}
}
public void onAfterSave(E source, CouchbaseDocument doc) {
if (LOG.isDebugEnabled()) {
LOG.debug("onAfterSave({}, {})", source, doc);
}
}
public void onAfterDelete(CouchbaseDocument doc) {
if (LOG.isDebugEnabled()) {
LOG.debug("onAfterConvert({})", doc);
}
}
public void onBeforeDelete(CouchbaseDocument doc) {
if (LOG.isDebugEnabled()) {
LOG.debug("onAfterConvert({})", doc);
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
/**
* @author Michael Nitschinger
*/
public class AfterDeleteEvent<E> extends CouchbaseMappingEvent<E> {
public AfterDeleteEvent(E source) {
super(source, null);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* @author Michael Nitschinger
*/
public class AfterSaveEvent<E> extends CouchbaseMappingEvent<E> {
public AfterSaveEvent(E source, CouchbaseDocument document) {
super(source, document);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
/**
* @author Michael Nitschinger
*/
public class BeforeConvertEvent<E> extends CouchbaseMappingEvent<E> {
public BeforeConvertEvent(E source) {
super(source, null);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
/**
* @author Michael Nitschinger
*/
public class BeforeDeleteEvent<E> extends CouchbaseMappingEvent<E> {
public BeforeDeleteEvent(E source) {
super(source, null);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* @author Michael Nitschinger
*/
public class BeforeSaveEvent<E> extends CouchbaseMappingEvent<E> {
public BeforeSaveEvent(E source, CouchbaseDocument document) {
super(source, document);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* A mapping event.
*
* @author Michael Nitschinger
*/
public class CouchbaseMappingEvent<T> extends ApplicationEvent {
private final CouchbaseDocument document;
public CouchbaseMappingEvent(T source, CouchbaseDocument document) {
super(source);
this.document = document;
}
public CouchbaseDocument getDocument() {
return document;
}
@SuppressWarnings("unchecked")
@Override
public T getSource() {
return (T) super.getSource();
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* A{@link ApplicationListener} for Couchbase mapping events logging the events.
*
* @author Michael Nitschinger
*/
public class LoggingEventListener extends AbstractCouchbaseEventListener<Object> {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingEventListener.class);
@Override
public void onBeforeDelete(CouchbaseDocument doc) {
LOGGER.info("onBeforeDelete: {}", doc);
}
@Override
public void onAfterDelete(CouchbaseDocument doc) {
LOGGER.info("onAfterDelete: {}", doc);
}
@Override
public void onAfterSave(Object source, CouchbaseDocument doc) {
LOGGER.info("onAfterSave: {}, {}", source, doc);
}
@Override
public void onBeforeSave(Object source, CouchbaseDocument doc) {
LOGGER.info("onBeforeSave: {}, {}", source, doc);
}
@Override
public void onBeforeConvert(Object source) {
LOGGER.info("onBeforeConvert: {}, {}", source);
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.util.Assert;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.util.Set;
/**
* javax.validation dependant entities validator. When it is registered as Spring component its automatically invoked
* before entities are saved in database.
*
* @author Maciej Walkowiak
* @author Michael Nitschinger
*/
public class ValidatingCouchbaseEventListener extends AbstractCouchbaseEventListener<Object> {
private static final Logger LOG = LoggerFactory.getLogger(ValidatingCouchbaseEventListener.class);
private final Validator validator;
/**
* Creates a new {@link ValidatingCouchbaseEventListener} using the given {@link Validator}.
*
* @param validator must not be {@literal null}.
*/
public ValidatingCouchbaseEventListener(Validator validator) {
Assert.notNull(validator);
this.validator = validator;
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void onBeforeSave(Object source, CouchbaseDocument dbo) {
LOG.debug("Validating object: {}", source);
Set violations = validator.validate(source);
if (!violations.isEmpty()) {
LOG.info("During object: {} validation violations found: {}", source, violations);
throw new ConstraintViolationException(violations);
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
/**
* @author Michael Nitschinger
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = EventContextConfiguration.class)
public class AbstractCouchbaseEventListenerTests {
@Autowired
private CouchbaseTemplate couchbaseTemplate;
@Autowired
private SimpleMappingEventListener eventListener;
@Test
public void shouldEmitEvents() {
assertEquals(0, eventListener.onBeforeSaveEvents.size());
assertEquals(0, eventListener.onAfterSaveEvents.size());
assertEquals(0, eventListener.onBeforeConvertEvents.size());
couchbaseTemplate.save(new User("john smith", 18));
assertEquals(1, eventListener.onBeforeSaveEvents.size());
assertEquals(1, eventListener.onAfterSaveEvents.size());
assertEquals(1, eventListener.onBeforeConvertEvents.size());
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.TestApplicationConfig;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
/**
* @author Michael Nitschinger
*/
@Configuration
public class EventContextConfiguration extends TestApplicationConfig {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
@Bean
public ValidatingCouchbaseEventListener validatingCouchbaseEventListener() {
return new ValidatingCouchbaseEventListener(validator());
}
@Bean
public SimpleMappingEventListener simpleMappingEventListener() {
return new SimpleMappingEventListener();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import java.util.ArrayList;
/**
* @author Michael Nitschinger
*/
public class SimpleMappingEventListener extends AbstractCouchbaseEventListener<Object> {
public final ArrayList<BeforeConvertEvent<Object>> onBeforeConvertEvents = new ArrayList<BeforeConvertEvent<Object>>();
public final ArrayList<BeforeSaveEvent<Object>> onBeforeSaveEvents = new ArrayList<BeforeSaveEvent<Object>>();
public final ArrayList<AfterSaveEvent<Object>> onAfterSaveEvents = new ArrayList<AfterSaveEvent<Object>>();
@Override
public void onBeforeConvert(Object source) {
onBeforeConvertEvents.add(new BeforeConvertEvent<Object>(source));
}
@Override
public void onBeforeSave(Object source, CouchbaseDocument doc) {
onBeforeSaveEvents.add(new BeforeSaveEvent<Object>(source, doc));
}
@Override
public void onAfterSave(Object source, CouchbaseDocument doc) {
onAfterSaveEvents.add(new AfterSaveEvent<Object>(source, doc));
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.springframework.data.annotation.Id;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
/**
* @author Michael Nitschinger
*/
public class User {
@Id
private String id;
@Size(min = 10)
private String name;
@Min(18)
private Integer age;
public User(String name, Integer age) {
id = "id";
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.mapping.event;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.validation.ConstraintViolationException;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* @author Michael Nitschinger
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = EventContextConfiguration.class)
public class ValidatingCouchbaseEventListenerTests {
@Autowired
CouchbaseTemplate template;
@Test
public void shouldThrowConstraintViolationException() {
User user = new User("john", 17);
try {
template.save(user);
fail();
} catch (ConstraintViolationException e) {
assertThat(e.getConstraintViolations().size(), equalTo(2));
}
}
@Test
public void shouldNotThrowAnyExceptions() {
template.save(new User("john smith", 18));
}
}

View File

@@ -15,4 +15,5 @@ Import-Template:
org.w3c.*;version="0.0.0",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.slf4j.*;version="${slf4j:[=.=.=,+1.0.0)}",
org.joda.time.*;version="${jodatime:[=.=.=,+1.0.0)}";resolution:=optional
org.joda.time.*;version="${jodatime:[=.=.=,+1.0.0)}";resolution:=optional,
javax.validation.*;version="${validation:[=.=.=.=,+1.0.0)}";resolution:=optional