Alter value of a static field during class loading using a Java agent

1.7k views Asked by At

We have a java process that calls some method of class X. Class X has timeout static field which decides how long thread should wait for in case of some error. Now, I want to change that value without changing my java process (I don't want deployment, and this change is experimental). How can I use java agent to change this timeout value to say 1 minute (1*60*1000)

Class X {
    ....
    // timeout = 5 minutes
    private static long timeout = 5*60*1000;
    ....
}

In short, how to write java agent to change value of a static variable. I have went through some tutorials, but none of those explain how to do this. I do not have access to main method. The project is run by an IOC container.

Thanks,

Rishi

1

There are 1 answers

4
Rafael Winterhalter On BEST ANSWER

Using reflection, you can implement this quite easily:

class EasyFieldAlterationAgent {
  public static void premain(String args) throws Exception {
    Field field = X.class.getDeclaredField("timeout");
    field.setAccessible(true);
    field.setValue(null, 42L); // set your value here.
  }
}

Note that this will change the field not before but right after class loading time. If this is not possible for you to work with, you might also just redefine the class itself what I would only recommend if:

  1. Your security setting does not allow you to use reflection.
  2. You need to change the value befor the class's type initializer is executed.

If you want to really change the field before loading the class, you are lucky enough that you want to change the value of a field that is both static and that defines a primitive value. Such fields store their values directly at the site of the field. Using an agent, you can define a ClassFileTransformer that simply alters the value of the field on the fly. ASM is a good tool for implementing such a simple transformation. Using this tool, you could implement your agent approximately as follows:

class FieldAlterationAgent {
  public static void premain(String args, Instrumentation inst) {
    instrumentation.addTransformer(new ClassFileTransformer() {
      @Override
      public void byte[] transform(ClassLoader loader,
                                   String className,
                                   Class<?> classBeingRedefined,
                                   ProtectionDomain protectionDomain,
                                   byte[] classfileBuffer)
          throws IllegalClassFormatException {
        if (!className.equals("X") {
          return classFileBuffer;
        }
        ClassWriter classWriter = new ClassWriter(new ClassVisitor() {
          @Override
          public FieldVisitor visitField(int access, 
                                         String name, 
                                         String desc, 
                                         String signature, 
                                         Object value) {
            if(name.equals("timeout") {
              value = 42L; // set value here, make sure its a long!
            }
            return super.visitField(access, name, desc, signature, value);
          }
        }, 0);
        new ClassReader(classFileBuffer).accept(classWriter);
        return classWriter.toByteArray();
      }
    });
  }
}

You can tell that this latter version requires a lot more code and requires your agent to be packed together with its ASM dependency.

To apply the agent, put either class in a jar file and put it onto the agent path.