https://issues.apache.org/bugzilla/show_bug.cgi?id=57274

            Bug ID: 57274
           Summary: Annotation scanning can cause classes to skip class
                    transformation
           Product: Tomcat 8
           Version: 8.0.14
          Hardware: PC
                OS: Mac OS X 10.1
            Status: NEW
          Severity: major
          Priority: P2
         Component: Catalina
          Assignee: dev@tomcat.apache.org
          Reporter: pverhey...@broadleafcommerce.com

Full context, I am using Spring 4.1.2.RELEASE in my application with Spring
Instrument hooked up as a -javaagent
(-javaagent:/path/to/spring-instrument-4.1.2.RELEASE). We are relying on class
transformation to transform some of our JPA classes and add new methods/fields
on startup. However, I observed that for some classes, class transformation is
completely skipped.

After some debugging, I have narrowed this down to the getResourceInternal
method of WebappClassLoaderBase. Specifically, this is giving me grief:

    protected ResourceEntry findResourceInternal(final String name, final
String path) {

        if (!state.isAvailable()) {
            log.info(sm.getString("webappClassLoader.stopped", name));
            return null;
        }

        if (name == null || path == null) {
            return null;
        }
        // This is returning a non-null entry. On every other class that I
observed, this returns null and it continues on to the class transformers
        ResourceEntry entry = resourceEntries.get(path);
        if (entry != null) {
            return entry;
        }
    ...
    ...
    // Remaining implementation excluded, but below here is where it loops
through the configured `ClassFileTransformer`s

When my JPA persistent unit is loaded, about 98% of the classes return null for
resourceEntries.get(path). Some classes return a non-null entry, and so they
are immediately returned thus skipping the class transformation below.

With more debugging, I came across the code on startup that scans every class
in every jar in the web application on startup to look for SCIs
(ContextConfig.webConfig()). For almost every class that it looks for, it grabs
an input stream based for the class file based on the FileInputStream and then
puts it in a cache.

However, this process also looks for super classes (from ContextConfig):

    private void populateJavaClassCache(String className, JavaClass javaClass)
{
        if (javaClassCache.containsKey(className)) {
            return;
        }

        // Add this class to the cache
        javaClassCache.put(className, new JavaClassCacheEntry(javaClass));

        populateJavaClassCache(javaClass.getSuperclassName());

        for (String iterface : javaClass.getInterfaceNames()) {
            populateJavaClassCache(iterface);
        }
    }

where populateJavaClassCache:

    private void populateJavaClassCache(String className) {
        if (!javaClassCache.containsKey(className)) {
            String name = className.replace('.', '/') + ".class";
            try (InputStream is =
context.getLoader().getClassLoader().getResourceAsStream(name)) {
                if (is == null) {
                    return;
                }
                ClassParser parser = new ClassParser(is);
                JavaClass clazz = parser.parse();
                populateJavaClassCache(clazz.getClassName(), clazz);
            } catch (ClassFormatException e) {
                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
                        className), e);
            } catch (IOException e) {
                log.debug(sm.getString("contextConfig.invalidSciHandlesTypes",
                        className), e);
            }
        }
    }

Using context.getLoader().getClassLoader().getResourceAsStream() causes the
WebappClassLoader to load the class and store it in the resourceEntries map.
However at this point, the WebappClassLoader does not have any
ClassTransformers registered with it yet, and thus no class transformation
happens.

So, if you have any JPA entity that is a superclass of something else, class
transformation would be skipped.

I am not sure exactly what the right fix is here but I feel like it should be
something that skips using the WebappClassLoader to load the class? That could
be very expensive though as we would need to scan through all of the jars in
the classpath again to find the superclass definition (which would make this
O(n2).

I have some additional information at
https://github.com/BroadleafCommerce/BroadleafCommerce/issues/1171 but I put in
just the Tomcat-relevant info here.

-- 
You are receiving this mail because:
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to