Skip to content

Commit 07b3a15

Browse files
christophstroblThomas Darimont
authored and
Thomas Darimont
committed
DATAKV-91 - Add support for sending application events.
We now allow definition of event types to be published via the application context. Original pull request: #5.
1 parent a8a37d4 commit 07b3a15

File tree

5 files changed

+521
-18
lines changed

5 files changed

+521
-18
lines changed

src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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.
@@ -101,4 +101,12 @@ public interface KeyValueAdapter extends DisposableBean {
101101
* @return
102102
*/
103103
long count(KeyValueQuery<?> query, Serializable keyspace);
104+
105+
/**
106+
* Check if values from the given keyspace are contained in the underlying key-value store.
107+
*
108+
* @param keyspace
109+
* @return true if {@literal keyspace} already present in adapter.
110+
*/
111+
boolean hasKeyspace(Serializable keyspace);
104112
}

src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java

+92-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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.
@@ -20,37 +20,49 @@
2020
import java.io.Serializable;
2121
import java.util.ArrayList;
2222
import java.util.Collection;
23+
import java.util.Collections;
24+
import java.util.HashSet;
2325
import java.util.List;
26+
import java.util.Set;
2427
import 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;
2633
import org.springframework.dao.DataAccessException;
2734
import org.springframework.dao.DuplicateKeyException;
2835
import org.springframework.dao.InvalidDataAccessApiUsageException;
2936
import org.springframework.dao.support.PersistenceExceptionTranslator;
3037
import org.springframework.data.domain.Sort;
38+
import org.springframework.data.keyvalue.core.event.KeyValueEvent;
3139
import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext;
3240
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
3341
import org.springframework.data.mapping.PersistentEntity;
3442
import org.springframework.data.mapping.PersistentProperty;
3543
import org.springframework.data.mapping.context.MappingContext;
3644
import org.springframework.util.Assert;
3745
import org.springframework.util.ClassUtils;
46+
import org.springframework.util.CollectionUtils;
3847
import 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

Comments
 (0)