I'm currently working on a project, trying to insert additional Log4j logging statements into a running webapp. To realise that, I start a Java agent via
JVM parameter when launching WildFly:
-javaagent:path/to/agent.jar
The agent's premain method receives the Instrumentation object and establishes a MBean for remote access. The logging insertion is achieved using Instrumentation and Javassist. So far, this works perfectly.
However, to keep that working, the agent.jar also has to reside in the webapp's WAR file on deployment, since the log4j Logger class used for logging ships with this JAR. If not, I get a VerifyError when the class definition is updated by Instrumentation API. But trying to load e.g. classes from java.lang by inserting code like "Math.random()" works as expected.
It's important to notice that the agent classes are loaded with an AppClassLoader which is also the parent of the application's ModuleClassLoader.
Therefore I'm wondering why classes residing in agent.jar can't be loaded by delegation through the ModuleClassLoader.
These observation brought me to the assumption that the webapp module needs to declare an explicit dependency on external JARs, even if the classes are known to the parent AppClassLoader. For security issues this would make sense to me.
Can anyone confirm these assumptions or does anyone have another idea or experience what causes this behavior?
Thanks!
---------------- EDIT ---------------------
Playing around with the WildFly classloading mechanism helped me to describe my problem more in detail. Assuming I want to load a class named com.example.LoggerClass (residing in agent.jar!) using the ModuleClassLoader in a ManagedBean belonging to my webapp:
Class<?> aClass = this.getClass().getClassLoader().loadClass("com.example.LoggerClass");
This results in a ClassNotFoundExxception!
But delegating this to the underlying AppClassLoader manually works perfectly:
Class<?> aClass = this.getClass().getClassLoader().getParent().loadClass("com.example.LoggerClass");
Now the JBoss docs concerning ModuleClassLoader's loadClass method tell the following:
Find a class, possibly delegating to other loader(s)
This may explain the behavior I showed above, assuming that ModuleClassLoader does not delegate class loading because of security issues. Is there any way to override this and make ModuleClassLoader delegate to AppClassLoader in certain cases?
An Java agent is always loaded by the system class loader. All of its dependencies must be available on the class path. If you are howver experienceing a verifier error, your byte code is illegal as verification happens before loading completes. This means, your problem is not class loader related.
Related
When trying to inject a class which is in the java.lang namespace via java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch on a OpenJDK 11, nothing happens and no error is thrown. When placing the class to inject into a different package, it works as expected.
JarFile jar = new JarFile(new File("file/to/bootstrap.jar));
instrumentation.appendToBootstrapClassLoaderSearch(jar);
// throws ClassNotFoundException java/lang/Dispatcher
Class.forName("java.lang.Dispatcher", false, null);
bootstrap.jar
└─ java/lang/Dispatcher.class
The reason I want to do this is to overcome issues with some OSGi containers. They typically restrict delegation to the bootstrap class loader to only certain packages. By default that obviously always includes java.* which is why I want to put my Dispatcher class there. I'm aware of org.osgi.framework.bootdelegation but that property only gets read during initialization. That means when attaching an agent at runtime, it's already too late to override this value.
An alternative would be to instrument all known OSGi class loaders and to white-list the agent classes. But doing that for each framework and test that for each version seems less feasible.
How can I inject a custom class like java.lang.Dispatcher into the bootstrap class loader? Are there other patterns or best practices to avoid OSGi bootdelegation issues?
To provide some more context:
My idea is to only inject this one Dispatcher class into the bootstrap class loader. The dispatcher basically just holds a static Map. The rest of the agent's classes would be loaded by a dedicated URLClassLoader which is a child of the bootstrap class loader. The agent would then register MethodHandles in the dispatcher's Map so that the injected byte code can get ahold of the MethodHandles which enable accessing the agent's classes loaded in the agent class loader.
It is possible by using unsafe API. Since Java 9, the boot class loader's implementation has changed to only check a designated jmod for a known package, but the boot search path is no longer checked.
Java 11 also removed the sun.misc.Unsafe#defineClass method but the same method is still available in jdk.internal.misc.Unsafe.
You do have to open that class's module which is internal. You can either do so by using sun.misc.Unsafe which allows you to write a field value (accessible) without accessibility checks or by using Instrumentation's official API.
If you are using Byte Buddy, have a look at the ClassInjector implementations which offer implementations for all approaches.
There is an open ticket for adressing the need of Java agents to inject helper classes but until it is resolved, this is a common workaround.
Working on an existing application, it runs on Weblogic as a massive ear file.
There is custom code, written by my organization, as well as code written by the vendor that all runs on one classpath when weblogic starts up.
Some of our custom code uses spring 1.2, in the latest version of the vendors code, they use spring3. So we cannot get the ear to completely work unless we can get each component the spring version it needs in order to function. But since they are both using the classpath that weblogic is started on, either spring1.2 or spring 3.0 will be first depending on the order in the classpath.
Am I stuck? Missing something? I've never had to deal with classpaths at this level.
Thanks
Classloaders use a delegation model when loading a class. The classloader implementation first checks its cache to see if the requested class has already been loaded. This class verification improves performance in that its cached memory copy is used instead of repeated loading of a class from disk. If the class is not found in its cache, the current classloader asks its parent for the class. Only if the parent cannot load the class does the classloader attempt to load the class. If a class exists in both the parent and child classloaders, the parent version is loaded. This delegation model is followed to avoid multiple copies of the same form being loaded. Multiple copies of the same class can lead to a ClassCastException.
Think setting the following in weblogic.xml might help
prefer-web-inf-classes Element
The weblogic.xml Web application deployment descriptor contains a prefer-web-inf-classes element (a sub-element of the element). By default, this element is set to False. Setting this element to True subverts the classloader delegation model so that class definitions from the Web application are loaded in preference to class definitions in higher-level classloaders. This allows a Web application to use its own version of a third-party class, which might also be part of WebLogic Server. See "weblogic.xml Deployment Descriptor Elements".*
When using this feature, you must be careful not to mix instances created from the Web application's class definition with issuances created from the server's definition. If such instances are mixed, a ClassCastException results.
Refer to the URL below
Oracle Weblogic Server
I have a custom URLClassLoader which loads a couple of classes from jar files outside the normal classpath. So far, so good.
My problem is that I can't call this ClassLoader each and every time when I need a class loaded by my ClassLoader because these classes might by used by third party libs. For that reason I added the classes to the current Thread ClassLoader by reflection:
ProtectionDomain pd = getClass().getProtectionDomain();
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final java.lang.reflect.Method clM = ClassLoader.class.getDeclaredMethod("defineClass", Class.class);
clM.invoke(classLoader, className, byteContent, 0, byteContent.length, pd);
But when it comes to a call to findClass inside that Thread ClassLoader and the requested class has not been added by me yet, I get a NoClassDefFoundException. Of course.
My question is now, is there a way to put my ClassLoader in the global class loading chain of my application? Using the Java option for system ClassLoader is not possible as my ClassLoader can be contained in a web application and deployed as a war file. I also tried using Thread.currentThread().setContextClassLoader() but once a new Thread is created, it of course doesn't have my ClassLoader set.
I searched the web for days but couldn't find any solution.
Thanks for your help and any suggestions.
Best regards,
Gerry
Not really, especially if you want this to be compatible with running as a web application.
You've got multiple problems to solve here. Web containers are permitted to run you under a security manager that will make your code not work (i.e., they can forbid access to the filesystem and/or forbid the creation of ClassLoaders). Java was designed to allow the safe loading of mobile code (it was originally targeting, of all things, cable set-top boxes), and so you can imagine the ability for an application to modify the system class loader on the fly was not part of the architecture.
For the application, what you ought to be doing is either (1) putting your JAR files from which you're specially loading these classes in the system classpath, or (2) loading your application in a custom class loader, then having that custom class loader load your classes (in essentially the same way you are now). Load the third-party libraries in the same class loader. Now you're going to be OK, regardless of what you mean by "might be used by third-party libraries" (this was confusing to me and I am not sure what technique they might be using, and it matters).
For the webapp, you should bundle the needed classes in the web application. You're defeating the web application design by attempting to load classes from elsewhere; the idea of the web application and .war is to make these things self-contained.
So you're doing this the wrong way. If you insist on doing it the wrong way, there may be various non-portable hacks you can start using in order to get your code working properly in particular situations, but I'm only going to write one answer for now and see what you think. :)
I'm doing some runtime bytecode manipulation on some of my business objects, and it's very important that they be loaded in the right order. Currently I'm simply calling Class.getSimpleName() on them in the right order in my Startup servlet. This has been working just fine, but if there's a better way, I'm all ears.
Now, however, I need a method in one of my servlet filters to return a concrete business object type. This is causing the classloader to load that particular business object class first (out of order) and things break.
What I'd like is to be able to run my getSimpleName() hack before any of my servlets or filters are loaded. Is there some place I can put code that runs before the classloader even loads my filters?
Yes, you can.
Look at ServletContextListener.
The tomcat class loader works in this fashion.
if you want to load any classes before any of you web-app classes load, then you can put those classes in a jar file and deploy it to tomcat common library, these classes will be loaded before your web application classes are loaded by the class loader.
You can check the documentation of how apache tomcat class loader works here
I have a problem concerning JBoss and class loading.
Here is the configuration I am working with. I have two instances of JBoss 4.2.3.GA on the same server. On each instance an application is running, and these applications are communicating with each other. There is an utility class, packed in both applications archives. This utility class is strictly the same in both applications.
This usually works fine, but in particular situations, I get ClassCastException. The case is the following:
A user is using a web application, which calls the application on the first JBoss instance (let's call it the application A). And application A calls the application B (on the second instance). This particular call takes several seconds to succeed.
If another user is trying to use the web application in a similar context (call to application A, which calls application B), and if this call happens during the first user call, I get systematically a ClassCastException : X cannot be cast to X (where X is my utility class, shared by both applications).
I found some information, and I deduced it was a class loading problem. Indeed, in this particular context of concurrent calls, my utility class is not loaded by the same class loader. I put a print command to see which class loader is used. In usual behavior, org.jboss.mx.loading.UnifiedClassLoader3 is used to load classes. In the particular described above, the application B seems to used a different class loader for the second user. My print command gave me the following:
WebappClassLoader
delegate: false
repositories:
/WEB-INF/classes/----------> Parent Classloader:java.net.FactoryURLClassLoader#de8209
My guess is that application B return an instance of my utility class loaded by this WebappClassLoader, and application A (which is using UnifiedClassLoader3) cannot cast it.
But i don't get why the UnifiedClassLoader3 cannot be used in this case, on application B. And why is this WebappClassLoader used ?
All I know about the class loading configuration in my JBoss instances is that class loading isolation is used, the following configuration is used for both applications :
<jboss-app>
<module-order>strict</module-order>
<loader-repository>applicationAorApplicationB.ear</loader-repository>
</jboss-app>
Do you have any advice to resolve this problem? How can I configure jboss class loader to avoid these class cast exception?
I precise that there is no hot deployment: I clean the server each time I deploy the applications.
If JBoss is using different classloaders for different packages, it is actually following the behavior according to the spec. For many releases it did not do this. You can disable WAR classloader isolation. See the JBoss documentation for more information.
You can also use a variety of different methods that do not require the applications to be in the same classloader for communication, and this would make your applications more specification compliant. See this stackoverflow question for more details.