United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4238086 : can't find resources in jar file when the jar is accessed via UNC path (win)

Details
Type:
Bug
Submit Date:
1999-05-13
Status:
Resolved
Updated Date:
2003-04-12
Project Name:
JDK
Resolved Date:
2002-08-30
Component:
core-libs
OS:
windows_nt
Sub-Component:
java.net
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.2.1
Fixed Versions:
1.4.2 (mantis)

Related Reports
Relates:

Sub Tasks

Description

Name: dbT83986			Date: 05/13/99


When trying to load an ImageIcon from an application jar
file that resides on an unmapped network directory, the image 
will not load.  If you map the path to a drive instead of using 
the UNC path, the image is able to be loaded.

Here is an example.  I've included several different ways
of trying to display the image.

import javax.swing.*;          
import java.awt.*;
import java.io.*;
import javax.swing.border.*;


public class BugDemo extends JFrame 
{

	public BugDemo()
	{
	}


	public Image getImageFromJAR(String fileName)
	{
		if( fileName == null ) return null;

        Image image = null;
        byte[] tn = null;
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        InputStream in = getClass().getResourceAsStream(fileName);

        try{
            int length = in.available();
            tn = new byte[length];
            in.read( tn);
            image = toolkit.createImage( tn);
        }
        catch(Exception exc){
			System.out.println( exc +" getting resource " +fileName );
            return null;
        }
        return image;
	}


    void method1()
    {
		JLabel label = new JLabel(new ImageIcon("images/Splash.GIF"));
		label.setBorder(new BevelBorder(BevelBorder.LOWERED));

		JOptionPane.showMessageDialog(this, label,
				"About", JOptionPane.INFORMATION_MESSAGE);
    }


    void method2()
    {
        ImageIcon image = 
			new ImageIcon(ClassLoader.getSystemResource("images/Splash.GIF")); 
		JLabel label = new JLabel(image);
		label.setBorder(new BevelBorder(BevelBorder.LOWERED));

		JOptionPane.showMessageDialog(this, label,
				"About2", JOptionPane.INFORMATION_MESSAGE);
    }


    void method3()
    {
        Image image = getImageFromJAR("images/Splash.GIF");
		JLabel label = new JLabel(new ImageIcon(image));
		label.setBorder(new BevelBorder(BevelBorder.LOWERED));

		JOptionPane.showMessageDialog(this, label,
				"About2", JOptionPane.INFORMATION_MESSAGE);
    }


    public static void main(String[] args)
    {
		BugDemo frame = new BugDemo();

		frame.method1();
		frame.method2();
		frame.method3();
		System.exit(0);

    }

}

method1 and method two do not return an image, while method
three results in a null pointer exception, when the application
is run from an unmapped directory.
(Review ID: 63144) 
======================================================================

                                    

Comments
WORK AROUND



Name: dbT83986			Date: 05/13/99


extracting the images from the jar or mapping the directory
to a drive letter seems to get around
the problem, but complicates distribution of applications
======================================================================

Specify the UNC path with foward slashes in the form:

  file:////servername/sharename/path/foo.jar

If we modify the test in the evalation, the test will properly locate 
the resource.

e.g. 
change:

  u[0] = new java.net.URL("file:///\\\\doze\\iris\\i.jar");

to:

  u[0] = new java.net.URL("file:////doze/iris/i.jar");

OUTPUT:
C:\tmp\iris>j:/jdk1.4.1/windows-i586/bin/java -version
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b17)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b17, mixed mode)

C:\tmp\iris>j:/jdk1.4.1/windows-i586/bin/java URLTest
file://doze/iris/i.jar
opened stream with 8225 bytes available
file://doze/iris/i.jar
opened stream with 7833 bytes available

Note that

  u[0] = new java.net.URL("file:////\\\\doze\\iris\\i.jar");

also works.

-- iag@sfbay 2002-07-17

                                     
2002-07-17
SUGGESTED FIX

This fix is not ideal.  See the evaluation section for more details.

*** /tmp/geta15064	Wed Jul 17 17:29:07 2002
--- JarURLConnection.java	Wed Jul 17 17:29:06 2002
***************
*** 154,160 ****
  	    throw new MalformedURLException("no ! found in url spec:" + spec);
  	}
  
! 	jarFileURL = new URL(spec.substring(0, separator++));
  	entryName = null;
  
  	/* if ! is the last letter of the innerURL, entryName is null */
--- 154,163 ----
  	    throw new MalformedURLException("no ! found in url spec:" + spec);
  	}
  
