Skip to content

Method: resolveLifecycleCallbacks()

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.exception.MetamodelInitializationException;
18: import cz.cvut.kbss.jopa.model.annotations.EntityListeners;
19: import cz.cvut.kbss.jopa.model.lifecycle.LifecycleEvent;
20:
21: import java.lang.reflect.Method;
22: import java.lang.reflect.Modifier;
23:
24: class EntityLifecycleCallbackResolver {
25:
26: private final AbstractIdentifiableType<?> managedType;
27: private final EntityLifecycleListenerManager manager;
28:
29: /**
30: * Creates a lifecycle callback resolver for the specified entity type.
31: * @param et The type to process
32: */
33: EntityLifecycleCallbackResolver(AbstractIdentifiableType<?> et) {
34: this.managedType = et;
35: this.manager = new EntityLifecycleListenerManager();
36: }
37:
38: /**
39: * Builds an instance of {@link EntityLifecycleListenerManager} for the specified managed type.
40: * <p>
41: * The manager contains:
42: * <ul>
43: * <li>Lifecycle callbacks declared in the entity class,</li>
44: * <li>EntityListener instance declared on the entity class,</li>
45: * <li>Reference to parent {@link EntityLifecycleListenerManager} (if exists).</li>
46: * </ul>
47: *
48: * @return Lifecycle listener manager instance
49: */
50: EntityLifecycleListenerManager resolve() {
51: resolveLifecycleCallbacks();
52: resolveEntityListeners();
53: if (managedType.getSupertype() != null) {
54: manager.setParent(managedType.getSupertype().getLifecycleListenerManager());
55: }
56: return manager;
57: }
58:
59: private void resolveLifecycleCallbacks() {
60: final Class<?> cls = managedType.getJavaType();
61:• for (Method m : cls.getDeclaredMethods()) {
62:• for (LifecycleEvent hookType : LifecycleEvent.values()) {
63:• if (m.getDeclaredAnnotation(hookType.getAnnotation()) != null) {
64: verifyCallbackNotAlreadyDefined(hookType);
65: verifyLifecycleCallbackSignature(m);
66: manager.addLifecycleCallback(hookType, m);
67: }
68: }
69: }
70: }
71:
72: private void verifyCallbackNotAlreadyDefined(LifecycleEvent hookType) {
73: if (manager.hasLifecycleCallback(hookType)) {
74: throw new MetamodelInitializationException("The type [" + managedType.getJavaType().getName() +
75: "] has multiple lifecycle callbacks for the lifecycle event [" + hookType + "].");
76: }
77: }
78:
79: private void verifyLifecycleCallbackSignature(Method callback) {
80: if (callback.getParameterCount() > 0) {
81: throw MetamodelInitializationException
82: .invalidArgumentsForLifecycleListener(managedType.getJavaType(), callback);
83: }
84: if (!callback.getReturnType().equals(Void.TYPE)) {
85: throw MetamodelInitializationException
86: .invalidReturnTypeForLifecycleListener(managedType.getJavaType(), callback);
87: }
88: if (Modifier.isFinal(callback.getModifiers()) || Modifier.isStatic(callback.getModifiers())) {
89: throw MetamodelInitializationException
90: .invalidLifecycleListenerModifier(managedType.getJavaType(), callback);
91: }
92: }
93:
94: private void resolveEntityListeners() {
95: final EntityListeners listenersAnn = managedType.getJavaType().getDeclaredAnnotation(EntityListeners.class);
96: if (listenersAnn == null) {
97: return;
98: }
99: for (Class<?> listenerType : listenersAnn.value()) {
100: try {
101: final Object listener = listenerType.newInstance();
102: manager.addEntityListener(listener);
103: resolveEntityListenerCallbacks(listener, listenerType);
104: } catch (InstantiationException | IllegalAccessException e) {
105: throw new MetamodelInitializationException("Unable to instantiate entity listener of type "
106: + listenerType + ". The listener has to have a public no-arg constructor.");
107: }
108: }
109: }
110:
111: private void resolveEntityListenerCallbacks(Object listener, Class<?> listenerType) {
112: for (Method m : listenerType.getDeclaredMethods()) {
113: for (LifecycleEvent hookType : LifecycleEvent.values()) {
114: if (m.getDeclaredAnnotation(hookType.getAnnotation()) != null) {
115: verifyEntityListenerCallbackNotAlreadyDefined(listener, listenerType, hookType);
116: verifyEntityListenerCallbackSignature(listenerType, m);
117: manager.addEntityListenerCallback(listener, hookType, m);
118: }
119: }
120: }
121: }
122:
123: private void verifyEntityListenerCallbackNotAlreadyDefined(Object listener, Class<?> listenerType,
124: LifecycleEvent event) {
125: if (manager.hasEntityListenerCallback(listener, event)) {
126: throw new MetamodelInitializationException("The entity listener [" + listenerType.getName() +
127: "] has multiple callbacks for the lifecycle event [" + event + "].");
128: }
129: }
130:
131: private void verifyEntityListenerCallbackSignature(Class<?> listenerType, Method callback) {
132: verifyCallbackParameterCount(listenerType, callback);
133: verifyCallbackParameterTypes(listenerType, callback);
134: verifyCallbackReturnType(listenerType, callback);
135: verifyCallbackModifiers(listenerType, callback);
136: }
137:
138: private void verifyCallbackModifiers(Class<?> listenerType, Method callback) {
139: if (Modifier.isFinal(callback.getModifiers()) || Modifier.isStatic(callback.getModifiers())) {
140: throw MetamodelInitializationException.invalidEntityListenerCallbackModifier(listenerType, callback);
141: }
142: }
143:
144: private void verifyCallbackReturnType(Class<?> listenerType, Method callback) {
145: if (!callback.getReturnType().equals(Void.TYPE)) {
146: throw MetamodelInitializationException.invalidReturnTypeForEntityListenerCallback(listenerType, callback);
147: }
148: }
149:
150: private void verifyCallbackParameterTypes(Class<?> listenerType, Method callback) {
151: final Class<?> paramType = callback.getParameterTypes()[0];
152: if (!paramType.isAssignableFrom(Object.class) && !paramType.isAssignableFrom(managedType.getJavaType())) {
153: throw MetamodelInitializationException
154: .invalidEntityListenerCallbackParameterType(managedType.getJavaType(), listenerType, callback);
155: }
156: }
157:
158: private void verifyCallbackParameterCount(Class<?> listenerType, Method callback) {
159: if (callback.getParameterCount() != 1) {
160: throw MetamodelInitializationException
161: .invalidArgumentsForEntityListenerCallback(listenerType, callback);
162: }
163: }
164: }