JDK-4388666 : New URLClassLoader continues using classes from old jar.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 1.3.0,1.3.1_01
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic,solaris_8
  • CPU: generic,sparc
  • Submitted: 2000-11-13
  • Updated: 2001-11-26
  • Resolved: 2001-11-26
Related Reports
Duplicate :  
Description

Name: yyT116575			Date: 11/13/2000


Linux:
java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0)
Java HotSpot(TM) Client VM (build 1.3.0, mixed mode)

AND

Linux:
java version "1.2.2"
Classic VM (build 1.2.2_006, green threads, nojit)

AND

Windows98:
java version "1.2"
Classic VM (build JDK-1.2-V, native threads)


I'm trying do dynamic updates of classes using an explicit URLClassLoader, but
if I put the classes in a jar file, the updates aren't picked up correctly.  If
I use a directory instead it works fine.  Using the Unix tool fuser I can see
that the JVM keeps the jar file open.  This is not the behavior I expect when
I create a URLClassLoader.


SOURCE:

// Interface for changable implementation.
public interface cInterface {
    public void doStuff();
}

// Implementation
public class cImpl implements cInterface {
    public void doStuff() {
        System.out.println("\t\tdoStuff!");
        //doMoreStuff();
        //doYetMoreStuff();
    }
    /*
    public void doMoreStuff() {
        System.out.println("\t\tdoMoreStuff!");
    }
    public void doYetMoreStuff() {
        System.out.println("\t\tdoYetMoreStuff!");
    }
    */
}

// Client program
import java.io.*;
import java.net.*;

public class jarTest {
    public static void main( String[] argv ) throws Exception {
        URL[] auJars;
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s;

        while(true) {
            System.out.println("Hit enter.");
            s = br.readLine();

            try {
                auJars = new URL[1];
                auJars[0]=new URL("file:test.jar");
                            
                ClassLoader cl = new java.net.URLClassLoader(auJars);
                System.out.println("new Classloader:"+cl);

                cInterface ci = (cInterface)
                cl.loadClass("cImpl").newInstance();
                ci.doStuff();
            } catch( Exception e ) {
                e.printStackTrace();
            }
        }
    }
}

Procedure to reproduce:
$ javac *.java
$ jar cvf test.jar *.class
$ rm cImpl.class
$ java jarTest
Hit enter.

new Classloader:java.net.URLClassLoader@c3579d5b
                doStuff!
Hit enter.

Open another window and perform the following steps
in the same directory:
1.uncomment other functions in cImpl.java
2.$ javac cImpl.java ; jar cvf test.jar *.class; rm cImpl.class 

Switch back to the original window without restarting
jarTest and hit enter, a exception thrown:

new Classloader:java.net.URLClassLoader@caef9d5b
Exception in thread "main" java.lang.ClassFormatError: cImpl (Truncated class file)
        at java.lang.ClassLoader.defineClass0(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:474)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:106)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:247)
        at java.net.URLClassLoader.access$1(URLClassLoader.java:215)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:196)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:295)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
        at jarTest.main(jarTest.java:26)

The JDC Tech Tip:

http://developer.java.sun.com/developer/TechTips/2000/tt1027.html

...shows the general dynamic class update method.  What I found is simply
that it doesn't work with jars.

Jim

(Review ID: 112059) 
======================================================================

Comments
WORK AROUND Name: yyT116575 Date: 11/13/2000 Use directories to store dynamically updatable class rather than jars. ====================================================================== setUseCaches(false) on JarURLConnection before calling getJarFile()
11-06-2004

EVALUATION A fix would have to be made to sun.misc.URLClassPath. So I'll transfer this bug to classes_net. Evaluation and investigation by ###@###.### The function private JarFile getJarFile(URL url) uses JarURLConnection's getJarFile() method to retrieve jar files. By default JarURLConnection caches JarFiles and always returns a cached copy. Even when a file is updated on disk, the cached copy is returned and changes are not seen. To avoid caching, one should call setUseCaches(false) on JarURLConnection before calling getJarFile(). ###@###.### 2001-10-23 ---------------------------------------------------------------- The problem is a little more complex, because in this situation Jar file caching at the URLConnection level is not happening. The test app is creating separate URLClassLoaders and this results in new JarFile objects being created for each class loader instance. These JarFile instances are not closed because the classloader could be called at any future time to load a new class from the same jar file. This particular problem happens because ZipFile (extended to JarFile) does not always open a new (system level) file each time a ZipFile instance is created. The Zipfile native code keeps a reference count for each currently open file and works off the same native file handle for concurrent accesses to the same file. This means that if the underlying file is modified while a ZipFile instance (for that file) is in existence, the updated file may be read but the cached information such as the file length is no longer valid. I believe this is a bug in ZipFile, which you can show easily enough. If you open a ZipFile, read its contents, modify it externally, reopen it and read its contents again (in the same VM) then you'll get a variety of errors caused by this caching behavior. I would suggest that ZipFile keeps in its cache the modification date of the file and it checks this information each time open is called against the file. If the mod date is different, then the file should be opened again. I'm going to assign to classes_util so they can check this evaluation. ###@###.### 2001-11-22 We have now fixed ZipFile, so it keeps track of the modification time and reloads zip file if necessary. I am closing this as a duplicate of 4353705 ###@###.### 2001-11-26
26-11-2001