JDK-6207022 : JarURLConnection throws FileNotFoundException in Linux when jar is modified
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.jar
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: linux
  • CPU: x86
  • Submitted: 2004-12-10
  • Updated: 2011-02-16
  • Resolved: 2008-04-03
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
jdk 1.4.2, 1.4.1 and alos latest 1.5.0 beta 3

ADDITIONAL OS VERSION INFORMATION :
Linux 2.4.27 and also on linux 2.6.7

EXTRA RELEVANT SYSTEM CONFIGURATION :
Not linked to a specific hardware/software configuration

A DESCRIPTION OF THE PROBLEM :
So here is the problem:
If I use a JarInputStream to open a jar, and creates URL from entry of the jar file like this :

URL u = new URL("jar:"jar.toURL()+"!/"+entry.getName());

Everything is fine, I can open a stream with the URL on the resource like this :

u.openStream().

But If I ever change the jar file (example, adding a file in it)... Then If I reopen a JarInputStream on it and recreate the same way an URL, when doing openStream it throws a FileNotFoundException.

If you just replace the jar with the same jar not modified (example by cp the jar to it), the exception does not occurs, it occurs simply if the jar file has been modified.

The strangest thing is that if I use a ZipInputStream instead of a JarInputStream, there is no problem.

Quentin Anciaux


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I provide a code snippet to reproduce the bug. Just put a jar file called "test.jar and run the class by java -cp ./ bug.testJarInputStream

It will list the content and tell if it succeed to open a stream. Then it will wait to press a key. At this time replace the "test.jar" with a modified version (example add a file in it) then press the key.. You'll have a fileNotFoundException for every entry in the jar.

Now replace the JarInputStream and JarEntry of the code snippet by ZipInputStream and ZipEntry.

Do the same, and you'll have no Exception.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No Exception like in the ZipInputStream
ACTUAL -
FileNotFoundException

ERROR MESSAGES/STACK TRACES THAT OCCUR :
ava.io.FileNotFoundException: JAR entry org/objectstyle/cayenne/project/validator/ObjRelationshipValidator.class not found in /home/qan/workspace/testJarInputStream/bin/test.jar
        at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:97)
        at sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:107)
        at java.net.URL.openStream(URL.java:1007)
        at bug.testJarInputStream.loadJarAndTestContent(testJarInputStream.java:66)
        at bug.testJarInputStream.main(testJarInputStream.java:37)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Created on 28-aot-2004
 */
package bug;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

/**
 * @author Quentin Anciaux
 */
public class testJarInputStream
{
 public testJarInputStream()
 {
  
 }
 
 public static void main(String [] args)
 {
  testJarInputStream handler = new testJarInputStream();
  try
  {
   handler.loadJarAndTestContent("test.jar");
   handler.awaitUserInteraction();
   handler.loadJarAndTestContent("test.jar");
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
 }
 
 private void loadJarAndTestContent(
  String pathToJarFile)
 throws IOException
 {
  JarInputStream stream = null;
  try
  {
   File jar = new File(pathToJarFile);
   stream = new JarInputStream(new FileInputStream(pathToJarFile));
   JarEntry entry = null;
   while ((entry = stream.getNextJarEntry())!=null)
   {
    StringBuffer strURL = new StringBuffer();
    strURL.append("jar:");
    strURL.append(jar.toURL());
    strURL.append("!/");
    strURL.append(entry.getName());
          URL urlToEntry = new URL(strURL.toString());
    InputStream in = null;
    try
    {
     in = urlToEntry.openStream();
     System.out.println("entry URL "+urlToEntry+" is ok !");
    }
    catch (Exception bug)
    {
     bug.printStackTrace();
    }
    finally
    {
     if (in != null) in.close();
    }
   }
  }
  catch (IOException e)
  {
   throw e;
  }
  finally
  {
   if (stream != null) stream.close();
  }
 }
 
 private void awaitUserInteraction()
 {
  try
  {
   InputStream in = System.in;
   System.out.println("Change the jar file and press a key.");
   in.read();
  }
  catch (IOException e)
  {
  }
 }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use ZipInputStream instead of JarInputStream
###@###.### 2004-12-10 10:46:21 GMT

Comments
EVALUATION The root cause is that there are two files in the test with the same name, and that the first one of them is cached by name via JarURLConnection. The "modified file" is, insofar as the OS is concerned, a new file. So there are really two files, a first one and a second one, both with the same name. The first is opened via an input stream. For each entry, a JarURLConnection is opened. The input stream is closed. The file is modified, and the process repeated. The input stream will, correctly, get the entries of the modified file. However, because the filename is the same, and JarURLConnection cached the file, when attempting to access the entries in the modified file, some of them are not found, because the cached object is the first, unmodified file. The solution is to turn off the caching: instead of is = u.openStream() use URLConnection uc = u.openConnection(); uc.setUseCaches(false); is = uc.getInputStream(); The attached testcase shows how this can be done. Please note that my investigation shows that the same problem occurs with both JAR and ZIP files. I am closing this bug because an easy workaround exists.
03-04-2008

WORK AROUND See evaluation and attached TestCase.java.
03-04-2008

EVALUATION In general, the JDK does not support replacement of jar files at runtime. On windows, the operating system itself prevents it, on Unix bad things can happen. It is in general always a good idea to *remove* the old file before replacing it, and this operation can be done almost atomically. In this case, there is a very good chance the application will operate reasonably.
10-08-2005