Is there any solution about Load Time Bytecode Transformation without specify javaagent option in java command line

StackOverflow https://stackoverflow.com/questions/20244885

Question

As an Open Source Lifecycle Framework API provider, I want to try my best to hiding internal designs with an implicit manner to Provide Lifecycle API, so that will bring much more convenience to API Client.

It is expected to avoid doing configuration for both Core Java Applications and Java EE Applications, but the reality is that I am using java command -javaagent:${path}/Lifecycle.jar option to enable my own ClassFileTransformer at class load time.

After some searches, some unclear directions were found. I need some Java Guy to summarize and guide us.

  1. agentmain vs premain
  2. intergration with specified runtime environment, such as Glassfish's ByteCodePreprocessor, which has the following method to perform byte code transformation:

    public byte[] preprocess(String classname, byte[] classBytes);

My confusions about those directions:

  1. For Core Java Application, it seems that we can modify startup class' main method to adapt agentmain solution. Is there some other options?
  2. For using JavaEE Container, such as Glassfish, I can use ByteCodePreprocessor to modify class byte code, but I need to create some new classes, but I don't know where to store those new class files, or how to design or apply a new ClassLoader to load the newly created class files during preprocessing a class file.

(BTW Lifecycle API will follow a meta-driven style, which is very close with JPA without EntityManager interface, and most of them is just Annotations and CallbackContext interface and LifecycleEvent interface for now.)

Was it helpful?

Solution

Well, the only other method I could think of would be using a custom class loader which you could register at run time. This is how frameworks like Powermock do their heavy lifting. However, this requires some setup as well, but it can be done programatically.

As long as your framework has well defined entry points and as long as all code is run from within your application, you could apply a custom class loader which could instrument all loaded classes.

However, this will not work for classes already loaded. (You could break the parent first patern, but this might raise ClassCastExceptions on instances from outside your framework.)

Avoiding this would require you to override the system class loader which is equally verbose. For the sake of completeness, here is an excerpt of the javadoc of ClassLoader.getSystemClassLoader:

If the system property "java.system.class.loader" is defined when this method is first invoked then the value of that property is taken to be the name of a class that will be returned as the system class loader. The class is loaded using the default system class loader and must define a public constructor that takes a single parameter of type ClassLoader which is used as the delegation parent. An instance is then created using this constructor with the default system class loader as the parameter. The resulting class loader is defined to be the system class loader.

In this custom class loader, you could just always return instrumented classes.

The difference between agentmain and premain is that the former is invoked when you attach an agent to a running JVM (via the attach API) while the latter is invoked if the agent is specified on the command line at the JVM's startup. Registering an agent at runtime might actually be a solution for you. The blog entry that I linked offers a quite good description of that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top