Search code examples
javajbosswildflyinstrumentation

VerifyError when instrumenting webapp on WildFly 8


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?


Solution

  • 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.