Skip to contentMethod: addEntityListener(Object)
1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.jopa.model.metamodel;
14:
15: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
16: import cz.cvut.kbss.jopa.model.annotations.EntityListeners;
17: import cz.cvut.kbss.jopa.model.lifecycle.LifecycleEvent;
18:
19: import java.lang.reflect.InvocationTargetException;
20: import java.lang.reflect.Method;
21: import java.util.*;
22:
23: /**
24: * Manages entity lifecycle callbacks declared either in the entity (entity lifecycle callbacks) or in its entity
25: * listener (entity listener callbacks) and provides means for their invocation.
26: */
27: public class EntityLifecycleListenerManager {
28:
29: private static final EntityLifecycleListenerManager EMPTY = new EntityLifecycleListenerManager();
30:
31: private EntityLifecycleListenerManager parent;
32:
33: private final Map<LifecycleEvent, Method> lifecycleCallbacks = new EnumMap<>(LifecycleEvent.class);
34:
35: private List<Object> entityListeners;
36:
37: private Map<Object, Map<LifecycleEvent, Method>> entityListenerCallbacks;
38:
39: private Map<Object, Object> instancesBeingProcessed = new IdentityHashMap<>();
40:
41: /**
42: * Gets default instance of this manager, which contains no listeners and does nothing on invocation.
43: *
44: * @return Default {@link EntityLifecycleListenerManager} instance
45: */
46: public static EntityLifecycleListenerManager empty() {
47: return EMPTY;
48: }
49:
50: /**
51: * Calls pre-persist callbacks for the specified instance.
52: * <p>
53: * These include:
54: * <ul>
55: * <li>Lifecycle callbacks declared by the entity or its managed ancestors,</li>
56: * <li>Callbacks declared in classes referenced by {@link EntityListeners} on the entity or its ancestors.</li>
57: * </ul>
58: *
59: * @param instance The instance for persist
60: */
61: public void invokePrePersistCallbacks(Object instance) {
62: invokeCallbacks(instance, LifecycleEvent.PRE_PERSIST);
63: }
64:
65: private void invokeCallbacks(Object instance, LifecycleEvent lifecycleEvent) {
66: // The manager may be invoked from multiple threads (as each entity type has its listener and entity types are global for PU)
67: synchronized (this) {
68: if (instancesBeingProcessed.containsKey(instance)) {
69: return;
70: }
71: instancesBeingProcessed.put(instance, EMPTY);
72: }
73: try {
74: invokeEntityListenerCallbacks(instance, lifecycleEvent);
75: invokeInternalCallbacks(instance, lifecycleEvent);
76: } finally {
77: synchronized (this) {
78: instancesBeingProcessed.remove(instance);
79: }
80: }
81: }
82:
83: private void invokeEntityListenerCallbacks(Object instance, LifecycleEvent lifecycleEvent) {
84: if (parent != null) {
85: parent.invokeEntityListenerCallbacks(instance, lifecycleEvent);
86: }
87: if (entityListeners != null) {
88: entityListeners
89: .forEach(listener -> getEntityListenerCallback(listener, lifecycleEvent).ifPresent(method -> {
90: if (!method.isAccessible()) {
91: method.setAccessible(true);
92: }
93: try {
94: method.invoke(listener, instance);
95: } catch (IllegalAccessException | InvocationTargetException e) {
96: throw new OWLPersistenceException("Unable to invoke entity listener method " + method, e);
97: }
98: }));
99: }
100: }
101:
102: private Optional<Method> getEntityListenerCallback(Object listener, LifecycleEvent lifecycleEvent) {
103: final Map<LifecycleEvent, Method> callbacks = entityListenerCallbacks.get(listener);
104: return Optional.ofNullable(callbacks.get(lifecycleEvent));
105: }
106:
107: private void invokeInternalCallbacks(Object instance, LifecycleEvent lifecycleEvent) {
108: if (parent != null) {
109: parent.invokeInternalCallbacks(instance, lifecycleEvent);
110: }
111: if (lifecycleCallbacks.containsKey(lifecycleEvent)) {
112: final Method listener = lifecycleCallbacks.get(lifecycleEvent);
113: if (!listener.isAccessible()) {
114: listener.setAccessible(true);
115: }
116: try {
117: listener.invoke(instance);
118: } catch (IllegalAccessException | InvocationTargetException e) {
119: throw new OWLPersistenceException("Unable to invoke method lifecycle listener " + listener, e);
120: }
121: }
122: }
123:
124: /**
125: * Calls post-persist callbacks for the specified instance.
126: * <p>
127: * These include:
128: * <ul>
129: * <li>Lifecycle callbacks declared by the entity or its managed ancestors,</li>
130: * <li>Callbacks declared in classes referenced by {@link EntityListeners} on the entity or its ancestors.</li>
131: * </ul>
132: *
133: * @param instance The newly persisted instance
134: */
135: public void invokePostPersistCallbacks(Object instance) {
136: invokeCallbacks(instance, LifecycleEvent.POST_PERSIST);
137: }
138:
139: /**
140: * Calls post-load callbacks for the specified instance.
141: * <p>
142: * These include:
143: * <ul>
144: * <li>Lifecycle callbacks declared by the entity or its managed ancestors,</li>
145: * <li>Callbacks declared in classes referenced by {@link EntityListeners} on the entity or its ancestors.</li>
146: * </ul>
147: *
148: * @param instance The loaded instance
149: */
150: public void invokePostLoadCallbacks(Object instance) {
151: invokeCallbacks(instance, LifecycleEvent.POST_LOAD);
152: }
153:
154: /**
155: * Calls pre-update callbacks for the specified instance.
156: * <p>
157: * These include:
158: * <ul>
159: * <li>Lifecycle callbacks declared by the entity or its managed ancestors,</li>
160: * <li>Callbacks declared in classes referenced by {@link EntityListeners} on the entity or its ancestors.</li>
161: * </ul>
162: *
163: * @param instance The updated instance
164: */
165: public void invokePreUpdateCallbacks(Object instance) {
166: invokeCallbacks(instance, LifecycleEvent.PRE_UPDATE);
167: }
168:
169: /**
170: * Calls post-update callbacks for the specified instance.
171: * <p>
172: * These include:
173: * <ul>
174: * <li>Lifecycle callbacks declared by the entity or its managed ancestors,</li>
175: * <li>Callbacks declared in classes referenced by {@link EntityListeners} on the entity or its ancestors.</li>
176: * </ul>
177: *
178: * @param instance The updated instance
179: */
180: public void invokePostUpdateCallbacks(Object instance) {
181: invokeCallbacks(instance, LifecycleEvent.POST_UPDATE);
182: }
183:
184: /**
185: * Calls pre-remove callbacks for the specified instance.
186: * <p>
187: * These include:
188: * <ul>
189: * <li>Lifecycle callbacks declared by the entity or its managed ancestors,</li>
190: * <li>Callbacks declared in classes referenced by {@link EntityListeners} on the entity or its ancestors.</li>
191: * </ul>
192: *
193: * @param instance The instance for removal
194: */
195: public void invokePreRemoveCallbacks(Object instance) {
196: invokeCallbacks(instance, LifecycleEvent.PRE_REMOVE);
197: }
198:
199: /**
200: * Calls post-remove callbacks for the specified instance.
201: * <p>
202: * These include:
203: * <ul>
204: * <li>Lifecycle callbacks declared by the entity or its managed ancestors,</li>
205: * <li>Callbacks declared in classes referenced by {@link EntityListeners} on the entity or its ancestors.</li>
206: * </ul>
207: *
208: * @param instance The removed instance
209: */
210: public void invokePostRemoveCallbacks(Object instance) {
211: invokeCallbacks(instance, LifecycleEvent.POST_REMOVE);
212: }
213:
214: void setParent(EntityLifecycleListenerManager parent) {
215: this.parent = parent;
216: }
217:
218: void addEntityListener(Object entityListener) {
219:• assert entityListener != null;
220:
221:• if (entityListeners == null) {
222: this.entityListeners = new ArrayList<>();
223: }
224: entityListeners.add(entityListener);
225:• if (entityListenerCallbacks == null) {
226: this.entityListenerCallbacks = new HashMap<>();
227: }
228: entityListenerCallbacks.put(entityListener, new EnumMap<>(LifecycleEvent.class));
229: }
230:
231: void addLifecycleCallback(LifecycleEvent event, Method callback) {
232: assert event != null;
233: assert callback != null;
234:
235: lifecycleCallbacks.put(event, callback);
236: }
237:
238: Map<LifecycleEvent, Method> getLifecycleCallbacks() {
239: return Collections.unmodifiableMap(lifecycleCallbacks);
240: }
241:
242: boolean hasLifecycleCallback(LifecycleEvent event) {
243: return lifecycleCallbacks.containsKey(event);
244: }
245:
246: List<Object> getEntityListeners() {
247: return entityListeners != null ? Collections.unmodifiableList(entityListeners) : Collections.emptyList();
248: }
249:
250: Map<Object, Map<LifecycleEvent, Method>> getEntityListenerCallbacks() {
251: return entityListenerCallbacks != null ? Collections.unmodifiableMap(entityListenerCallbacks) :
252: Collections.emptyMap();
253: }
254:
255: void addEntityListenerCallback(Object listener, LifecycleEvent event, Method callback) {
256: assert listener != null;
257: assert event != null;
258: assert callback != null;
259: assert entityListenerCallbacks.containsKey(listener);
260:
261: entityListenerCallbacks.get(listener).put(event, callback);
262: }
263:
264: boolean hasEntityListenerCallback(Object listener, LifecycleEvent event) {
265: return entityListenerCallbacks != null && entityListenerCallbacks.containsKey(listener) &&
266: entityListenerCallbacks.get(listener).containsKey(event);
267: }
268: }