! 	int csep = spec.indexOf(':');
! 	jarFileURL = new URL(spec.substring(0, csep),
! 			     url.getHost(), 
!  			     spec.substring(csep + 1, separator++));
  	entryName = null;
  
  	/* if ! is the last letter of the innerURL, entryName is null */

-- iag@sfbay 2002-07-17
                                     
2002-07-17
EVALUATION

I am unable to further evaluate this bug without additional information.

Using the provided test case, I have been able to reproduce the
described problem in jdk1.2.2; however, I have been unable to
reproduce the problem in any jdk since that time.  The last version
tested was jdk1.4.1-rc-b16.

Can this problem be reproduced using the latest jdk under development?
Does this test still reproduce the problem and fail as described?  On
what type of system is the network directory (e.g. Solaris, winNT,
etc.)?  Is the network directory viewable via the UNC path outside of
the java application?  Please provide the complete command-line used to 
launch the application/test.

Windows may cache network authentication to a network directory which
has been previously mounted.  To be absolutely certain that you do not 
retain authorization to that directory after it has been unmounted, you
should log off and re-log in.

-- iag@sfbay 2002-07-14

I've reproduced the problem.  The customer has provided another test and 
additional details which have allowed me to easily display the error.

FILE: URLTest.java
public class URLTest {
    public static void main(String[] args) {
	try {
	    java.net.URL[] u = new java.net.URL[1];
 	    u[0] = new java.net.URL("file:///\\\\doze\\iris\\i.jar");
	    System.out.println(u[0]);

	    java.io.InputStream st1 = u[0].openStream();
	    System.out.println("opened stream with " + st1.available() + " bytes available");

	    String path = u[0].getFile().replace('/', java.io.File.separatorChar);
	    java.io.File file = new java.io.File(path);
	    if (!file.exists()) {
		throw new java.io.FileNotFoundException(path);
	    }
	    java.net.URLClassLoader scl = new java.net.URLClassLoader(u);
 	    java.io.InputStream st = scl.getResourceAsStream("images/Splash.GIF");
	    System.out.println("opened stream with " + st.available() + " bytes available");
	} catch(Exception e){
	    e.printStackTrace();
	}
    }
}

URLTest.class resides on a machine running windows.  i.jar contains a single
file images/SPLASH.gif.  The .jar file is on another windows machine and is
accessible via the UNC path "\\doze\iris\i.jar".

OUTPUT:
C:\tmp\iris>j:/jdk1.4.1/windows-i586/bin/java -version
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b17)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b17, mixed mode)

C:\tmp\iris>j:/jdk1.4.1/windows-i586/bin/java URLTest
file:///doze/iris/i.jar
opened stream with 8225 bytes available
file:///doze/iris/i.jar
java.lang.NullPointerException
        at URLTest.main(URLTest.java:29)


The NPE occurs because the call to ClassLoader.getResourceAsStream has masked
an IOException and returned null.  If we modify the JDK, we get the following
stack trace:

C:\tmp\iris>d:/tmp/iris/tl/build/windows-i586/bin/java URLTest
file:///doze/iris/i.jar
opened stream with 8225 bytes available
file:///doze/iris/i.jar
java.util.zip.ZipException: The system cannot find the path specified
        at java.util.zip.ZipFile.open(Native Method)
        at java.util.zip.ZipFile.<init>(ZipFile.java:112)
        at java.util.jar.JarFile.<init>(JarFile.java:117)
        at java.util.jar.JarFile.<init>(JarFile.java:55)
        at sun.net.www.protocol.jar.URLJarFile.<init>(URLJarFile.java:55)
        at sun.net.www.protocol.jar.URLJarFile.getJarFile(URLJarFile.java:40)
        at sun.net.www.protocol.jar.JarFileFactory.get(JarFileFactory.java:63)
        at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:85)
        at sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:105)
        at java.net.URL.openStream(URL.java:961)
        at java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:943)
        at URLTest.main(URLTest.java:24)
java.lang.NullPointerException
        at URLTest.main(URLTest.java:25)

ZipFile.open is being called with "\doze\iris\i.jar" which is interpreted as
an absolute path on the local machine's current drive, not the expected UNC
path.

This is derived from the url "file:/doze/iris/i.jar" which represents an
absolute path on the local machine (see rfc 2398). 

It appears that the the location of the jar file is stored in a private field
in java.net.JarURLConnection.jarFileURL.  The jarFileURL field is set by the
constructor.  The JarURLConnnection constructor's single parameter is a jar
url which represents the resource we wish to locate.  In our case the url is:

  "jar:file:///doze/iris/i.jar!/images/Splash.GIF"

