1
1
/*
2
- * Copyright 2014 the original author or authors.
2
+ * Copyright 2014-2015 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
20
20
import java .io .Serializable ;
21
21
import java .util .ArrayList ;
22
22
import java .util .Collection ;
23
+ import java .util .Collections ;
24
+ import java .util .HashSet ;
23
25
import java .util .List ;
26
+ import java .util .Set ;
24
27
import java .util .concurrent .ConcurrentHashMap ;
25
28
29
+ import org .springframework .beans .BeansException ;
30
+ import org .springframework .context .ApplicationContext ;
31
+ import org .springframework .context .ApplicationContextAware ;
32
+ import org .springframework .context .ApplicationEventPublisher ;
26
33
import org .springframework .dao .DataAccessException ;
27
34
import org .springframework .dao .DuplicateKeyException ;
28
35
import org .springframework .dao .InvalidDataAccessApiUsageException ;
29
36
import org .springframework .dao .support .PersistenceExceptionTranslator ;
30
37
import org .springframework .data .domain .Sort ;
38
+ import org .springframework .data .keyvalue .core .event .KeyValueEvent ;
31
39
import org .springframework .data .keyvalue .core .mapping .context .KeyValueMappingContext ;
32
40
import org .springframework .data .keyvalue .core .query .KeyValueQuery ;
33
41
import org .springframework .data .mapping .PersistentEntity ;
34
42
import org .springframework .data .mapping .PersistentProperty ;
35
43
import org .springframework .data .mapping .context .MappingContext ;
36
44
import org .springframework .util .Assert ;
37
45
import org .springframework .util .ClassUtils ;
46
+ import org .springframework .util .CollectionUtils ;
38
47
import org .springframework .util .StringUtils ;
39
48
40
49
/**
41
50
* Basic implementation of {@link KeyValueOperations}.
42
51
*
43
52
* @author Christoph Strobl
44
53
* @author Oliver Gierke
54
+ * @author Thomas Darimont
45
55
*/
46
- public class KeyValueTemplate implements KeyValueOperations {
56
+ public class KeyValueTemplate implements KeyValueOperations , ApplicationContextAware {
47
57
48
58
private static final PersistenceExceptionTranslator DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR = new KeyValuePersistenceExceptionTranslator ();
49
59
50
60
private final KeyValueAdapter adapter ;
51
61
private final ConcurrentHashMap <Class <?>, String > keySpaceCache = new ConcurrentHashMap <Class <?>, String >();
52
62
private final MappingContext <? extends PersistentEntity <?, ? extends PersistentProperty <?>>, ? extends PersistentProperty <?>> mappingContext ;
53
63
private final IdentifierGenerator identifierGenerator ;
64
+ private ApplicationEventPublisher eventPublisher ;
65
+ private final Set <KeyValueEvent .Type > eventTypesToPublish = new HashSet <KeyValueEvent .Type >(4 );
54
66
private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR ;
55
67
56
68
/**
@@ -109,22 +121,26 @@ public void insert(final Serializable id, final Object objectToInsert) {
109
121
Assert .notNull (id , "Id for object to be inserted must not be null!" );
110
122
Assert .notNull (objectToInsert , "Object to be inserted must not be null!" );
111
123
124
+ final String keyspace = resolveKeySpace (objectToInsert .getClass ());
125
+
126
+ potentiallyPublishEvent (KeyValueEvent .beforeInsert (this , keyspace , id , objectToInsert ));
127
+
112
128
execute (new KeyValueCallback <Void >() {
113
129
114
130
@ Override
115
131
public Void doInKeyValue (KeyValueAdapter adapter ) {
116
132
117
- String typeKey = resolveKeySpace (objectToInsert .getClass ());
118
-
119
- if (adapter .contains (id , typeKey )) {
133
+ if (adapter .contains (id , keyspace )) {
120
134
throw new DuplicateKeyException (String .format (
121
135
"Cannot insert existing object with id %s!. Please use update." , id ));
122
136
}
123
137
124
- adapter .put (id , objectToInsert , typeKey );
138
+ adapter .put (id , objectToInsert , keyspace );
125
139
return null ;
126
140
}
127
141
});
142
+
143
+ potentiallyPublishEvent (KeyValueEvent .afterInsert (this , keyspace , id , objectToInsert ));
128
144
}
129
145
130
146
/*
@@ -156,14 +172,20 @@ public void update(final Serializable id, final Object objectToUpdate) {
156
172
Assert .notNull (id , "Id for object to be inserted must not be null!" );
157
173
Assert .notNull (objectToUpdate , "Object to be updated must not be null!" );
158
174
175
+ final String keyspace = resolveKeySpace (objectToUpdate .getClass ());
176
+
177
+ potentiallyPublishEvent (KeyValueEvent .beforeUpdate (this , keyspace , id , objectToUpdate ));
178
+
159
179
execute (new KeyValueCallback <Void >() {
160
180
161
181
@ Override
162
182
public Void doInKeyValue (KeyValueAdapter adapter ) {
163
- adapter .put (id , objectToUpdate , resolveKeySpace ( objectToUpdate . getClass ()) );
183
+ adapter .put (id , objectToUpdate , keyspace );
164
184
return null ;
165
185
}
166
186
});
187
+
188
+ potentiallyPublishEvent (KeyValueEvent .afterUpdate (this , keyspace , id , objectToUpdate ));
167
189
}
168
190
169
191
/*
@@ -209,13 +231,17 @@ public <T> T findById(final Serializable id, final Class<T> type) {
209
231
Assert .notNull (id , "Id for object to be inserted must not be null!" );
210
232
Assert .notNull (type , "Type to fetch must not be null!" );
211
233
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 >() {
213
239
214
240
@ SuppressWarnings ("unchecked" )
215
241
@ Override
216
242
public T doInKeyValue (KeyValueAdapter adapter ) {
217
243
218
- Object result = adapter .get (id , resolveKeySpace ( type ) );
244
+ Object result = adapter .get (id , keyspace );
219
245
220
246
if (result == null || getKeySpace (type ) == null || typeCheck (type , result )) {
221
247
return (T ) result ;
@@ -224,6 +250,10 @@ public T doInKeyValue(KeyValueAdapter adapter) {
224
250
return null ;
225
251
}
226
252
});
253
+
254
+ potentiallyPublishEvent (KeyValueEvent .afterGet (this , keyspace , id , result ));
255
+
256
+ return result ;
227
257
}
228
258
229
259
/*
@@ -235,17 +265,21 @@ public void delete(final Class<?> type) {
235
265
236
266
Assert .notNull (type , "Type to delete must not be null!" );
237
267
238
- final String typeKey = resolveKeySpace (type );
268
+ final String keyspace = resolveKeySpace (type );
269
+
270
+ potentiallyPublishEvent (KeyValueEvent .beforeDelete (this , keyspace ));
239
271
240
272
execute (new KeyValueCallback <Void >() {
241
273
242
274
@ Override
243
275
public Void doInKeyValue (KeyValueAdapter adapter ) {
244
276
245
- adapter .deleteAllOf (typeKey );
277
+ adapter .deleteAllOf (keyspace );
246
278
return null ;
247
279
}
248
280
});
281
+
282
+ potentiallyPublishEvent (KeyValueEvent .afterDelete (this , keyspace ));
249
283
}
250
284
251
285
/*
@@ -272,14 +306,22 @@ public <T> T delete(final Serializable id, final Class<T> type) {
272
306
Assert .notNull (id , "Id for object to be inserted must not be null!" );
273
307
Assert .notNull (type , "Type to delete must not be null!" );
274
308
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 >() {
276
314
277
315
@ SuppressWarnings ("unchecked" )
278
316
@ Override
279
317
public T doInKeyValue (KeyValueAdapter adapter ) {
280
- return (T ) adapter .delete (id , resolveKeySpace ( type ) );
318
+ return (T ) adapter .delete (id , keyspace );
281
319
}
282
320
});
321
+
322
+ potentiallyPublishEvent (KeyValueEvent .afterDelete (this , keyspace , id , result ));
323
+
324
+ return result ;
283
325
}
284
326
285
327
/*
@@ -416,14 +458,37 @@ public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTrans
416
458
this .exceptionTranslator = exceptionTranslator ;
417
459
}
418
460
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
+
419
484
protected String resolveKeySpace (Class <?> type ) {
420
485
421
486
Class <?> userClass = ClassUtils .getUserClass (type );
422
487
423
- String potentialAlias = keySpaceCache .get (userClass );
488
+ String potentialKeySpace = keySpaceCache .get (userClass );
424
489
425
- if (potentialAlias != null ) {
426
- return potentialAlias ;
490
+ if (potentialKeySpace != null ) {
491
+ return potentialKeySpace ;
427
492
}
428
493
429
494
String keySpaceString = null ;
@@ -450,4 +515,15 @@ private RuntimeException resolveExceptionIfPossible(RuntimeException e) {
450
515
DataAccessException translatedException = exceptionTranslator .translateExceptionIfPossible (e );
451
516
return translatedException != null ? translatedException : e ;
452
517
}
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
+ }
453
529
}
0 commit comments