JDK-4679530 : "file:" URLs: URI.toURL spec violated, host value inconsistency, equality issues
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Won't Fix
  • OS: solaris_8
  • CPU: sparc
  • Submitted: 2002-05-03
  • Updated: 2017-07-25
  • Resolved: 2017-07-25
Related Reports
Relates :  
Relates :  
Description
The spec for for the method java.net.URI.toURL asserts:

	This convenience method works as if invoking it were equivalent to
	evaluating the expression

		new URL(this.toString())

	after first checking that this URI is absolute.

But that equivalence assertion is currently violated for "file:" URLs,
in 1.4.0 and Hopper today.  The two approaches produce URL instances
that are not equal to each other-- the difference is whether the URL's
host value is null or empty string.

More generally:

Currently in 1.4.0 and Hopper, the following means of creating "file:"
URLs create a URL with a host value of empty string:

	a)	new URL("file:/tmp/")
	b)	(new File("/tmp")).toURL()

but these other means create a URL with a host value of null:

	c)	new URI("file:/tmp/").toURL()
	d)	(new File("/tmp")).toURI().toURL()

Thus, a URL created by (a) or (b) will not be equal (by URL.equals) to
a URL created by (c) of (d).

It would seem highly desirable for URL instances created by all of (a),
(b), (c), and (d)-- which all have the same external string forms-- to
be considered equal by URL.equals.

Put slightly differently, it would seem highly desirable to make the
expression "u.equals(new URL(u.toExternalForm())" return true for as
many kinds of URL instances "u" as reasonably possible.

The behaviors of (c) and (d) do seem justified, because these URIs
really do not have host components, and null is the value for not
having a given component-- see 4496251, which is marked fixed and
integrated in merlin-beta3, but was actually backed out because of
4506291 (also for merlin-beta3).

The seemingly reasonable equivalence assertion in the spec for
URI.toURL, which claims that (c) should produce the same result as (a),
is currently violated-- given that (c) seems to be doing the right
thing, this would seem to indicate that (a) should be fixed (again--
see Comments section), but then to keep 4506291 fixed as well, (b)
would then have to be fixed also, so that all of these expressions would
produce equivalent URL instances, with null host values.

But would such a change in the behaviors or (a) or (b) raise any other
compatibility issues?

Below is a test program that demonstrates the above issues and example output of the program with 1.4.0 and the latest Hopper promoted build.

import java.io.File;
import java.net.URI;
import java.net.URL;

public class FileURL {

    public static void main(String[] args) throws Exception {
	String pathString = args[0];
	String pathURL = (new File(pathString)).toURL().toExternalForm();

	println();
	println("URL a = new URL(\"" + pathURL + "\")");
	println();

	URL a = new URL(pathURL);
	dumpURL(a);

	println();
	println("URL b = (new File(\"" + pathString + "\")).toURL()");
	println();

	URL b = (new File(pathString)).toURL();
	dumpURL(b);

	println();
	println("URL c = new URI(\"" + pathURL + "\").toURL()");
	println();

	URL c = new URI(pathURL).toURL();
	dumpURL(c);

	println();
	println("URL d = (new File(\"" + pathString + "\")).toURI().toURL()");
	println();

	URL d = (new File(pathString)).toURI().toURL();
	dumpURL(d);

	println();
	println("a.equals(b) returns " + a.equals(b));
	println("a.equals(c) returns " + a.equals(c));
	println("a.equals(d) returns " + a.equals(d));
	println("b.equals(a) returns " + b.equals(a));
	println("b.equals(c) returns " + b.equals(c));
	println("b.equals(d) returns " + b.equals(d));
	println("c.equals(a) returns " + c.equals(a));
	println("c.equals(b) returns " + c.equals(b));
	println("c.equals(d) returns " + c.equals(d));
	println("d.equals(a) returns " + d.equals(a));
	println("d.equals(b) returns " + d.equals(b));
	println("d.equals(c) returns " + d.equals(c));
    }

    private static void dumpURL(URL u) {
	println("external form: " + u.toExternalForm());
	println("\tprotocol:  " + u.getProtocol());
	println("\tauthority: " + u.getAuthority());
	println("\tuser info: " + u.getUserInfo());
	println("\thost:      " + u.getHost());
	println("\tport:      " + u.getPort());
	println("\tpath:      " + u.getPath());
	println("\tfile:      " + u.getFile());
	println("\tquery:     " + u.getQuery());
	println("\tref:       " + u.getRef());
    }

    private static void println() { System.err.println(); }
    private static void println(String s) { System.err.println(s); }
}

[terrier] 71 % java -version
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
[terrier] 72 % java FileURL /tmp

URL a = new URL("file:/tmp/")

external form: file:/tmp/
	protocol:  file
	authority: null
	user info: null
	host:      
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

URL b = (new File("/tmp")).toURL()

external form: file:/tmp/
	protocol:  file
	authority: 
	user info: null
	host:      
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

URL c = new URI("file:/tmp/").toURL()

external form: file:/tmp/
	protocol:  file
	authority: null
	user info: null
	host:      null
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

URL d = (new File("/tmp")).toURI().toURL()

external form: file:/tmp/
	protocol:  file
	authority: null
	user info: null
	host:      null
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

a.equals(b) returns true
a.equals(c) returns false
a.equals(d) returns false
b.equals(a) returns true
b.equals(c) returns false
b.equals(d) returns false
c.equals(a) returns false
c.equals(b) returns false
c.equals(d) returns true
d.equals(a) returns false
d.equals(b) returns false
d.equals(c) returns true

[terrier] 74 % java -version
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-b10)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b10, mixed mode)
[terrier] 75 % java FileURL /tmp

URL a = new URL("file:/tmp/")

external form: file:/tmp/
	protocol:  file
	authority: null
	user info: null
	host:      
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

URL b = (new File("/tmp")).toURL()

external form: file:/tmp/
	protocol:  file
	authority: 
	user info: null
	host:      
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

URL c = new URI("file:/tmp/").toURL()

external form: file:/tmp/
	protocol:  file
	authority: null
	user info: null
	host:      null
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

URL d = (new File("/tmp")).toURI().toURL()

external form: file:/tmp/
	protocol:  file
	authority: null
	user info: null
	host:      null
	port:      -1
	path:      /tmp/
	file:      /tmp/
	query:     null
	ref:       null

a.equals(b) returns true
a.equals(c) returns false
a.equals(d) returns false
b.equals(a) returns true
b.equals(c) returns false
b.equals(d) returns false
c.equals(a) returns false
c.equals(b) returns false
c.equals(d) returns true
d.equals(a) returns false
d.equals(b) returns false
d.equals(c) returns true

Comments
Old issue. Policy is not to change behavior of URL class except in case of critical need.
25-07-2017

SUGGESTED FIX The apparent ideal solution would be to modify the behavior of *both* (a) and (b) to produce URL instances with a host value of null instead of empty string (thus satisfying both 4496251 and 4506291 as well as this bug). Other possible solution alternatives might include: - removing the equivalence assertion in URI.toURL - making (c) and (d) produce URLs with host values of empty string - changing URL.equals to consider a null host value to be equivalent to an empty string host value None of these other alternatives seem very desirable. ###@###.### 2002-05-03
03-05-2002