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: [email protected]
Reporter: [email protected]
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: [email protected]
For additional commands, e-mail: [email protected]