Skip to content

Method: EntityLifecycleCallbackResolver()

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