javajvmjmx

How can I dynamically set a System property on the JVM, after it has started, without modifying my program's code?


I would like to be able to change a system property while a java application is running inside an application server. I know that this can be done with java.lang.System.setProperty or by adding the -DpropertyName=value as a JVM flag.

But I want to do this at runtime and without modifying the application code.

Is there a tool that can change system properties in the JVM at runtime without having to update the application-code ( e.g. just by attaching a tool the running process or by using JMX ) ?


Solution

  • The idea is to attach an agent that calls System.setProperty to a running application.
    Note: this does not require modifying application code.

    Here is the agent code:

    import com.sun.tools.attach.VirtualMachine;
    
    public class SetProperty {
    
        public static void main(String[] args) throws Exception {
            String url = SetProperty.class.getProtectionDomain().getCodeSource().getLocation().toString();
            if (args.length < 2 || !url.startsWith("file:")) {
                System.out.println("Usage: java -jar setproperty.jar pid property=value");
                System.exit(1);
            }
    
            VirtualMachine vm = VirtualMachine.attach(args[0]);
            try {
                int startIndex = "\\".equals(System.getProperty("file.separator")) ? 6 : 5;
                vm.loadAgent(url.substring(startIndex), args[1]);
            } finally {
                vm.detach();
            }
        }
    
        public static void agentmain(String args) {
            int eq = args.indexOf('=');
            System.setProperty(args.substring(0, eq), args.substring(eq + 1));
        }
    }
    
    1. Compile the source code:
    javac -source 8 -target 8 SetProperty.java
    
    1. Create MANIFEST.MF file with the following contents:
    Main-Class: SetProperty
    Agent-Class: SetProperty
    
    1. Build .jar file from the compiled sources and the manifest:
    jar cfm setproperty.jar MANIFEST.MF SetProperty.class 
    
    1. Launch the agent passing the target process ID and the property to set (JDK 9+):
    java -jar setproperty.jar 1234 MyProperty=SomeValue
    

    If you are using JDK 8, you'll need to add tools.jar to the class path:

    java -cp setproperty.jar:$JAVA_HOME/lib/tools.jar SetProperty 1234 MyProperty=SomeValue
    

    I prepared setproperty.jar so you can skip steps 1-3 and proceed directly to the final command.