re-add event notifications to template

This commit is contained in:
Simon Baslé
2015-06-29 18:36:10 +02:00
parent bd4f963d70
commit 87bf10a076
15 changed files with 703 additions and 6 deletions

View File

@@ -55,6 +55,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.context.MappingContext;
import org.springframework.data.mapping.model.BeanWrapper;
@@ -179,6 +185,18 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
this.eventPublisher = eventPublisher;
}
/**
* 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);
}
}
@Override
public void save(Object objectToSave) {
save(objectToSave, PersistTo.NONE, ReplicateTo.NONE);
@@ -363,11 +381,11 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
final CouchbasePersistentProperty versionProperty = persistentEntity.getVersionProperty();
final Long version = versionProperty != null ? beanWrapper.getProperty(versionProperty, Long.class) : null;
//TODO event beforeConvert
maybeEmitEvent(new BeforeConvertEvent<Object>(objectToPersist));
final CouchbaseDocument converted = new CouchbaseDocument();
converter.write(objectToPersist, converted);
//TODO event beforeSave
maybeEmitEvent(new BeforeSaveEvent<Object>(objectToPersist, converted));
execute(new BucketCallback<Boolean>() {
@Override
public Boolean doInBucket() throws InterruptedException, ExecutionException {
@@ -401,13 +419,13 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
}
}
});
//TODO event afterSave
maybeEmitEvent(new AfterSaveEvent<Object>(objectToPersist, converted));
}
private void doRemove(final Object objectToRemove, final PersistTo persistTo, final ReplicateTo replicateTo) {
ensureNotIterable(objectToRemove);
//TODO event BeforeDelete
maybeEmitEvent(new BeforeDeleteEvent<Object>(objectToRemove));
if (objectToRemove instanceof String) {
execute(new BucketCallback<Boolean>() {
@Override
@@ -417,7 +435,7 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
return deletedDoc != null;
}
});
//TODO event afterDelete
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
return;
}
@@ -432,7 +450,7 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationEventP
return deletedDoc != null;
}
});
//TODO event afterDelete
maybeEmitEvent(new AfterDeleteEvent<Object>(objectToRemove));
}
private <T> T mapToEntity(String id, Document<String> data, Class<T> entityClass) {

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2012-2015 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) {
E source = (E) event.getSource();
// Check for matching domain type and invoke callbacks
if (source != null && !domainClass.isAssignableFrom(source.getClass())) {
return;
}
if (event instanceof BeforeDeleteEvent) {
onBeforeDelete(event.getSource(), event.getDocument());
return;
}
else if (event instanceof AfterDeleteEvent) {
onAfterDelete(event.getSource(), event.getDocument());
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(Object source, CouchbaseDocument doc) {
if (LOG.isDebugEnabled()) {
LOG.debug("onAfterConvert({})", doc);
}
}
public void onBeforeDelete(Object source, CouchbaseDocument doc) {
if (LOG.isDebugEnabled()) {
LOG.debug("onAfterConvert({})", doc);
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2012-2015 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 2012-2015 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 2012-2015 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 2012-2015 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 2012-2015 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 2012-2015 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,59 @@
/*
* Copyright 2012-2015 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(Object source, CouchbaseDocument doc) {
LOGGER.info("onBeforeDelete: {}, {}", source, doc);
}
@Override
public void onAfterDelete(Object source, CouchbaseDocument doc) {
LOGGER.info("onAfterDelete: {} {}", source, 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,67 @@
/*
* Copyright 2012-2015 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 java.util.Set;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.util.Assert;
/**
* 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,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 static org.junit.Assert.assertEquals;
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.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* @author Michael Nitschinger
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = EventContextConfiguration.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class AbstractCouchbaseEventListenerTests {
@Autowired
private CouchbaseTemplate couchbaseTemplate;
@Autowired
private SimpleMappingEventListener eventListener;
@Test
public void shouldEmitEvents() {
int beforeSave = eventListener.onBeforeSaveEvents.size();
int afterSave = eventListener.onAfterSaveEvents.size();
int beforeConvert = eventListener.onBeforeConvertEvents.size();
couchbaseTemplate.save(new User("john smith", 18));
assertEquals(beforeSave + 1, eventListener.onBeforeSaveEvents.size());
assertEquals(afterSave + 1, eventListener.onAfterSaveEvents.size());
assertEquals(beforeConvert + 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.UnitTestApplicationConfig;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
/**
* @author Michael Nitschinger
*/
@Configuration
public class EventContextConfiguration extends UnitTestApplicationConfig {
@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 java.util.ArrayList;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
/**
* @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 javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import org.springframework.data.annotation.Id;
/**
* @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,60 @@
/*
* 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 static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import javax.validation.ConstraintViolationException;
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;
/**
* @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));
}
}