Search code examples
javajvmclassloaderjavassist

JVM locks JAR files on startup


I've implemented a custom ClassLoader which is set via system property -Djava.system.class.loader=com.MyClassLoader. The CL contains a static initializer that invokes some code (before any class is loaded) manipulating the byte code of a class file within a jar (maven dependency) using the javassist library. This works fine, except that i cannot replace the old jar with the new one since the JVM is locking the file and only releases it when it terminates. Why is that and how can I enforce the JVM to release the lock?

Here is a little code snippet:

public class CustomClassLoader extends ClassLoader {

static {
   ...
   modifyJar();
}

private static void modifyJar(){        
   URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
    URL[] urls = urlClassLoader.getURLs();
    for(URL url : urls) {
      //find matching jar and modify byte code
    }        
    replaceJarFile(metaData);
}

private static void replaceJarFile(JarMetaData jmd){
     //add modified class to new jar file
     JarFile jar = new JarFile(jmd.getJarFile());
     ...

    //this method call returns false, jar is locked by another process (the JVM)
    if(oldJarFile.delete()){
       ...
    }  
  }
}

OS: Windows 10

JDK version: 1.8.0_131


Solution

  • It has never been specified that changing the jars in use by a JVM should be possible. And the JVM is locking the jars for quiet a long time now. Changing the classes within a jar in use would also bear the semantic problem of how to handle modifications to already loaded classes or even an ongoing class loading overlapping with a write.

    Since modifying the jar would be a permanent change, the most reasonable approach for that would be doing it before starting the JVM, e.g. in a different JVM.

    But if you want to change class definitions on-the-fly, you should write a Java Agent. For those JVMs supporting Java Agents, the Instrumentation API offers everything needed, e.g. transforming classes at load time or even redefining already loaded classes.
    It also offers a standard way of adding jar files to the bootstrap or system class path, whereas assuming that the application class loader is a subclass of URLClassLoader will start to fail with Java 9.