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