You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2021/03/24 14:03:30 UTC

[cayenne] branch master updated: CAY-2705 Performance of callback annotation processing

This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new 54d7728  CAY-2705 Performance of callback annotation processing
54d7728 is described below

commit 54d7728873ac0f0646d07a794c97f1685fe5dd93
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Wed Mar 24 15:56:14 2021 +0300

    CAY-2705 Performance of callback annotation processing
---
 RELEASE-NOTES.txt                                  |  1 +
 .../org/apache/cayenne/map/EntityResolver.java     |  2 +-
 .../apache/cayenne/reflect/CallbackOnEntity.java   | 65 +++++++++++-----------
 .../reflect/LifecycleCallbackEventHandler.java     |  9 +++
 .../cayenne/reflect/LifecycleCallbackRegistry.java | 18 +++++-
 5 files changed, 60 insertions(+), 35 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 66815b1..f8ed0ac 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -16,6 +16,7 @@ Changes/New Features:
 Bug Fixes:
 
 CAY-2702 Modeler: Callbacks table has too small default width
+CAY-2705 Performance of callback annotation processing
 
 ----------------------------------
 Release: 4.2.M3
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
index 0d498a3..b62bfa3 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
@@ -176,7 +176,7 @@ public class EntityResolver implements MappingNamespace, Serializable {
                 for (Method m : entityClass.getDeclaredMethods()) {
                     LIFECYCLE_EVENT_MAP.forEach((eventType, annotationType) -> {
                         if(m.getDeclaredAnnotation(annotationType) != null) {
-                            callbackRegistry.addCallback(eventType, entityClass, m.getName());
+                            callbackRegistry.addCallback(eventType, entityClass, m);
                         }
                     });
                 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/CallbackOnEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/CallbackOnEntity.java
index 610217a..ecf5f82 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/CallbackOnEntity.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/CallbackOnEntity.java
@@ -33,19 +33,31 @@ import org.apache.cayenne.util.Util;
  */
 class CallbackOnEntity extends AbstractCallback {
 
-    private Method callbackMethod;
+    private final Method callbackMethod;
 
-    CallbackOnEntity(Class<?> objectClass, String methodName)
-            throws IllegalArgumentException {
-        this.callbackMethod = findMethod(objectClass, methodName);
+    CallbackOnEntity(Class<?> objectClass, String methodName) throws IllegalArgumentException {
+        this(findMethod(objectClass, methodName));
+    }
+
+    /**
+     * @since 4.2
+     */
+    CallbackOnEntity(Method method) throws IllegalArgumentException {
+        if(!validateMethod(method)) {
+            throw new IllegalArgumentException("Class " + method.getDeclaringClass().getName()
+                    + " has no valid callback method '" + method.getName() + "'");
+        }
+        this.callbackMethod = method;
+        if (!Util.isAccessible(callbackMethod)) {
+            callbackMethod.setAccessible(true);
+        }
     }
 
     @Override
     public void performCallback(Object entity) {
         try {
             callbackMethod.invoke(entity, (Object[]) null);
-        }
-        catch (Exception e) {
+        } catch (Exception e) {
             throw new CayenneRuntimeException("Error invoking entity callback method "
                     + callbackMethod.getName(), e);
         }
@@ -59,32 +71,23 @@ class CallbackOnEntity extends AbstractCallback {
                 + callbackMethod.getName();
     }
 
-    private Method findMethod(Class<?> objectClass, String methodName)
-            throws IllegalArgumentException {
-        Method[] methods = objectClass.getDeclaredMethods();
-        for (Method method : methods) {
-            if (methodName.equals(method.getName())) {
-
-                // must be non-static, void, with no args
-                // JPA spec also requires it to be non-final, but we don't care
-                int modifiers = method.getModifiers();
-                if (!Modifier.isStatic(modifiers)
-                        && Void.TYPE.isAssignableFrom(method.getReturnType())
-                        && method.getParameterTypes().length == 0) {
-
-                    if (!Util.isAccessible(method)) {
-                        method.setAccessible(true);
-                    }
+    static private boolean validateMethod(Method method) {
+        int modifiers = method.getModifiers();
+        // must be non-static, void, with no args
+        // JPA spec also requires it to be non-final, but we don't care
+        return !Modifier.isStatic(modifiers)
+                && Void.TYPE.isAssignableFrom(method.getReturnType())
+                && method.getParameterTypes().length == 0;
+    }
 
-                    return method;
-                }
-            }
+    static private Method findMethod(Class<?> objectClass, String methodName) throws IllegalArgumentException {
+        Method method;
+        try {
+            method = objectClass.getDeclaredMethod(methodName);
+        } catch (NoSuchMethodException ex) {
+            throw new IllegalArgumentException("Class " + objectClass.getName()
+                    + " has no valid callback method '" + methodName + "'");
         }
-
-        throw new IllegalArgumentException("Class "
-                + objectClass.getName()
-                + " has no valid callback method '"
-                + methodName
-                + "'");
+        return method;
     }
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
index 28b478f..9dd4147 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackEventHandler.java
@@ -87,6 +87,15 @@ class LifecycleCallbackEventHandler {
     }
 
     /**
+     * Registers a callback method to be invoked on an entity class instances when a
+     * lifecycle event occurs.
+     * @since 4.2
+     */
+    void addListener(Class<?> entityClass, Method method) {
+        addCallback(entityClass, new CallbackOnEntity(method));
+    }
+
+    /**
      * Registers callback method to be invoked on a provided non-entity object when a
      * lifecycle event occurs.
      */
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java
index d730db6..c8af4c8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LifecycleCallbackRegistry.java
@@ -64,9 +64,7 @@ public class LifecycleCallbackRegistry {
 
 		this.entityResolver = resolver;
 
-		// initialize callbacks map in constructor to avoid synchronization
-		// issues
-		// downstream.
+		// initialize callbacks map in constructor to avoid synchronization issues downstream.
 		this.eventCallbacks = new LifecycleCallbackEventHandler[LifecycleEvent.values().length];
 		for (int i = 0; i < eventCallbacks.length; i++) {
 			eventCallbacks[i] = new LifecycleCallbackEventHandler();
@@ -161,6 +159,20 @@ public class LifecycleCallbackRegistry {
 	}
 
 	/**
+	 * Registers a callback method to be invoked on an entity class instances
+	 * when a lifecycle event occurs.
+	 *
+	 * @param type of the lifecycle event
+	 * @param entityClass type of the entity
+	 * @param method callback method reference
+	 *
+	 * @since 4.2
+	 */
+	public void addCallback(LifecycleEvent type, Class<?> entityClass, Method method) {
+		eventCallbacks[type.ordinal()].addListener(entityClass, method);
+	}
+
+	/**
 	 * Adds a listener, mapping its methods to events based on annotations.
 	 * 
 	 * @since 3.1