The constructor calls the method parseSpecs which takes this url, determines
the location of the jar file, and stores the location in the jarFileURL field
using the following code fragment:

    private void parseSpecs(URL url) throws MalformedURLException {
	String spec = url.getFile();

	int separator = spec.indexOf('!');
	/*
	 * REMIND: we don't handle nested JAR URLs
	 */
	if (separator == -1) {
	    throw new MalformedURLException("no ! found in url spec:" + spec);
	}

   	jarFileURL = new URL(spec.substring(0, separator++));

The single argument URL(String) constructor is called with

  "file:///doze/iris/i.jar"

thus, jarFileURL is set to "file:/doze/iris/i.jar".  As mentioned above, a 
technically valid url, but certainly not the expected one.

Note that it is actually surprising that a url of "file:///doze/iris/i.jar" 
is recognized as a UNC path.  This behaviour was introduced in jdk1.3 as a
result of the fix for 4180841.  


There are a couple of different solutions we should consider.  The first
question is why does URL("file:///\\\\doze\\iris\\i.jar") return
"file:///doze/iris/i.jar"?  Though I have been unable to find conclusive
evidence to support this, ###@###.### believes that the leading backslashes 
should probably have never been removed.  If this is the case, then this 
would be the ideal fix.  

Unfortunately, a change of this magnitude would have a huge impact on the
stability of UNC support.  It is highly unlikely that all other parts of the
JDK will properly identify this new format as a UNC path. 

The most obvious solution is to take advantage of the existing fix for 4180841.
This may be accomplished by using a different URL constructor in
JarURLConnection.parseSpecs.  If we instead use URL(String protocol, String
host, String file), the test passes.  Though this does solve the reported
problem, there are a couple of issues.  First, this fix will apply to all
platforms, not simply those which support UNC paths.  This problem is easily
solved.  Next, we may be unable to conclusively distinguish when a url path
should be interpreted as a UNC path verses an absolute path on the current
disk.  One possible solution for this problem is to attempt to find the jar
file locally.  If that fails, we could attempt to interpret the url as a UNC
path.  The correct path interpretation order is unclear.

Though more conservative than the previous suggestion, I believe that this
fix is also potentially unstable.

I strongly recommend that this bug _not_ be addressed in hopper.  A more
appropriate release would be mantis, when we can further investigate other
possible solutions and their potential impact.

-- iag@sfbay 2002-07-17


------


It's important to note that URLs that have UNC paths in the URL path
are working as expected. For example the following work as expected with 
1.4.1 and previous releases :-

file:////server/dir/images.jar
file://\\\\server\\dir\\images.jar

Both cases correspond to \\server\dir\images.jar. URLConnection can open a 
connection to this file, and URLClassLoader can obtain a resource from the jar 
file.


We do have a problem if the NetBIOS peer name is put in the host component,
eg: -

file://server/dir/images.jar

URLClassPath will treat this as \dir\images.jar as the underlying JarLoader
in sun.misc.URLClassPath is not UNC aware.

As it happens URL's file protocol handler is broken in this case aswell as
the UNC code broken in 1.4.0. This was noticed too later for 1.4.1 but will
be fixed in mantis (1.4.2) - see bug 4671171. 


Going further, the URL that the customer is using is similiar to the 
following :-

file:///\\\\doze\\iris\\i.jar

Although technically not a valid URI (as the \ characters should be escaped) it
is parsed by URL and with escaping removed we are working with :-

file:///\\doze\iris\i.jar

Essenitally this means we are trying to open the file \\\doze\iris\i.jar
which isn't a valid UNC path. 

As regards why java.io.File handles this and URLClassLoader's
getResourcesAsStream doesn't - this stems from the normalization done
by java.io.File. It normalizes the path to \\doze\iris\i.jar and thus 
opens the file. 

###@###.### 2002-07-24

We will fix this in mantis (in conjunction with 4671171) so that UNCs
are handled in file: URLs where the UNC is entirely contained in the
URL path or alternatively, the standard form where the hostname from
the UNC goes into the authority field of the URL.
However, we will not support urls like
file:///\\doze\iris\i.jar because they evaluate to invalid UNCs
(in this case \\\doze\iris\i.jar). The nearest correct equivalent
for this url would be file://\\doze\iris\i.jar.
Strictly, the \ character should not be used unescaped in URLs (and is not 
supported by the URI class). However, all of the browsers support them
on Windows and we should also continue to do so.

###@###.### 2002-07-29
                                     
2002-07-29
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
mantis
mantis-b02

FIXED IN:
mantis
mantis-b02

INTEGRATED IN:
mantis
mantis-b02


                                     
2004-06-14



Hardware and Software, Engineered to Work Together