11/*
2- * Copyright 2014 the original author or authors.
2+ * Copyright 2014-2015 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2020import java .io .Serializable ;
2121import java .util .ArrayList ;
2222import java .util .Collection ;
23+ import java .util .Collections ;
24+ import java .util .HashSet ;
2325import java .util .List ;
26+ import java .util .Set ;
2427import java .util .concurrent .ConcurrentHashMap ;
2528
29+ import org .springframework .beans .BeansException ;
30+ import org .springframework .context .ApplicationContext ;
31+ import org .springframework .context .ApplicationContextAware ;
32+ import org .springframework .context .ApplicationEventPublisher ;
2633import org .springframework .dao .DataAccessException ;
2734import org .springframework .dao .DuplicateKeyException ;
2835import org .springframework .dao .InvalidDataAccessApiUsageException ;
2936import org .springframework .dao .support .PersistenceExceptionTranslator ;
3037import org .springframework .data .domain .Sort ;
38+ import org .springframework .data .keyvalue .core .event .KeyValueEvent ;
3139import org .springframework .data .keyvalue .core .mapping .context .KeyValueMappingContext ;
3240import org .springframework .data .keyvalue .core .query .KeyValueQuery ;
3341import org .springframework .data .mapping .PersistentEntity ;
3442import org .springframework .data .mapping .PersistentProperty ;
3543import org .springframework .data .mapping .context .MappingContext ;
3644import org .springframework .util .Assert ;
3745import org .springframework .util .ClassUtils ;
46+ import org .springframework .util .CollectionUtils ;
3847import org .springframework .util .StringUtils ;
3948
4049/**
4150 * Basic implementation of {@link KeyValueOperations}.
4251 *
4352 * @author Christoph Strobl
4453 * @author Oliver Gierke
54+ * @author Thomas Darimont
4555 */
46- public class KeyValueTemplate implements KeyValueOperations {
56+ public class KeyValueTemplate implements KeyValueOperations , ApplicationContextAware {
4757
4858 private static final PersistenceExceptionTranslator DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR = new KeyValuePersistenceExceptionTranslator ();
4959
5060 private final KeyValueAdapter adapter ;
5161 private final ConcurrentHashMap <Class <?>, String > keySpaceCache = new ConcurrentHashMap <Class <?>, String >();
5262 private final MappingContext <? extends PersistentEntity <?, ? extends PersistentProperty <?>>, ? extends PersistentProperty <?>> mappingContext ;
5363 private final IdentifierGenerator identifierGenerator ;
64+ private ApplicationEventPublisher eventPublisher ;
65+ private final Set <KeyValueEvent .Type > eventTypesToPublish = new HashSet <KeyValueEvent .Type >(4 );
5466 private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR ;
5567
5668 /**
@@ -109,22 +121,26 @@ public void insert(final Serializable id, final Object objectToInsert) {
109121 Assert .notNull (id , "Id for object to be inserted must not be null!" );
110122 Assert .notNull (objectToInsert , "Object to be inserted must not be null!" );
111123
124+ final String keyspace = resolveKeySpace (objectToInsert .getClass ());
125+
126+ potentiallyPublishEvent (KeyValueEvent .beforeInsert (this , keyspace , id , objectToInsert ));
127+
112128 execute (new KeyValueCallback <Void >() {
113129
114130 @ Override
115131 public Void doInKeyValue (KeyValueAdapter adapter ) {
116132
117- String typeKey = resolveKeySpace (objectToInsert .getClass ());
118-
119- if (adapter .contains (id , typeKey )) {
133+ if (adapter .contains (id , keyspace )) {
120134 throw new DuplicateKeyException (String .format (
121135 "Cannot insert existing object with id %s!. Please use update." , id ));
122136 }
123137
124- adapter .put (id , objectToInsert , typeKey );
138+ adapter .put (id , objectToInsert , keyspace );
125139 return null ;
126140 }
127141 });
142+
143+ potentiallyPublishEvent (KeyValueEvent .afterInsert (this , keyspace , id , objectToInsert ));
128144 }
129145
130146 /*
@@ -156,14 +172,20 @@ public void update(final Serializable id, final Object objectToUpdate) {
156172 Assert .notNull (id , "Id for object to be inserted must not be null!" );
157173 Assert .notNull (objectToUpdate , "Object to be updated must not be null!" );
158174
175+ final String keyspace = resolveKeySpace (objectToUpdate .getClass ());
176+
177+ potentiallyPublishEvent (KeyValueEvent .beforeUpdate (this , keyspace , id , objectToUpdate ));
178+
159179 execute (new KeyValueCallback <Void >() {
160180
161181 @ Override
162182 public Void doInKeyValue (KeyValueAdapter adapter ) {
163- adapter .put (id , objectToUpdate , resolveKeySpace ( objectToUpdate . getClass ()) );
183+ adapter .put (id , objectToUpdate , keyspace );
164184 return null ;
165185 }
166186 });
187+
188+ potentiallyPublishEvent (KeyValueEvent .afterUpdate (this , keyspace , id , objectToUpdate ));
167189 }
168190
169191 /*
@@ -209,13 +231,17 @@ public <T> T findById(final Serializable id, final Class<T> type) {
209231 Assert .notNull (id , "Id for object to be inserted must not be null!" );
210232 Assert .notNull (type , "Type to fetch must not be null!" );
211233
212- return execute (new KeyValueCallback <T >() {
234+ final String keyspace = resolveKeySpace (type );
235+
236+ potentiallyPublishEvent (KeyValueEvent .beforeGet (this , keyspace , id ));
237+
238+ T result = execute (new KeyValueCallback <T >() {
213239
214240 @ SuppressWarnings ("unchecked" )
215241 @ Override
216242 public T doInKeyValue (KeyValueAdapter adapter ) {
217243
218- Object result = adapter .get (id , resolveKeySpace ( type ) );
244+ Object result = adapter .get (id , keyspace );
219245
220246 if (result == null || getKeySpace (type ) == null || typeCheck (type , result )) {
221247 return (T ) result ;
@@ -224,6 +250,10 @@ public T doInKeyValue(KeyValueAdapter adapter) {
224250 return null ;
225251 }
226252 });
253+
254+ potentiallyPublishEvent (KeyValueEvent .afterGet (this , keyspace , id , result ));
255+
256+ return result ;
227257 }
228258
229259 /*
@@ -235,17 +265,21 @@ public void delete(final Class<?> type) {
235265
236266 Assert .notNull (type , "Type to delete must not be null!" );
237267
238- final String typeKey = resolveKeySpace (type );
268+ final String keyspace = resolveKeySpace (type );
269+
270+ potentiallyPublishEvent (KeyValueEvent .beforeDelete (this , keyspace ));
239271
240272 execute (new KeyValueCallback <Void >() {
241273
242274 @ Override
243275 public Void doInKeyValue (KeyValueAdapter adapter ) {
244276
245- adapter .deleteAllOf (typeKey );
277+ adapter .deleteAllOf (keyspace );
246278 return null ;
247279 }
248280 });
281+
282+ potentiallyPublishEvent (KeyValueEvent .afterDelete (this , keyspace ));
249283 }
250284
251285 /*
@@ -272,14 +306,22 @@ public <T> T delete(final Serializable id, final Class<T> type) {
272306 Assert .notNull (id , "Id for object to be inserted must not be null!" );
273307 Assert .notNull (type , "Type to delete must not be null!" );
274308
275- return execute (new KeyValueCallback <T >() {
309+ final String keyspace = resolveKeySpace (type );
310+
311+ potentiallyPublishEvent (KeyValueEvent .beforeDelete (this , keyspace , id ));
312+
313+ T result = execute (new KeyValueCallback <T >() {
276314
277315 @ SuppressWarnings ("unchecked" )
278316 @ Override
279317 public T doInKeyValue (KeyValueAdapter adapter ) {
280- return (T ) adapter .delete (id , resolveKeySpace ( type ) );
318+ return (T ) adapter .delete (id , keyspace );
281319 }
282320 });
321+
322+ potentiallyPublishEvent (KeyValueEvent .afterDelete (this , keyspace , id , result ));
323+
324+ return result ;
283325 }
284326
285327 /*
@@ -416,14 +458,37 @@ public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTrans
416458 this .exceptionTranslator = exceptionTranslator ;
417459 }
418460
461+ /*
462+ * (non-Javadoc)
463+ * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
464+ */
465+ @ Override
466+ public void setApplicationContext (ApplicationContext applicationContext ) throws BeansException {
467+ eventPublisher = applicationContext ;
468+ }
469+
470+ /**
471+ * Define the event types to publish via {@link ApplicationEventPublisher}.
472+ *
473+ * @param eventTypesToPublish use {@literal null} or {@link Collections#emptySet()} to disable publishing.
474+ */
475+ public void setEventTypesToPublish (Set <KeyValueEvent .Type > eventTypesToPublish ) {
476+
477+ this .eventTypesToPublish .clear ();
478+
479+ if (!CollectionUtils .isEmpty (eventTypesToPublish )) {
480+ this .eventTypesToPublish .addAll (eventTypesToPublish );
481+ }
482+ }
483+
419484 protected String resolveKeySpace (Class <?> type ) {
420485
421486 Class <?> userClass = ClassUtils .getUserClass (type );
422487
423- String potentialAlias = keySpaceCache .get (userClass );
488+ String potentialKeySpace = keySpaceCache .get (userClass );
424489
425- if (potentialAlias != null ) {
426- return potentialAlias ;
490+ if (potentialKeySpace != null ) {
491+ return potentialKeySpace ;
427492 }
428493
429494 String keySpaceString = null ;
@@ -450,4 +515,15 @@ private RuntimeException resolveExceptionIfPossible(RuntimeException e) {
450515 DataAccessException translatedException = exceptionTranslator .translateExceptionIfPossible (e );
451516 return translatedException != null ? translatedException : e ;
452517 }
518+
519+ private void potentiallyPublishEvent (KeyValueEvent event ) {
520+
521+ if (eventPublisher == null ) {
522+ return ;
523+ }
524+
525+ if (eventTypesToPublish .contains (event .getType ()) || eventTypesToPublish .contains (KeyValueEvent .Type .ANY )) {
526+ eventPublisher .publishEvent (event );
527+ }
528+ }
453529}
0 commit comments