diff --git a/pom.xml b/pom.xml index 2917aeb0..d85d8420 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-keyvalue - 0.1.0.BUILD-SNAPSHOT + 0.1.0.DATAKV-91-SNAPSHOT Spring Data KeyValue diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java index bfc019f7..1c0a0eef 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -101,4 +101,12 @@ public interface KeyValueAdapter extends DisposableBean { * @return */ long count(KeyValueQuery query, Serializable keyspace); + + /** + * Check if values from the given keyspace are contained in the underlying key-value store. + * + * @param keyspace + * @return true if {@literal keyspace} already present in adapter. + */ + boolean hasKeyspace(Serializable keyspace); } diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java index 5d897c47..6d677226 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -20,14 +20,22 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.dao.DataAccessException; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.domain.Sort; +import org.springframework.data.keyvalue.core.event.KeyValueEvent; import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.data.mapping.PersistentEntity; @@ -35,6 +43,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** @@ -42,8 +51,9 @@ * * @author Christoph Strobl * @author Oliver Gierke + * @author Thomas Darimont */ -public class KeyValueTemplate implements KeyValueOperations { +public class KeyValueTemplate implements KeyValueOperations, ApplicationContextAware { private static final PersistenceExceptionTranslator DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR = new KeyValuePersistenceExceptionTranslator(); @@ -51,6 +61,8 @@ public class KeyValueTemplate implements KeyValueOperations { private final ConcurrentHashMap, String> keySpaceCache = new ConcurrentHashMap, String>(); private final MappingContext>, ? extends PersistentProperty> mappingContext; private final IdentifierGenerator identifierGenerator; + private ApplicationEventPublisher eventPublisher; + private final Set eventTypesToPublish = new HashSet(4); private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR; /** @@ -109,22 +121,26 @@ public void insert(final Serializable id, final Object objectToInsert) { Assert.notNull(id, "Id for object to be inserted must not be null!"); Assert.notNull(objectToInsert, "Object to be inserted must not be null!"); + final String keyspace = resolveKeySpace(objectToInsert.getClass()); + + potentiallyPublishEvent(KeyValueEvent.beforeInsert(this, keyspace, id, objectToInsert)); + execute(new KeyValueCallback() { @Override public Void doInKeyValue(KeyValueAdapter adapter) { - String typeKey = resolveKeySpace(objectToInsert.getClass()); - - if (adapter.contains(id, typeKey)) { + if (adapter.contains(id, keyspace)) { throw new DuplicateKeyException(String.format( "Cannot insert existing object with id %s!. Please use update.", id)); } - adapter.put(id, objectToInsert, typeKey); + adapter.put(id, objectToInsert, keyspace); return null; } }); + + potentiallyPublishEvent(KeyValueEvent.afterInsert(this, keyspace, id, objectToInsert)); } /* @@ -156,14 +172,20 @@ public void update(final Serializable id, final Object objectToUpdate) { Assert.notNull(id, "Id for object to be inserted must not be null!"); Assert.notNull(objectToUpdate, "Object to be updated must not be null!"); + final String keyspace = resolveKeySpace(objectToUpdate.getClass()); + + potentiallyPublishEvent(KeyValueEvent.beforeUpdate(this, keyspace, id, objectToUpdate)); + execute(new KeyValueCallback() { @Override public Void doInKeyValue(KeyValueAdapter adapter) { - adapter.put(id, objectToUpdate, resolveKeySpace(objectToUpdate.getClass())); + adapter.put(id, objectToUpdate, keyspace); return null; } }); + + potentiallyPublishEvent(KeyValueEvent.afterUpdate(this, keyspace, id, objectToUpdate)); } /* @@ -209,13 +231,17 @@ public T findById(final Serializable id, final Class type) { Assert.notNull(id, "Id for object to be inserted must not be null!"); Assert.notNull(type, "Type to fetch must not be null!"); - return execute(new KeyValueCallback() { + final String keyspace = resolveKeySpace(type); + + potentiallyPublishEvent(KeyValueEvent.beforeGet(this, keyspace, id)); + + T result = execute(new KeyValueCallback() { @SuppressWarnings("unchecked") @Override public T doInKeyValue(KeyValueAdapter adapter) { - Object result = adapter.get(id, resolveKeySpace(type)); + Object result = adapter.get(id, keyspace); if (result == null || getKeySpace(type) == null || typeCheck(type, result)) { return (T) result; @@ -224,6 +250,10 @@ public T doInKeyValue(KeyValueAdapter adapter) { return null; } }); + + potentiallyPublishEvent(KeyValueEvent.afterGet(this, keyspace, id, result)); + + return result; } /* @@ -235,17 +265,21 @@ public void delete(final Class type) { Assert.notNull(type, "Type to delete must not be null!"); - final String typeKey = resolveKeySpace(type); + final String keyspace = resolveKeySpace(type); + + potentiallyPublishEvent(KeyValueEvent.beforeDelete(this, keyspace)); execute(new KeyValueCallback() { @Override public Void doInKeyValue(KeyValueAdapter adapter) { - adapter.deleteAllOf(typeKey); + adapter.deleteAllOf(keyspace); return null; } }); + + potentiallyPublishEvent(KeyValueEvent.afterDelete(this, keyspace)); } /* @@ -272,14 +306,22 @@ public T delete(final Serializable id, final Class type) { Assert.notNull(id, "Id for object to be inserted must not be null!"); Assert.notNull(type, "Type to delete must not be null!"); - return execute(new KeyValueCallback() { + final String keyspace = resolveKeySpace(type); + + potentiallyPublishEvent(KeyValueEvent.beforeDelete(this, keyspace, id)); + + T result = execute(new KeyValueCallback() { @SuppressWarnings("unchecked") @Override public T doInKeyValue(KeyValueAdapter adapter) { - return (T) adapter.delete(id, resolveKeySpace(type)); + return (T) adapter.delete(id, keyspace); } }); + + potentiallyPublishEvent(KeyValueEvent.afterDelete(this, keyspace, id, result)); + + return result; } /* @@ -416,14 +458,37 @@ public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTrans this.exceptionTranslator = exceptionTranslator; } + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + eventPublisher = applicationContext; + } + + /** + * Define the event types to publish via {@link ApplicationEventPublisher}. + * + * @param eventTypesToPublish use {@literal null} or {@link Collections#emptySet()} to disable publishing. + */ + public void setEventTypesToPublish(Set eventTypesToPublish) { + + this.eventTypesToPublish.clear(); + + if (!CollectionUtils.isEmpty(eventTypesToPublish)) { + this.eventTypesToPublish.addAll(eventTypesToPublish); + } + } + protected String resolveKeySpace(Class type) { Class userClass = ClassUtils.getUserClass(type); - String potentialAlias = keySpaceCache.get(userClass); + String potentialKeySpace = keySpaceCache.get(userClass); - if (potentialAlias != null) { - return potentialAlias; + if (potentialKeySpace != null) { + return potentialKeySpace; } String keySpaceString = null; @@ -450,4 +515,15 @@ private RuntimeException resolveExceptionIfPossible(RuntimeException e) { DataAccessException translatedException = exceptionTranslator.translateExceptionIfPossible(e); return translatedException != null ? translatedException : e; } + + private void potentiallyPublishEvent(KeyValueEvent event) { + + if (eventPublisher == null) { + return; + } + + if (eventTypesToPublish.contains(event.getType()) || eventTypesToPublish.contains(KeyValueEvent.Type.ANY)) { + eventPublisher.publishEvent(event); + } + } } diff --git a/src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java b/src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java new file mode 100644 index 00000000..0dbeb837 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/event/KeyValueEvent.java @@ -0,0 +1,177 @@ +/* + * Copyright 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.keyvalue.core.event; + +import java.io.Serializable; + +import org.springframework.context.ApplicationEvent; + +/** + * {@link KeyValueEvent} gets published for operations executed by eg. + * {@link org.springframework.data.keyvalue.core.KeyValueTemplate}. Use the {@link #getType()} to determine which event + * has been emitted. + * + * @author Christoph Strobl + * @author Thomas Darimont + * @param + */ +public class KeyValueEvent extends ApplicationEvent { + + private static final long serialVersionUID = -7128527253428193044L; + + public enum Type { + ANY, BEFORE_INSERT, AFTER_INSERT, BEFORE_UPDATE, AFTER_UPDATE, BEFORE_DELETE, AFTER_DELETE, BEFORE_GET, AFTER_GET + } + + private final Type type; + private final String keyspace; + private final Serializable id; + private final Object value; + + protected KeyValueEvent(Object source, Type type, String keyspace, Serializable id, Object value) { + + super(source); + this.type = type; + this.keyspace = keyspace; + this.id = id; + this.value = value; + } + + /** + * @return {@link Type} of event. Never {@literal null}. + */ + public Type getType() { + return type; + } + + /** + * @return affected keyspace. Never {@literal null}. + */ + public String getKeyspace() { + return keyspace; + } + + /** + * @return can be {@literal null}. + */ + public Serializable getId() { + return id; + } + + /** + * @return can be {@literal null}. + */ + public Object getValue() { + return value; + } + + @Override + public String toString() { + return "KeyValueEvent [type=" + type + ", keyspace=" + keyspace + ", id=" + id + "]"; + } + + public static GetEvent beforeGet(Object source, String keyspace, Serializable id) { + return new GetEvent(source, Type.BEFORE_GET, keyspace, id, null); + } + + public static GetEvent afterGet(Object source, String keyspace, Serializable id, Object value) { + return new GetEvent(source, Type.AFTER_GET, keyspace, id, value); + } + + public static InsertEvent beforeInsert(Object source, String keyspace, Serializable id, Object value) { + return new InsertEvent(source, Type.BEFORE_INSERT, keyspace, id, value); + } + + public static InsertEvent afterInsert(Object source, String keyspace, Serializable id, Object value) { + return new InsertEvent(source, Type.AFTER_INSERT, keyspace, id, value); + } + + public static UpdateEvent beforeUpdate(Object source, String keyspace, Serializable id, Object value) { + return new UpdateEvent(source, Type.BEFORE_UPDATE, keyspace, id, value); + } + + public static UpdateEvent afterUpdate(Object source, String keyspace, Serializable id, Object value) { + return new UpdateEvent(source, Type.AFTER_UPDATE, keyspace, id, value); + } + + public static DropKeyspaceEvent beforeDelete(Object source, String keyspace) { + return new DropKeyspaceEvent(source, Type.BEFORE_DELETE, keyspace); + } + + public static DeleteEvent beforeDelete(Object source, String keyspace, Serializable id) { + return beforeDelete(source, keyspace, id, null); + } + + public static DeleteEvent beforeDelete(Object source, String keyspace, Serializable id, Object value) { + return new DeleteEvent(source, Type.BEFORE_DELETE, keyspace, id, value); + } + + public static DropKeyspaceEvent afterDelete(Object source, String keyspace) { + return new DropKeyspaceEvent(source, Type.AFTER_DELETE, keyspace); + } + + public static DeleteEvent afterDelete(Object source, String keyspace, Serializable id, Object value) { + return new DeleteEvent(source, Type.AFTER_DELETE, keyspace, id, value); + } + + public static class InsertEvent extends KeyValueEvent { + + private static final long serialVersionUID = -1; + + InsertEvent(Object source, Type type, String keyspace, Serializable id, Object value) { + super(source, type, keyspace, id, value); + } + } + + public static class UpdateEvent extends KeyValueEvent { + + private static final long serialVersionUID = -1; + + UpdateEvent(Object source, Type type, String keyspace, Serializable id, Object value) { + super(source, type, keyspace, id, value); + } + } + + public static class DeleteEvent extends KeyValueEvent { + + private static final long serialVersionUID = -1; + + DeleteEvent(Object source, Type type, String keyspace, Serializable id, Object value) { + super(source, type, keyspace, id, value); + } + } + + public static class DropKeyspaceEvent extends DeleteEvent { + + private static final long serialVersionUID = -1; + + DropKeyspaceEvent(Object source, Type type, String keyspace) { + super(source, type, keyspace, null, null); + } + } + + public static class GetEvent extends KeyValueEvent { + + private static final long serialVersionUID = -1; + + protected GetEvent(Object source, Type type, String keyspace, + Serializable id, Object value) { + super(source, type, keyspace, id, value); + } + + } + +} diff --git a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java index dbe9719f..93c946e5 100644 --- a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java @@ -142,6 +142,17 @@ public void deleteAllOf(Serializable keyspace) { getKeySpaceMap(keyspace).clear(); } + /* + * (non-Javadoc) + * @see org.springframework.data.keyvalue.core.KeyValueAdapter#hasKeyspace(java.io.Serializable) + */ + @Override + public boolean hasKeyspace(Serializable keyspace) { + + Assert.notNull(keyspace, "Collection must not be null for lookup."); + return store.containsKey(keyspace); + } + /* * (non-Javadoc) * @see org.springframework.data.keyvalue.core.KeyValueAdapter#clear() diff --git a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java index d2071471..5402f860 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -28,6 +28,7 @@ import java.lang.annotation.Target; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import org.junit.Before; import org.junit.Rule; @@ -37,17 +38,26 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Persistent; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.keyvalue.annotation.KeySpace; +import org.springframework.data.keyvalue.core.event.KeyValueEvent; +import org.springframework.data.keyvalue.core.event.KeyValueEvent.DeleteEvent; +import org.springframework.data.keyvalue.core.event.KeyValueEvent.DropKeyspaceEvent; +import org.springframework.data.keyvalue.core.event.KeyValueEvent.GetEvent; +import org.springframework.data.keyvalue.core.event.KeyValueEvent.InsertEvent; +import org.springframework.data.keyvalue.core.event.KeyValueEvent.Type; +import org.springframework.data.keyvalue.core.event.KeyValueEvent.UpdateEvent; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl + * @author Thomas Darimont */ @RunWith(MockitoJUnitRunner.class) public class KeyValueTemplateUnitTests { @@ -63,10 +73,12 @@ public class KeyValueTemplateUnitTests { private @Mock KeyValueAdapter adapterMock; private KeyValueTemplate template; + private @Mock ApplicationContext ctxMock; @Before public void setUp() throws InstantiationException, IllegalAccessException { this.template = new KeyValueTemplate(adapterMock); + this.template.setApplicationContext(ctxMock); } /** @@ -420,6 +432,225 @@ public void setttingNullPersistenceExceptionTranslatorShouldThrowException() { template.setExceptionTranslator(null); } + /** + * @see DATAKV-91 + */ + @Test + public void shouldNotPublishEventWhenNoApplicationContextSet() { + + template.setApplicationContext(null); + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.AFTER_DELETE, + KeyValueEvent.Type.AFTER_INSERT, KeyValueEvent.Type.AFTER_UPDATE, KeyValueEvent.Type.BEFORE_DELETE, + KeyValueEvent.Type.BEFORE_INSERT, KeyValueEvent.Type.BEFORE_UPDATE))); + + template.insert("1", FOO_ONE); + + verifyZeroInteractions(ctxMock); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldNotPublishEventWhenNotExplicitlySetForPublication() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.AFTER_DELETE))); + + template.insert("1", FOO_ONE); + + verifyZeroInteractions(ctxMock); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishBeforeInsertEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.BEFORE_INSERT))); + + template.insert("1", FOO_ONE); + + ArgumentCaptor captor = ArgumentCaptor.forClass(InsertEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.BEFORE_INSERT)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), is((Object) FOO_ONE)); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishAfterInsertEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.AFTER_INSERT))); + + template.insert("1", FOO_ONE); + + ArgumentCaptor captor = ArgumentCaptor.forClass(InsertEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.AFTER_INSERT)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), is((Object) FOO_ONE)); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishBeforeUpdateEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.BEFORE_UPDATE))); + + template.update("1", FOO_ONE); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.BEFORE_UPDATE)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), is((Object) FOO_ONE)); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishAfterUpdateEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.AFTER_UPDATE))); + + template.update("1", FOO_ONE); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.AFTER_UPDATE)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), is((Object) FOO_ONE)); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishBeforeDeleteEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.BEFORE_DELETE))); + + template.delete("1", FOO_ONE.getClass()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.BEFORE_DELETE)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), nullValue()); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishAfterDeleteEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.AFTER_DELETE))); + when(adapterMock.delete(eq("1"), eq(FOO_ONE.getClass().getName()))).thenReturn(FOO_ONE); + + template.delete("1", FOO_ONE.getClass()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.AFTER_DELETE)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), is((Object) FOO_ONE)); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishBeforeGetEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.BEFORE_GET))); + when(adapterMock.get(eq("1"), eq(FOO_ONE.getClass().getName()))).thenReturn(FOO_ONE); + + template.findById("1", FOO_ONE.getClass()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(GetEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.BEFORE_GET)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), nullValue()); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishAfterGetEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.AFTER_GET))); + when(adapterMock.get(eq("1"), eq(FOO_ONE.getClass().getName()))).thenReturn(FOO_ONE); + + template.findById("1", FOO_ONE.getClass()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(GetEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.AFTER_GET)); + assertThat(captor.getValue().getId(), is((Serializable) "1")); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + assertThat(captor.getValue().getValue(), is((Object) FOO_ONE)); + } + + /** + * @see DATAKV-91 + */ + @Test + public void shouldPublishDropKeyspaceEventCorrectly() { + + template.setEventTypesToPublish(new HashSet(Arrays.asList(KeyValueEvent.Type.AFTER_DELETE))); + + template.delete(FOO_ONE.getClass()); + + ArgumentCaptor captor = ArgumentCaptor.forClass(DropKeyspaceEvent.class); + + verify(ctxMock, times(1)).publishEvent(captor.capture()); + verifyNoMoreInteractions(ctxMock); + + assertThat(captor.getValue().getType(), is(Type.AFTER_DELETE)); + assertThat(captor.getValue().getKeyspace(), is(Foo.class.getName())); + } + static class Foo { String foo;