Skip to content

Commit d5ba6e7

Browse files
DATAKV-91 - Add support for sending application events.
We now allow definition of event types to be published via the application context.
1 parent 35c2754 commit d5ba6e7

File tree

3 files changed

+421
-15
lines changed

3 files changed

+421
-15
lines changed

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

Lines changed: 77 additions & 14 deletions
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,21 +20,30 @@
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
/**
@@ -43,14 +52,16 @@
4352
* @author Christoph Strobl
4453
* @author Oliver Gierke
4554
*/
46-
public class KeyValueTemplate implements KeyValueOperations {
55+
public class KeyValueTemplate implements KeyValueOperations, ApplicationContextAware {
4756

4857
private static final PersistenceExceptionTranslator DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR = new KeyValuePersistenceExceptionTranslator();
4958

5059
private final KeyValueAdapter adapter;
5160
private final ConcurrentHashMap<Class<?>, String> keySpaceCache = new ConcurrentHashMap<Class<?>, String>();
5261
private final MappingContext<? extends PersistentEntity<?, ? extends PersistentProperty<?>>, ? extends PersistentProperty<?>> mappingContext;
5362
private final IdentifierGenerator identifierGenerator;
63+
private ApplicationEventPublisher eventPublisher;
64+
private Set<KeyValueEvent.Type> eventTypesToPublish = new HashSet<KeyValueEvent.Type>(4);
5465
private PersistenceExceptionTranslator exceptionTranslator = DEFAULT_PERSISTENCE_EXCEPTION_TRANSLATOR;
5566

5667
/**
@@ -109,22 +120,26 @@ public void insert(final Serializable id, final Object objectToInsert) {
109120
Assert.notNull(id, "Id for object to be inserted must not be null!");
110121
Assert.notNull(objectToInsert, "Object to be inserted must not be null!");
111122

123+
final String keyspace = resolveKeySpace(objectToInsert.getClass());
124+
125+
potentiallyPublishEvent(KeyValueEvent.beforeInsert(this, keyspace, id, objectToInsert));
126+
112127
execute(new KeyValueCallback<Void>() {
113128

114129
@Override
115130
public Void doInKeyValue(KeyValueAdapter adapter) {
116131

117-
String typeKey = resolveKeySpace(objectToInsert.getClass());
118-
119-
if (adapter.contains(id, typeKey)) {
132+
if (adapter.contains(id, keyspace)) {
120133
throw new DuplicateKeyException(String.format(
121134
"Cannot insert existing object with id %s!. Please use update.", id));
122135
}
123136

124-
adapter.put(id, objectToInsert, typeKey);
137+
adapter.put(id, objectToInsert, keyspace);
125138
return null;
126139
}
127140
});
141+
142+
potentiallyPublishEvent(KeyValueEvent.afterInsert(this, keyspace, id, objectToInsert));
128143
}
129144

130145
/*
@@ -156,14 +171,20 @@ public void update(final Serializable id, final Object objectToUpdate) {
156171
Assert.notNull(id, "Id for object to be inserted must not be null!");
157172
Assert.notNull(objectToUpdate, "Object to be updated must not be null!");
158173

174+
final String keyspace = resolveKeySpace(objectToUpdate.getClass());
175+
176+
potentiallyPublishEvent(KeyValueEvent.beforeUpdate(this, keyspace, id, objectToUpdate));
177+
159178
execute(new KeyValueCallback<Void>() {
160179

161180
@Override
162181
public Void doInKeyValue(KeyValueAdapter adapter) {
163-
adapter.put(id, objectToUpdate, resolveKeySpace(objectToUpdate.getClass()));
182+
adapter.put(id, objectToUpdate, keyspace);
164183
return null;
165184
}
166185
});
186+
187+
potentiallyPublishEvent(KeyValueEvent.afterUpdate(this, keyspace, id, objectToUpdate));
167188
}
168189

169190
/*
@@ -235,17 +256,21 @@ public void delete(final Class<?> type) {
235256

236257
Assert.notNull(type, "Type to delete must not be null!");
237258

238-
final String typeKey = resolveKeySpace(type);
259+
final String keyspace = resolveKeySpace(type);
260+
261+
potentiallyPublishEvent(KeyValueEvent.beforeDelete(this, keyspace));
239262

240263
execute(new KeyValueCallback<Void>() {
241264

242265
@Override
243266
public Void doInKeyValue(KeyValueAdapter adapter) {
244267

245-
adapter.deleteAllOf(typeKey);
268+
adapter.deleteAllOf(keyspace);
246269
return null;
247270
}
248271
});
272+
273+
potentiallyPublishEvent(KeyValueEvent.afterDelete(this, keyspace));
249274
}
250275

251276
/*
@@ -272,14 +297,22 @@ public <T> T delete(final Serializable id, final Class<T> type) {
272297
Assert.notNull(id, "Id for object to be inserted must not be null!");
273298
Assert.notNull(type, "Type to delete must not be null!");
274299

275-
return execute(new KeyValueCallback<T>() {
300+
final String keyspace = resolveKeySpace(type);
301+
302+
potentiallyPublishEvent(KeyValueEvent.beforeDelete(this, keyspace, id));
303+
304+
T result = execute(new KeyValueCallback<T>() {
276305

277306
@SuppressWarnings("unchecked")
278307
@Override
279308
public T doInKeyValue(KeyValueAdapter adapter) {
280-
return (T) adapter.delete(id, resolveKeySpace(type));
309+
return (T) adapter.delete(id, keyspace);
281310
}
282311
});
312+
313+
potentiallyPublishEvent(KeyValueEvent.afterDelete(this, keyspace, id, result));
314+
315+
return result;
283316
}
284317

285318
/*
@@ -416,14 +449,37 @@ public void setExceptionTranslator(PersistenceExceptionTranslator exceptionTrans
416449
this.exceptionTranslator = exceptionTranslator;
417450
}
418451

452+
/*
453+
* (non-Javadoc)
454+
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
455+
*/
456+
@Override
457+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
458+
eventPublisher = applicationContext;
459+
}
460+
461+
/**
462+
* Define the event types to publish via {@link ApplicationEventPublisher}.
463+
*
464+
* @param eventTypesToPublish use {@literal null} or {@link Collections#emptySet()} to disable publishing.
465+
*/
466+
public void setEventTypesToPublish(Set<KeyValueEvent.Type> eventTypesToPublish) {
467+
468+
this.eventTypesToPublish.clear();
469+
470+
if (!CollectionUtils.isEmpty(eventTypesToPublish)) {
471+
this.eventTypesToPublish.addAll(eventTypesToPublish);
472+
}
473+
}
474+
419475
protected String resolveKeySpace(Class<?> type) {
420476

421477
Class<?> userClass = ClassUtils.getUserClass(type);
422478

423-
String potentialAlias = keySpaceCache.get(userClass);
479+
String potentialKeySpace = keySpaceCache.get(userClass);
424480

425-
if (potentialAlias != null) {
426-
return potentialAlias;
481+
if (potentialKeySpace != null) {
482+
return potentialKeySpace;
427483
}
428484

429485
String keySpaceString = null;
@@ -450,4 +506,11 @@ private RuntimeException resolveExceptionIfPossible(RuntimeException e) {
450506
DataAccessException translatedException = exceptionTranslator.translateExceptionIfPossible(e);
451507
return translatedException != null ? translatedException : e;
452508
}
509+
510+
private void potentiallyPublishEvent(KeyValueEvent<?> event) {
511+
512+
if (eventPublisher != null && eventTypesToPublish.contains(event.getType())) {
513+
eventPublisher.publishEvent(event);
514+
}
515+
}
453516
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright 2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.keyvalue.core.event;
17+
18+
import java.io.Serializable;
19+
20+
import org.springframework.context.ApplicationEvent;
21+
22+
/**
23+
* {@link KeyValueEvent} gets published for operations executed by eg.
24+
* {@link org.springframework.data.keyvalue.core.KeyValueTemplate}. Use the {@link #getType()} to determine which event
25+
* has been emitted.
26+
*
27+
* @author Christoph Strobl
28+
* @param <T>
29+
*/
30+
public class KeyValueEvent<T> extends ApplicationEvent {
31+
32+
private static final long serialVersionUID = -7128527253428193044L;
33+
34+
public enum Type {
35+
BEFORE_INSERT, AFTER_INSERT, BEFORE_UPDATE, AFTER_UPDATE, BEFORE_DELETE, AFTER_DELETE
36+
}
37+
38+
private final Type type;
39+
private final String keyspace;
40+
private final Serializable id;
41+
private final Object value;
42+
43+
protected KeyValueEvent(T source, Type type, String keyspace, Serializable id, Object value) {
44+
super(source);
45+
this.type = type;
46+
this.keyspace = keyspace;
47+
this.id = id;
48+
this.value = value;
49+
}
50+
51+
/**
52+
* @return {@link Type} of event. Never {@literal null}.
53+
*/
54+
public Type getType() {
55+
return type;
56+
}
57+
58+
/**
59+
* @return affected keyspace. Never {@literal null}.
60+
*/
61+
public String getKeyspace() {
62+
return keyspace;
63+
}
64+
65+
/**
66+
* @return can be {@literal null}.
67+
*/
68+
public Serializable getId() {
69+
return id;
70+
}
71+
72+
/**
73+
* @return can be {@literal null}.
74+
*/
75+
public Object getValue() {
76+
return value;
77+
}
78+
79+
public static <T> InsertEvent<T> beforeInsert(T source, String keyspace, Serializable id, Object value) {
80+
return new InsertEvent<T>(source, Type.BEFORE_INSERT, keyspace, id, value);
81+
}
82+
83+
public static <T> InsertEvent<T> afterInsert(T source, String keyspace, Serializable id, Object value) {
84+
return new InsertEvent<T>(source, Type.AFTER_INSERT, keyspace, id, value);
85+
}
86+
87+
public static <T> UpdateEvent<T> beforeUpdate(T source, String keyspace, Serializable id, Object value) {
88+
return new UpdateEvent<T>(source, Type.BEFORE_UPDATE, keyspace, id, value);
89+
}
90+
91+
public static <T> UpdateEvent<T> afterUpdate(T source, String keyspace, Serializable id, Object value) {
92+
return new UpdateEvent<T>(source, Type.AFTER_UPDATE, keyspace, id, value);
93+
}
94+
95+
public static <T> DropKeyspaceEvent<T> beforeDelete(T source, String keyspace) {
96+
return new DropKeyspaceEvent<T>(source, Type.BEFORE_DELETE, keyspace);
97+
}
98+
99+
public static <T> DeleteEvent<T> beforeDelete(T source, String keyspace, Serializable id) {
100+
return beforeDelete(source, keyspace, id, null);
101+
}
102+
103+
public static <T> DeleteEvent<T> beforeDelete(T source, String keyspace, Serializable id, Object value) {
104+
return new DeleteEvent<T>(source, Type.BEFORE_DELETE, keyspace, id, value);
105+
}
106+
107+
public static <T> DropKeyspaceEvent<T> afterDelete(T source, String keyspace) {
108+
return new DropKeyspaceEvent<T>(source, Type.AFTER_DELETE, keyspace);
109+
}
110+
111+
public static <T> DeleteEvent<T> afterDelete(T source, String keyspace, Serializable id, Object value) {
112+
return new DeleteEvent<T>(source, Type.AFTER_DELETE, keyspace, id, value);
113+
}
114+
115+
public static class InsertEvent<T> extends KeyValueEvent<T> {
116+
117+
private static final long serialVersionUID = -1;
118+
119+
InsertEvent(T source, Type type, String keyspace, Serializable id, Object value) {
120+
super(source, type, keyspace, id, value);
121+
}
122+
}
123+
124+
public static class UpdateEvent<T> extends KeyValueEvent<T> {
125+
126+
private static final long serialVersionUID = -1;
127+
128+
UpdateEvent(T source, Type type, String keyspace, Serializable id, Object value) {
129+
super(source, type, keyspace, id, value);
130+
}
131+
}
132+
133+
public static class DeleteEvent<T> extends KeyValueEvent<T> {
134+
135+
private static final long serialVersionUID = -1;
136+
137+
DeleteEvent(T source, Type type, String keyspace, Serializable id, Object value) {
138+
super(source, type, keyspace, id, value);
139+
}
140+
}
141+
142+
public static class DropKeyspaceEvent<T> extends DeleteEvent<T> {
143+
144+
private static final long serialVersionUID = -1;
145+
146+
DropKeyspaceEvent(T source, Type type, String keyspace) {
147+
super(source, type, keyspace, null, null);
148+
}
149+
}
150+
151+
}

0 commit comments

Comments
 (0)