diff --git a/pom.xml b/pom.xml index 47999643..7c56738c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ 1.3.1 2.3.1 1.6.3.RELEASE + 1.0.0.GA @@ -74,6 +75,13 @@ test + + org.hibernate + hibernate-validator + 4.2.0.Final + test + + org.apache.httpcomponents httpclient @@ -94,6 +102,14 @@ true + + + javax.validation + validation-api + ${validation} + true + + org.hamcrest hamcrest-all diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 431faa6e..e8631d6f 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -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 ITERABLE_CLASSES; @@ -66,6 +74,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { protected final MappingContext, 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(objectToSave)); final CouchbaseDocument converted = new CouchbaseDocument(); couchbaseConverter.write(objectToSave, converted); + maybeEmitEvent(new BeforeSaveEvent(objectToSave, converted)); execute(new BucketCallback() { @Override public Boolean doInBucket() throws InterruptedException, ExecutionException { @@ -310,6 +323,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { } } }); + maybeEmitEvent(new AfterSaveEvent(objectToSave, converted)); } @Override @@ -334,9 +348,11 @@ public class CouchbaseTemplate implements CouchbaseOperations { } } + maybeEmitEvent(new BeforeConvertEvent(objectToInsert)); final CouchbaseDocument converted = new CouchbaseDocument(); couchbaseConverter.write(objectToInsert, converted); + maybeEmitEvent(new BeforeSaveEvent(objectToInsert, converted)); execute(new BucketCallback() { @Override public Boolean doInBucket() throws InterruptedException, ExecutionException { @@ -354,6 +370,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { return result; } }); + maybeEmitEvent(new AfterSaveEvent(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(objectToUpdate)); final CouchbaseDocument converted = new CouchbaseDocument(); couchbaseConverter.write(objectToUpdate, converted); + maybeEmitEvent(new BeforeSaveEvent(objectToUpdate, converted)); execute(new BucketCallback() { @Override public Boolean doInBucket() throws InterruptedException, ExecutionException { @@ -397,6 +416,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { } } }); + maybeEmitEvent(new AfterSaveEvent(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(objectToRemove)); if (objectToRemove instanceof String) { execute(new BucketCallback() { @Override @@ -417,6 +438,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { return client.delete((String) objectToRemove, persistTo, replicateTo).get(); } }); + maybeEmitEvent(new AfterDeleteEvent(objectToRemove)); return; } @@ -429,6 +451,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { return client.delete(converted.getId()); } }); + maybeEmitEvent(new AfterDeleteEvent(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 the enclosed type. + */ + protected void maybeEmitEvent(final CouchbaseMappingEvent event) { + if (eventPublisher != null) { + eventPublisher.publishEvent(event); + } + } + } diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListener.java new file mode 100644 index 00000000..7aea349b --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListener.java @@ -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 implements ApplicationListener> { + 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); + } + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/AfterDeleteEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AfterDeleteEvent.java new file mode 100644 index 00000000..607ef88a --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AfterDeleteEvent.java @@ -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 extends CouchbaseMappingEvent { + + public AfterDeleteEvent(E source) { + super(source, null); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/AfterSaveEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AfterSaveEvent.java new file mode 100644 index 00000000..517ec20d --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/AfterSaveEvent.java @@ -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 extends CouchbaseMappingEvent { + + public AfterSaveEvent(E source, CouchbaseDocument document) { + super(source, document); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeConvertEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeConvertEvent.java new file mode 100644 index 00000000..8a6fec1e --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeConvertEvent.java @@ -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 extends CouchbaseMappingEvent { + + public BeforeConvertEvent(E source) { + super(source, null); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeDeleteEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeDeleteEvent.java new file mode 100644 index 00000000..9cc83ba8 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeDeleteEvent.java @@ -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 extends CouchbaseMappingEvent { + + public BeforeDeleteEvent(E source) { + super(source, null); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeSaveEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeSaveEvent.java new file mode 100644 index 00000000..397cc7ca --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/BeforeSaveEvent.java @@ -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 extends CouchbaseMappingEvent { + + public BeforeSaveEvent(E source, CouchbaseDocument document) { + super(source, document); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/CouchbaseMappingEvent.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/CouchbaseMappingEvent.java new file mode 100644 index 00000000..de5da3d4 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/CouchbaseMappingEvent.java @@ -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 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(); + } +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/LoggingEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/LoggingEventListener.java new file mode 100644 index 00000000..f78f95db --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/LoggingEventListener.java @@ -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 { + + 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); + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java new file mode 100644 index 00000000..353bebd2 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListener.java @@ -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 { + + 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); + } + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListenerTests.java b/src/test/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListenerTests.java new file mode 100644 index 00000000..5de20613 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/event/AbstractCouchbaseEventListenerTests.java @@ -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()); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/event/EventContextConfiguration.java b/src/test/java/org/springframework/data/couchbase/core/mapping/event/EventContextConfiguration.java new file mode 100644 index 00000000..6015046b --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/event/EventContextConfiguration.java @@ -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(); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/event/SimpleMappingEventListener.java b/src/test/java/org/springframework/data/couchbase/core/mapping/event/SimpleMappingEventListener.java new file mode 100644 index 00000000..9048f3ca --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/event/SimpleMappingEventListener.java @@ -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 { + + public final ArrayList> onBeforeConvertEvents = new ArrayList>(); + public final ArrayList> onBeforeSaveEvents = new ArrayList>(); + public final ArrayList> onAfterSaveEvents = new ArrayList>(); + + @Override + public void onBeforeConvert(Object source) { + onBeforeConvertEvents.add(new BeforeConvertEvent(source)); + } + + @Override + public void onBeforeSave(Object source, CouchbaseDocument doc) { + onBeforeSaveEvents.add(new BeforeSaveEvent(source, doc)); + } + + @Override + public void onAfterSave(Object source, CouchbaseDocument doc) { + onAfterSaveEvents.add(new AfterSaveEvent(source, doc)); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/event/User.java b/src/test/java/org/springframework/data/couchbase/core/mapping/event/User.java new file mode 100644 index 00000000..597c7fa5 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/event/User.java @@ -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; + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListenerTests.java b/src/test/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListenerTests.java new file mode 100644 index 00000000..05df2eea --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/event/ValidatingCouchbaseEventListenerTests.java @@ -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)); + } +} diff --git a/template.mf b/template.mf index cf01b4ea..3e396567 100644 --- a/template.mf +++ b/template.mf @@ -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