JDK-4711907 : (cl spec) Class.getResource{AsStream} makes unspecified changes to resource name
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:class_loading
  • Affected Version: 1.4.0,5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2002-07-09
  • Updated: 2017-05-16
  • Resolved: 2003-12-19
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
5.0 b32Fixed
Description

Name: erR10175			Date: 07/08/2002


 
New JCK testcases

api/java_lang/Class/index.html#Reflect2[Class2245]
api/java_lang/Class/index.html#Reflect2[Class2246]

fail with the following messages:
Class2245: Failed. unexpected changes detected
Class2246: Failed. unexpected changes detected
 
because both java.lang.Class.getResource(String name) and 
java.lang.Class.getResourceAsStream(String name), while delegating their call 
to class loader, make changes to the resource name that are not specified
explicitly in the descriptions of the methods.
 
The specification of the methods reads:
"This method delegates the call to its class loader, after making these 
changes to the resource name: if the resource name starts with "/", it is 
unchanged; otherwise, the package name is prepended to the resource name 
after converting "." to "/"."

The RI makes the following changes to the resource name that seem reasonable 
but are not specified in the documentation explicitly:
1. leading "/" is removed;
2. "/" is inserted between package name and resource name;
3. "." is converted to "/" in the package name, but is left unchanged 
   in resource name.
 
The following table shows 5 resource names that tested (the package 
name is "p1.p2"):

----------------------------------------------------------------
resource name        may be expected       made by RI
                     according to spec
----------------------------------------------------------------
"/"                     "/"                  ""            
""                      "p1.p2"            "p1/p2/"
"/repository\\dir/resource.dat" 
              "/repository\\dir/resource.dat"
                                  "repository\\dir/resource.dat"
"x.y.z"              "p1.p2x.y.z"       "p1/p2/x.y.z"
"x/y/z"              "p1.p2x/y/z"       "p1/p2/x/y/z"

----------------------------------------------------------------

To reproduce the first failure compile and run Class2245.java (see below) as 
the following log shows:
$java -version && javac Class2245.java && java Class2245; echo $?
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b15)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b15, mixed mode)
getResource()
getResource(p1/p2/)
getResource(repository\dir/resource.dat)
getResource(p1/p2/x.y.z)
getResource(p1/p2/x/y/z)
unexpected changes detected
1
 
----------------------- Class2234.java
import java.io.PrintStream;
import java.util.Vector;
import java.net.URL;

/*
 * class loader that defines class p1.p2.B
 * and hook getResource calls
 */

class ResourceGetter extends ClassLoader { 

    protected ResourceGetter(ClassLoader parent) {
        super(parent);
        log = new Vector();
    }
    
    public static final String specialClass = "p1.p2.B";

    /* bytes of an empty class p1.p2.B
     */
    public static final byte[] specialClassCode = {
        (byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, 
        (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x2D,
        (byte)0x00, (byte)0x05, (byte)0x07, (byte)0x00, 
        (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x10,
        (byte)0x6A, (byte)0x61, (byte)0x76, (byte)0x61, 
        (byte)0x2F, (byte)0x6C, (byte)0x61, (byte)0x6E,
        (byte)0x67, (byte)0x2F, (byte)0x4F, (byte)0x62, 
        (byte)0x6A, (byte)0x65, (byte)0x63, (byte)0x74,
        (byte)0x07, (byte)0x00, (byte)0x04, (byte)0x01, 
        (byte)0x00, (byte)0x07, (byte)0x70, (byte)0x31,
        (byte)0x2F, (byte)0x70, (byte)0x32, (byte)0x2F, 
        (byte)0x42, (byte)0x00, (byte)0x20, (byte)0x00,
        (byte)0x03, (byte)0x00, (byte)0x01, (byte)0x00, 
        (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
        (byte)0x00, (byte)0x00, (byte)0x00
    };

    protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (name.equals(specialClass)) {
            return defineClass(specialClass, specialClassCode, 0, specialClassCode.length);
        } 
        return super.loadClass(name, resolve);
    }
    public Class loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

    public Vector log;

    public URL getResource(String name) {
        log.add("getResource(" + name + ")");
        return getParent().getResource(name);
    }

    public void printLog() {
        for (int i=0; i < log.size(); ++i) {
            System.out.println(log.get(i));
        }
    };
}

public class Class2245 {
    public static void exit(int retCode, String msg) {
        System.out.println(msg);
        System.exit(retCode);
    }

    public static void main(String[] args) {
        ResourceGetter cl = new ResourceGetter(Class2245.class.getClassLoader());
        Class c = null;
        try {
            c = Class.forName(ResourceGetter.specialClass, false, cl);
        } catch (ClassNotFoundException e) {
            exit(1, "cannot load class " + ResourceGetter.specialClass);
        }
        
        String[][] cases = {
            {"/", "/"}, // unchanged
            {"", "p1.p2"},^G^G^G^G
            {"/repository\\dir/resource.dat", "/repository\\dir/resource.dat"}, // unchanged
            {"x.y.z", "p1.p2x.y.z"},
            {"x/y/z", "p1.p2x/y/z"},
        };
    
        boolean result = true;

        for (int i = 0; i < cases.length; ++i) {
            cl.log.clear();
            c.getResource(cases[i][0]);
            if (!cl.log.contains("getResource(" + cases[i][1] + ")")) {
                cl.printLog();
                result = false;
            }
        }
    
        if (!result) {
            exit(1, "unexpected changes detected");
        }
    
        exit(0, "OKAY");
    }
}
--------------------------------------

======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta FIXED IN: tiger-beta INTEGRATED IN: tiger-b32 tiger-beta VERIFIED IN: tiger-beta2
24-08-2004

EVALUATION These resource name changes have been performed since before jdk1.1. They are implemented in the private method Class.resolveName which was added in update 1.45. The javadoc for Class.getResourceAsStream (which was later propagated to Class.getResource in bug 4062578) was updated to contain the disputed specification in the same update. -- iag@sfbay 2002-07-09 We should document the long-standing behaviour of both of these methods. -- iag@sfbay 2003-11-24
09-07-2002