Skip to content

Method: invokePostLoadCallbacks(Object)

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