JDK-4238086 : can't find resources in jar file when the jar is accessed via UNC path (win)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.2.1
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_nt
  • CPU: x86
  • Submitted: 1999-05-13
  • Updated: 2003-04-12
  • Resolved: 2002-08-30
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.2 mantisFixed
Related Reports
Relates :  
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
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mantis mantis-b02 FIXED IN: mantis mantis-b02 INTEGRATED IN: mantis mantis-b02
14-06-2004

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
29-07-2002

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
17-07-2002

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
17-07-2002