Skip to content

Package: EntityLifecycleListenerManager

EntityLifecycleListenerManager

nameinstructionbranchcomplexitylinemethod
EntityLifecycleListenerManager()
M: 0 C: 19
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
addEntityListener(Object)
M: 4 C: 34
89%
M: 1 C: 5
83%
M: 1 C: 3
75%
M: 0 C: 8
100%
M: 0 C: 1
100%
addEntityListenerCallback(Object, LifecycleEvent, Method)
M: 16 C: 25
61%
M: 4 C: 4
50%
M: 4 C: 1
20%
M: 0 C: 6
100%
M: 0 C: 1
100%
addLifecycleCallback(LifecycleEvent, Method)
M: 8 C: 13
62%
M: 2 C: 2
50%
M: 2 C: 1
33%
M: 0 C: 4
100%
M: 0 C: 1
100%
addParent(EntityLifecycleListenerManager)
M: 4 C: 9
69%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 3
100%
M: 0 C: 1
100%
empty()
M: 0 C: 2
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getEntityListenerCallback(Object, LifecycleEvent)
M: 0 C: 12
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
getEntityListenerCallbacks()
M: 1 C: 8
89%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 1
100%
getEntityListeners()
M: 0 C: 9
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getLifecycleCallbacks()
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
getParents()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
hasEntityLifecycleCallback(LifecycleEvent)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
hasEntityListenerCallback(Object, LifecycleEvent)
M: 0 C: 20
100%
M: 2 C: 4
67%
M: 2 C: 2
50%
M: 0 C: 2
100%
M: 0 C: 1
100%
hasLifecycleCallback(LifecycleEvent)
M: 0 C: 16
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
invokeCallbacks(Object, LifecycleEvent)
M: 0 C: 42
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 11
100%
M: 0 C: 1
100%
invokeEntityListenerCallbacks(Object, LifecycleEvent)
M: 0 C: 17
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
invokeInternalCallbacks(Object, LifecycleEvent)
M: 8 C: 32
80%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 2 C: 8
80%
M: 0 C: 1
100%
invokePostLoadCallbacks(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
invokePostPersistCallbacks(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
invokePostRemoveCallbacks(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
invokePostUpdateCallbacks(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
invokePrePersistCallbacks(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
invokePreRemoveCallbacks(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
invokePreUpdateCallbacks(Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
lambda$hasLifecycleCallback$4(LifecycleEvent, Object)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$invokeEntityListenerCallbacks$0(Object, LifecycleEvent, EntityLifecycleListenerManager)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$invokeEntityListenerCallbacks$1(Object, Object, Method)
M: 8 C: 19
70%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 2 C: 5
71%
M: 0 C: 1
100%
lambda$invokeEntityListenerCallbacks$2(LifecycleEvent, Object, Object)
M: 0 C: 9
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
lambda$invokeInternalCallbacks$3(Object, LifecycleEvent, EntityLifecycleListenerManager)
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
setParents(Set)
M: 11 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 0 C: 5
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

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