4

I'm trying to access classes from the servlet-api within a javaagent jar that is added to my application via the -javaagent:my-agent.jar flag. My application runs on Tomcat. The problem is that I get a ClassNotFoundException, because the agent jar is loaded by a classloader that has no access to the servlet-api classes.

More specifically, I want to include a ServletContainerInitializer in my javaagent

public class MyInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        System.out.println(ctx);
    }
}

META-INF/services/javax.servlet.ServletContainerInitializer

org.example.MyInitializer

The result is a ClassNotFoundException because ServletContainerInitializer could not be found.

Is there any way to access the servlet-api within a javaagent? Or more generally, is it possible to access any class that is loaded via the ApplicationClassLoader, like classes from the spring framework?

Felix
  • 5,804
  • 4
  • 25
  • 37

2 Answers2

1

If your agent has a premain method with a signature like

public static void premain(String agentArgs, Instrumentation inst)

You get hands on an Instrumentation object which provides the methods getAllLoadedClasses() and getInitiatedClasses(ClassLoader). Now it would be awkward to search these arrays each time and use the Class instances via Reflection only. But you can use these methods to find the desired API class, determine its runtime ClassLoader and create a new ClassLoader using it as a resolve parent and pointing to your agent classes which require them.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • How would that "pointing to my agent classes" look like? Another problem is that the MyInitializer class is loaded by the Application Server on startup so it is even more difficult to mess with the class loaders there. – Felix Jul 03 '15 at 13:12
  • 1
    E.g., you place all classes related to `MyInitializer` into a jar file and create a `URLClassLoader` using a `file:` URL pointing to the jar file. Probably, your agent has to postpone the initialization until the servlet container is fully initialized. I don’t know enough about your use case to tell you which condition to wait for. But note that you can use `addTransformer` to register a transformer which simply returns `null` to make no actual transformation. Since that transformer will be invoked for each loaded class, you learn about when your desired class gets loaded by tomcat. – Holger Jul 03 '15 at 13:21
0

What worked for me was I added servlet-api.jar file in the ClassPool.insertClassPath. And that resolved the issue.

    ClassPool $pool = ClassPool.getDefault();
            try {
                $pool.insertClassPath("/apache-tomcat-7.0.50/lib/servlet-api.jar");
            } catch (NotFoundException e) {
                throw new RuntimeException(e);
            }
    return $pool;

https://github.com/presci/MyAgent.git

presci
  • 353
  • 1
  • 2
  • 12