简体   繁体   中英

Failed to load resources in jar file when jar file is replaced

I meet java.util.zip.ZipException: invalid stored block lengths.

The stack trace is as following:

Caused by: java.util.zip.ZipException: invalid stored block lengths
at java.util.zip.InflaterInputStream.read(Unknown Source)
at java.io.FilterInputStream.read(Unknown Source)
at java.io.FilterInputStream.read(Unknown Source)
at java.util.Properties$LineReader.readLine(Unknown Source)
at java.util.Properties.load0(Unknown Source)
at java.util.Properties.load(Unknown Source)

This happens when my project try to upgrade. The upgrade logic is to replace old jar files with new ones, and JVM is still running.

There is jar file(jarA.jar) includes a property file, and property file records some full class names. These class names will be used to create instance by reflection. The upgrade logic try to load the property file with SystemClassLoader.getResourceAsStream().

If the jar file(jarA.jar) is replaced with new one, and the content of property is changed, this Exception will happen. Seems SystemClassLoader can not load the property correctly.

The project is compiled by java1.4, and running on jre1.7, Os is Windows.

Does any one can explain why SystemClassLoader failed to load while the property exists? I appreciate your help.

Usually classes are read from jar files only when it is first time required.

When you replace the jar files when JVM is running, the file InputStream the JVM was having has become obsolete. In this case, if the content of the JAR file is not changed, then the classes can be read from the jar. If the replaced jar has different content than the old jar, then the obsolte InputStream will try to read in the position that is known to it. But, since the conent has changed, it may not be able to read the file.

Hence this exception

Class reloading is usually done by throwing away the old classloader and creating a new one for the updated versions of the class. As far as I know there is no way to reload a changed class within the same classloader.

Reloading of a property file is another problem. But then it must not be included in the jar and you will have to monitor changes of the file. You shouldn't use getResourceAsStream() in that case, also.

If you want to use getResourceAsStream(String) after replacing your jar files, you just have to implement your own method ClassLoader.findResource(String)

public URL findResource(final String name) {
    // find URL (and cache it if necessary (Hashtable ?))
    // do not call super.findResource(String)
    return url; // in a jar: new URL("jar:jar-path!/" + name);
}

It will assure a proper reloading of any resource (even if in a jar).

(I'm still not sure exactly, but the default classloader appears to store a connection to the first found URL, which invalidates after replacing the source, but remains unchanged in the classloader in charge of your classes)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM