DATAMONGO-545 - Add BeforeDeleteEvent and AfterDeleteEvent.

Added events for before and after deletion of documents. Polished logging code in AbstractMongoEventListener.
This commit is contained in:
Martin Baumgartner
2013-03-24 16:56:14 +01:00
committed by Oliver Gierke
parent 0eb315a758
commit 8b50af07ce
7 changed files with 285 additions and 23 deletions

View File

@@ -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<Void>() {
public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException {
maybeEmitEvent(new BeforeDeleteEvent<T>(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<T>(queryObject, entityClass));
return null;
}
});

View File

@@ -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<T> extends MongoMappingEvent<DBObject> {
private static final long serialVersionUID = 1L;
private final Class<T> 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<T> type) {
super(dbo, dbo);
this.type = type;
}
/**
* Returns the type for which the {@link AbstractDeleteEvent} shall be invoked for.
*
* @return
*/
public Class<T> getType() {
return type;
}
}

View File

@@ -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<E> implements ApplicationListener<MongoMappingEvent<?>> {
@@ -45,6 +46,7 @@ public abstract class AbstractMongoEventListener<E> 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<E> 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<E> 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);
}
}
}

View File

@@ -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 <em>after</am> it has been mapped onto the domain type handled.
*
* @author Martin Baumgartner
*/
public class AfterDeleteEvent<T> extends AbstractDeleteEvent<T> {
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<T> type) {
super(dbo, type);
}
}

View File

@@ -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 <em>before</em> being mapped based on the domain class handled.
*
* @author Martin Baumgartner
*/
public class BeforeDeleteEvent<T> extends AbstractDeleteEvent<T> {
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<T> type) {
super(dbo, type);
}
}

View File

@@ -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 <jbrisbin@vmware.com>
* {@link ApplicationListener} for Mongo mapping events logging the events.
*
* @author Jon Brisbin
* @author Martin Baumgartner
*/
public class LoggingEventListener extends AbstractMongoEventListener<Object> {
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<Object> {
*/
@Override
public void onBeforeConvert(Object source) {
log.info("onBeforeConvert: " + source);
LOGGER.info("onBeforeConvert: {}", source);
}
/*
@@ -42,10 +46,7 @@ public class LoggingEventListener extends AbstractMongoEventListener<Object> {
*/
@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<Object> {
*/
@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<Object> {
*/
@Override
public void onAfterLoad(DBObject dbo) {
log.info("onAfterLoad: " + dbo);
LOGGER.info("onAfterLoad: {}", dbo);
}
/*
@@ -72,6 +73,24 @@ public class LoggingEventListener extends AbstractMongoEventListener<Object> {
*/
@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);
}
}

View File

@@ -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<DBObject> event = new BeforeDeleteEvent<Person>(new BasicDBObject(), Person.class);
SampleContactEventListener listener = new SampleContactEventListener();
listener.onApplicationEvent(event);
assertThat(listener.invokedOnBeforeDelete, is(true));
}
/**
* @see DATAMONGO-545
*/
@Test
public void invokePersonCallbackForPersonEvent() {
MongoMappingEvent<DBObject> event = new BeforeDeleteEvent<Person>(new BasicDBObject(), Person.class);
SamplePersonEventListener listener = new SamplePersonEventListener();
listener.onApplicationEvent(event);
assertThat(listener.invokedOnBeforeDelete, is(true));
}
/**
* @see DATAMONGO-545
*/
@Test
public void dontInvokePersonCallbackForAccountEvent() {
MongoMappingEvent<DBObject> event = new BeforeDeleteEvent<Account>(new BasicDBObject(), Account.class);
SamplePersonEventListener listener = new SamplePersonEventListener();
listener.onApplicationEvent(event);
assertThat(listener.invokedOnBeforeDelete, is(false));
}
/**
* @see DATAMONGO-545
*/
@Test
public void donInvokePersonCallbackForUntypedEvent() {
MongoMappingEvent<DBObject> event = new BeforeDeleteEvent<Account>(new BasicDBObject(), null);
SamplePersonEventListener listener = new SamplePersonEventListener();
listener.onApplicationEvent(event);
assertThat(listener.invokedOnBeforeDelete, is(false));
}
class SamplePersonEventListener extends AbstractMongoEventListener<Person> {
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<Contact> {
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<Account> {