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
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.