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