From 8b50af07ce5cf0ce1cfac3874f1052e222bffbbb Mon Sep 17 00:00:00 2001 From: Martin Baumgartner Date: Sun, 24 Mar 2013 16:56:14 +0100 Subject: [PATCH] DATAMONGO-545 - Add BeforeDeleteEvent and AfterDeleteEvent. Added events for before and after deletion of documents. Polished logging code in AbstractMongoEventListener. --- .../data/mongodb/core/MongoTemplate.java | 7 ++ .../mapping/event/AbstractDeleteEvent.java | 50 +++++++++++ .../event/AbstractMongoEventListener.java | 42 +++++++-- .../core/mapping/event/AfterDeleteEvent.java | 39 ++++++++ .../core/mapping/event/BeforeDeleteEvent.java | 39 ++++++++ .../mapping/event/LoggingEventListener.java | 41 ++++++--- .../AbstractMongoEventListenerUnitTests.java | 90 +++++++++++++++++-- 7 files changed, 285 insertions(+), 23 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 77fa0fb9e..ad1a0cd19 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -68,9 +68,11 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; import org.springframework.data.mongodb.core.mapping.event.AfterConvertEvent; +import org.springframework.data.mongodb.core.mapping.event.AfterDeleteEvent; import org.springframework.data.mongodb.core.mapping.event.AfterLoadEvent; import org.springframework.data.mongodb.core.mapping.event.AfterSaveEvent; import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent; +import org.springframework.data.mongodb.core.mapping.event.BeforeDeleteEvent; import org.springframework.data.mongodb.core.mapping.event.BeforeSaveEvent; import org.springframework.data.mongodb.core.mapping.event.MongoMappingEvent; import org.springframework.data.mongodb.core.mapreduce.GroupBy; @@ -1031,6 +1033,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { execute(collectionName, new CollectionCallback() { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { + maybeEmitEvent(new BeforeDeleteEvent(queryObject, entityClass)); + DBObject dboq = mapper.getMappedObject(queryObject, entity); MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, @@ -1044,6 +1048,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { WriteResult wr = writeConcernToUse == null ? collection.remove(dboq) : collection.remove(dboq, writeConcernToUse); handleAnyWriteResultErrors(wr, dboq, MongoActionOperation.REMOVE); + + maybeEmitEvent(new AfterDeleteEvent(queryObject, entityClass)); + return null; } }); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java new file mode 100644 index 000000000..1e1dadbef --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractDeleteEvent.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013 by the original author(s). + * + * 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.mongodb.core.mapping.event; + +import com.mongodb.DBObject; + +/** + * Base class for delete events. + * + * @author Martin Baumgartner + */ +public abstract class AbstractDeleteEvent extends MongoMappingEvent { + + private static final long serialVersionUID = 1L; + private final Class type; + + /** + * Creates a new {@link AbstractDeleteEvent} for the given {@link DBObject} and type. + * + * @param dbo must not be {@literal null}. + * @param type , possibly be {@literal null}. + */ + public AbstractDeleteEvent(DBObject dbo, Class type) { + + super(dbo, dbo); + this.type = type; + } + + /** + * Returns the type for which the {@link AbstractDeleteEvent} shall be invoked for. + * + * @return + */ + public Class getType() { + return type; + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListener.java index 9467dad8b..d30e4f19b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 by the original author(s). + * Copyright 2011-2013 by the original author(s). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import com.mongodb.DBObject; * * @author Jon Brisbin * @author Oliver Gierke + * @author Martin Baumgartner */ public abstract class AbstractMongoEventListener implements ApplicationListener> { @@ -45,6 +46,7 @@ public abstract class AbstractMongoEventListener implements ApplicationListen * (non-Javadoc) * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) */ + @SuppressWarnings("rawtypes") public void onApplicationEvent(MongoMappingEvent event) { if (event instanceof AfterLoadEvent) { @@ -57,6 +59,22 @@ public abstract class AbstractMongoEventListener implements ApplicationListen return; } + if (event instanceof AbstractDeleteEvent) { + + Class eventDomainType = ((AbstractDeleteEvent) event).getType(); + + if (eventDomainType != null && domainClass.isAssignableFrom(eventDomainType)) { + if (event instanceof BeforeDeleteEvent) { + onBeforeDelete(event.getDBObject()); + } + if (event instanceof AfterDeleteEvent) { + onAfterDelete(event.getDBObject()); + } + } + + return; + } + @SuppressWarnings("unchecked") E source = (E) event.getSource(); @@ -78,31 +96,43 @@ public abstract class AbstractMongoEventListener implements ApplicationListen public void onBeforeConvert(E source) { if (LOG.isDebugEnabled()) { - LOG.debug("onBeforeConvert(" + source + ")"); + LOG.debug("onBeforeConvert({})", source); } } public void onBeforeSave(E source, DBObject dbo) { if (LOG.isDebugEnabled()) { - LOG.debug("onBeforeSave(" + source + ", " + dbo + ")"); + LOG.debug("onBeforeSave({}, {})", source, dbo); } } public void onAfterSave(E source, DBObject dbo) { if (LOG.isDebugEnabled()) { - LOG.debug("onAfterSave(" + source + ", " + dbo + ")"); + LOG.debug("onAfterSave({}, {})", source, dbo); } } public void onAfterLoad(DBObject dbo) { if (LOG.isDebugEnabled()) { - LOG.debug("onAfterLoad(" + dbo + ")"); + LOG.debug("onAfterLoad({})", dbo); } } public void onAfterConvert(DBObject dbo, E source) { if (LOG.isDebugEnabled()) { - LOG.debug("onAfterConvert(" + dbo + "," + source + ")"); + LOG.debug("onAfterConvert({}, {})", dbo, source); + } + } + + public void onAfterDelete(DBObject dbo) { + if (LOG.isDebugEnabled()) { + LOG.debug("onAfterConvert({})", dbo); + } + } + + public void onBeforeDelete(DBObject dbo) { + if (LOG.isDebugEnabled()) { + LOG.debug("onAfterConvert({})", dbo); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java new file mode 100644 index 000000000..f6495fc85 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/AfterDeleteEvent.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013 by the original author(s). + * + * 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.mongodb.core.mapping.event; + +import com.mongodb.DBObject; + +/** + * Event being thrown after a single or a set of documents has/have been deleted. The {@link DBObject} held in the event + * will be the query document after it has been mapped onto the domain type handled. + * + * @author Martin Baumgartner + */ +public class AfterDeleteEvent extends AbstractDeleteEvent { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new {@link AfterDeleteEvent} for the given {@link DBObject} and type. + * + * @param dbo must not be {@literal null}. + * @param type can be {@literal null}. + */ + public AfterDeleteEvent(DBObject dbo, Class type) { + super(dbo, type); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java new file mode 100644 index 000000000..ce1848947 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/BeforeDeleteEvent.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013 by the original author(s). + * + * 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.mongodb.core.mapping.event; + +import com.mongodb.DBObject; + +/** + * Event being thrown before a document is deleted. The {@link DBObject} held in the event will represent the query + * document before being mapped based on the domain class handled. + * + * @author Martin Baumgartner + */ +public class BeforeDeleteEvent extends AbstractDeleteEvent { + + private static final long serialVersionUID = -2627547705679734497L; + + /** + * Creates a new {@link BeforeDeleteEvent} for the given {@link DBObject} and type. + * + * @param dbo must not be {@literal null}. + * @param type can be {@literal null}. + */ + public BeforeDeleteEvent(DBObject dbo, Class type) { + super(dbo, type); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/LoggingEventListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/LoggingEventListener.java index 54ccde4a9..4676bd792 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/LoggingEventListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/event/LoggingEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-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. @@ -17,15 +17,19 @@ package org.springframework.data.mongodb.core.mapping.event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; import com.mongodb.DBObject; /** - * @author Jon Brisbin + * {@link ApplicationListener} for Mongo mapping events logging the events. + * + * @author Jon Brisbin + * @author Martin Baumgartner */ public class LoggingEventListener extends AbstractMongoEventListener { - private static final Logger log = LoggerFactory.getLogger(LoggingEventListener.class); + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingEventListener.class); /* * (non-Javadoc) @@ -33,7 +37,7 @@ public class LoggingEventListener extends AbstractMongoEventListener { */ @Override public void onBeforeConvert(Object source) { - log.info("onBeforeConvert: " + source); + LOGGER.info("onBeforeConvert: {}", source); } /* @@ -42,10 +46,7 @@ public class LoggingEventListener extends AbstractMongoEventListener { */ @Override public void onBeforeSave(Object source, DBObject dbo) { - try { - log.info("onBeforeSave: " + source + ", " + dbo); - } catch (Throwable ignored) { - } + LOGGER.info("onBeforeSave: {}, {}", source, dbo); } /* @@ -54,7 +55,7 @@ public class LoggingEventListener extends AbstractMongoEventListener { */ @Override public void onAfterSave(Object source, DBObject dbo) { - log.info("onAfterSave: " + source + ", " + dbo); + LOGGER.info("onAfterSave: {}, {}", source, dbo); } /* @@ -63,7 +64,7 @@ public class LoggingEventListener extends AbstractMongoEventListener { */ @Override public void onAfterLoad(DBObject dbo) { - log.info("onAfterLoad: " + dbo); + LOGGER.info("onAfterLoad: {}", dbo); } /* @@ -72,6 +73,24 @@ public class LoggingEventListener extends AbstractMongoEventListener { */ @Override public void onAfterConvert(DBObject dbo, Object source) { - log.info("onAfterConvert: " + dbo + ", " + source); + LOGGER.info("onAfterConvert: {}, {}", dbo, source); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onAfterDelete(com.mongodb.DBObject) + */ + @Override + public void onAfterDelete(DBObject dbo) { + LOGGER.info("onAfterDelete: {}", dbo); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener#onBeforeDelete(com.mongodb.DBObject) + */ + @Override + public void onBeforeDelete(DBObject dbo) { + LOGGER.info("onBeforeDelete: {}", dbo); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListenerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListenerUnitTests.java index 863d2dbc0..3fec0e988 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListenerUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/event/AbstractMongoEventListenerUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 by the original author(s). + * Copyright 2011-2013 by the original author(s). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import com.mongodb.DBObject; * Unit tests for {@link AbstractMongoEventListener}. * * @author Oliver Gierke + * @author Martin Baumgartner */ public class AbstractMongoEventListenerUnitTests { @@ -61,7 +62,7 @@ public class AbstractMongoEventListenerUnitTests { } /** - * @see DATADOC-289 + * @see DATAMONGO-289 */ @Test public void afterLoadEffectGetsHandledCorrectly() { @@ -72,7 +73,7 @@ public class AbstractMongoEventListenerUnitTests { } /** - * @see DATADOC-289 + * @see DATAMONGO-289 */ @Test public void afterLoadEventGetsFilteredForDomainType() { @@ -87,7 +88,7 @@ public class AbstractMongoEventListenerUnitTests { } /** - * @see DATADOC-289 + * @see DATAMONGO-289 */ @Test public void afterLoadEventGetsFilteredForDomainTypeWorksForSubtypes() { @@ -102,7 +103,7 @@ public class AbstractMongoEventListenerUnitTests { } /** - * @see DATADOC-289 + * @see DATAMONGO-289 */ @Test public void afterLoadEventGetsFilteredForDomainTypeWorksForSubtypes2() { @@ -117,7 +118,7 @@ public class AbstractMongoEventListenerUnitTests { } /** - * @see DATADOC-333 + * @see DATAMONGO-333 */ @Test @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -127,10 +128,64 @@ public class AbstractMongoEventListenerUnitTests { listener.onApplicationEvent(new MongoMappingEvent(new Object(), new BasicDBObject())); } + /** + * @see DATAMONGO-545 + */ + @Test + public void invokeContactCallbackForPersonEvent() { + + MongoMappingEvent event = new BeforeDeleteEvent(new BasicDBObject(), Person.class); + SampleContactEventListener listener = new SampleContactEventListener(); + listener.onApplicationEvent(event); + + assertThat(listener.invokedOnBeforeDelete, is(true)); + } + + /** + * @see DATAMONGO-545 + */ + @Test + public void invokePersonCallbackForPersonEvent() { + + MongoMappingEvent event = new BeforeDeleteEvent(new BasicDBObject(), Person.class); + SamplePersonEventListener listener = new SamplePersonEventListener(); + listener.onApplicationEvent(event); + + assertThat(listener.invokedOnBeforeDelete, is(true)); + } + + /** + * @see DATAMONGO-545 + */ + @Test + public void dontInvokePersonCallbackForAccountEvent() { + + MongoMappingEvent event = new BeforeDeleteEvent(new BasicDBObject(), Account.class); + SamplePersonEventListener listener = new SamplePersonEventListener(); + listener.onApplicationEvent(event); + + assertThat(listener.invokedOnBeforeDelete, is(false)); + } + + /** + * @see DATAMONGO-545 + */ + @Test + public void donInvokePersonCallbackForUntypedEvent() { + + MongoMappingEvent event = new BeforeDeleteEvent(new BasicDBObject(), null); + SamplePersonEventListener listener = new SamplePersonEventListener(); + listener.onApplicationEvent(event); + + assertThat(listener.invokedOnBeforeDelete, is(false)); + } + class SamplePersonEventListener extends AbstractMongoEventListener { boolean invokedOnBeforeConvert; boolean invokedOnAfterLoad; + boolean invokedOnBeforeDelete; + boolean invokedOnAfterDelete; @Override public void onBeforeConvert(Person source) { @@ -141,12 +196,24 @@ public class AbstractMongoEventListenerUnitTests { public void onAfterLoad(DBObject dbo) { invokedOnAfterLoad = true; } + + @Override + public void onAfterDelete(DBObject dbo) { + invokedOnAfterDelete = true; + } + + @Override + public void onBeforeDelete(DBObject dbo) { + invokedOnBeforeDelete = true; + } } class SampleContactEventListener extends AbstractMongoEventListener { boolean invokedOnBeforeConvert; boolean invokedOnAfterLoad; + boolean invokedOnBeforeDelete; + boolean invokedOnAfterDelete; @Override public void onBeforeConvert(Contact source) { @@ -157,6 +224,17 @@ public class AbstractMongoEventListenerUnitTests { public void onAfterLoad(DBObject dbo) { invokedOnAfterLoad = true; } + + @Override + public void onAfterDelete(DBObject dbo) { + invokedOnAfterDelete = true; + } + + @Override + public void onBeforeDelete(DBObject dbo) { + invokedOnBeforeDelete = true; + } + } class SampleAccountEventListener extends AbstractMongoEventListener {