views:

519

answers:

3

I have a java agent which instruments bytecode. I am using the attach apis in java 6 to allow users to dynamically load the agent and instrument and deinstrument code using my java agent. I am using the Boot-Class-Path manifest attribute to make sure my javagent classes are in the boot classpath so that my users can instrument classes like ArrayList, etc.

However the problem comes with versioning. Lets say a user dynamically attaches version 1 of my agent. Then I given him version 2. Now since his app server never shut down since he attached version 1 of my agent, the version 1 classes are still loaded.

I need some way such that when my client version 2 of the javaagent, the version 1 is unloaded.

I know one way would be to write a customer classloader for my javaagent's classes, and set the classloader reference to null. However in that case I wont be able to instrument classes in the boot classpath since my classloader will be below in the hierarchy from the boot classloader and thus my users cant instrument classes like ArrayList because if I add a call inside ArrayList's methods to one of my agent's classes' methods the boot class loader wont be able to see them.

So is there any way to solve the boot classpath issue and still unload the previous agent's classes?

+1  A: 

I'm not an expert on this topic, but it seems like this kind of unload-to-replace is not directly supported.

But do you need to unload-replace the class?

Could you instead create a never-changing class that the external world talks to, in which you internally imlpement a versioning system?

For example, you create class MyToolAgent that has, say, a static string with the classname of the ToolAgentImplementation to use. When you first release, it's set to use ToolAgentImplementation1_0. When you upgrade to version 2.0, you deploy an additional class called ToolAgentImplmenetation2_0, and update the MyToolAgent class to load and use it. You never unload version 1.0, but you do stop using it. You do waste some memory here, but you achieve the version upgrade.

I don't know if this is feasible in your situation, but in general it seems the JVM doesn't support directly swapping in a new version, but that you should be able to hide that in some way.

rice
A: 

I'm not sure this will work, but it may give you some options...

Instead of trying to reload the current agent (let's call this the tool agent), add a new agent (an installer agent). The installer agent would have exactly 1 function: use RedefineClasses() to replace the original tool agent classes.

If you name the installer agent with a version number as part of the classname (MyToolInstallerV1), you can keep loading new installers that update the tool agent. If size becomes an issue, perhaps an installer agent could look for previous installers and replace their classes with a small no-op stub.

Devon_C_Miller
A: 

Hi,

I haven't instrumented anything by now but I'm just playing with java.util.ServiceLoader and implemented some kind of plugin architecture, with dynamic JAR load-unload with your URLClassLoader's approach.

I don't know the "instrumentation" could be altered this way but the versioning and the dynamic load-unload problems can be tracked down, and it's really fast to test it because the hard stuff is done by the ServiceLoader (just follow the tiny /META-INF/services/XXX specification) and you are ready :)

Regards.

ATorras