You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by rm...@apache.org on 2020/07/14 17:39:02 UTC

[openjpa] 01/01: OPENJPA-2817 dropping _transforming from PCClassFileTransformer and replace it by a minimal exclusion list

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

rmannibucau pushed a commit to branch OPENJPA-2817_PCClassFileTransformer-exclusions
in repository https://gitbox.apache.org/repos/asf/openjpa.git

commit 763e20c3777be37df7eafc62f3b103d524f51704
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Tue Jul 14 19:38:48 2020 +0200

    OPENJPA-2817 dropping _transforming from PCClassFileTransformer and replace it by a minimal exclusion list
---
 .../openjpa/enhance/PCClassFileTransformer.java    | 80 +++++++++++++++-------
 1 file changed, 55 insertions(+), 25 deletions(-)

diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
index 872d413..63b4d2e 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCClassFileTransformer.java
@@ -55,7 +55,6 @@ public class PCClassFileTransformer
     private final ClassLoader _tmpLoader;
     private final Log _log;
     private final Set _names;
-    private boolean _transforming = false;
 
     /**
      * Constructor.
@@ -107,6 +106,9 @@ public class PCClassFileTransformer
             _log.info(_loc.get("runtime-enhance-pcclasses"));
     }
 
+    // this must be called under a proper locking, this is guaranteed by either
+    // a correct concurrent classloader with transformation support OR
+    // a mono-threaded access guarantee (build, deploy time enhancements).
     @Override
     public byte[] transform(ClassLoader loader, String className,
         Class redef, ProtectionDomain domain, byte[] bytes)
@@ -118,17 +120,43 @@ public class PCClassFileTransformer
         if (className == null) {
             return null;
         }
-        // prevent re-entrant calls, which can occur if the enhancing
-        // loader is used to also load OpenJPA libraries; this is to prevent
-        // recursive enhancement attempts for internal openjpa libraries
-        if (_transforming)
-            return null;
-
-        _transforming = true;
 
         return transform0(className, redef, bytes);
     }
 
+    // very simplified flavor of
+    // @apache/tomee >
+    // container/openejb-core >
+    // org/apache/openejb/util/classloader/URLClassLoaderFirst.java#L207
+    private boolean isExcluded(final String className) {
+        if (    // api
+                className.startsWith("javax/") ||
+                className.startsWith("jakarta/") ||
+                // openjpa dependencies
+                className.startsWith("serp/") ||
+                // jvm
+                className.startsWith("java/") ||
+                className.startsWith("sun/") ||
+                className.startsWith("jdk/")) {
+            return true;
+        }
+        // it is faster to use substring when multiple tests are needed
+        if (className.startsWith("org/apache/")) {
+            final String sub = className.substring("org/apache/".length());
+            if (sub.startsWith("openjpa/") ||
+                    sub.startsWith("commons/")) {
+                return true;
+            }
+        }
+        if (className.startsWith("com/")) {
+            final String sub = className.substring("com/".length());
+            if (sub.startsWith("oracle/") || sub.startsWith("sun/")) { // jvm
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * We have to split the transform method into two methods to avoid
      * ClassCircularityError when executing method using pure-JIT JVMs
@@ -151,7 +179,7 @@ public class PCClassFileTransformer
             try {
                 PCEnhancer enhancer = new PCEnhancer(_repos.getConfiguration(),
                         new Project().loadClass(new ByteArrayInputStream(bytes),
-                                _tmpLoader), _repos);
+                                _tmpLoader), _repos, _tmpLoader);
                 enhancer.setAddDefaultConstructor(_flags.addDefaultConstructor);
                 enhancer.setEnforcePropertyRestrictions
                         (_flags.enforcePropertyRestrictions);
@@ -172,7 +200,6 @@ public class PCClassFileTransformer
                 throw (IllegalClassFormatException) t;
             throw new GeneralException(t);
         } finally {
-            _transforming = false;
             if (returnBytes != null && _log.isTraceEnabled())
                 _log.trace(_loc.get("runtime-enhance-complete", className,
                     bytes.length, returnBytes.length));
@@ -182,41 +209,44 @@ public class PCClassFileTransformer
     /**
      * Return whether the given class needs enhancement.
      */
-    private Boolean needsEnhance(String clsName, Class redef, byte[] bytes) {
-        if (redef != null) {
-            Class[] intfs = redef.getInterfaces();
+    private Boolean needsEnhance(String clsName, Class<?> redef, byte[] bytes) {
+        final boolean excluded = isExcluded(clsName);
+
+        if (!excluded && redef != null) { // if excluded we just check it is not in names
+            Class<?>[] intfs = redef.getInterfaces();
             for (int i = 0; i < intfs.length; i++)
-                if (PersistenceCapable.class.getName().
-                    equals(intfs[i].getName()))
-                    return Boolean.valueOf(!isEnhanced(bytes));
+                if (PersistenceCapable.class.getName()
+                    .equals(intfs[i].getName()))
+                    return false;
             return null;
         }
 
         if (_names != null) {
             if (_names.contains(clsName.replace('/', '.')))
-                return Boolean.valueOf(!isEnhanced(bytes));
+                return !isEnhanced(bytes);
+            if (excluded) {
+                return false;
+            }
             return null;
         }
 
-        if (clsName.startsWith("java/") || clsName.startsWith("javax/"))
-            return null;
-        if (isEnhanced(bytes))
-            return Boolean.FALSE;
+        if (excluded || isEnhanced(bytes))
+            return false;
 
         try {
-            Class c = Class.forName(clsName.replace('/', '.'), false,
+            Class<?> c = Class.forName(clsName.replace('/', '.'), false,
                 _tmpLoader);
             if (_repos.getMetaData(c, null, false) != null)
-                return Boolean.TRUE;
+                return true;
             return null;
         } catch (ClassNotFoundException cnfe) {
             // cannot load the class: this might mean that it is a proxy
             // or otherwise inaccessible class which can't be an entity
-            return Boolean.FALSE;
+            return false;
         } catch (LinkageError cce) {
             // this can happen if we are loading classes that this
             // class depends on; these will never be enhanced anyway
-            return Boolean.FALSE;
+            return false;
         } catch (RuntimeException re) {
             throw re;
         } catch (Throwable t) {