JDK-6500212 : (cl) ClassLoader.loadClass() fails when byte array is passed
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:class_loading
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2006-12-04
  • Updated: 2012-10-08
  • Resolved: 2007-07-27
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0-rc"
Java(TM) SE Runtime Environment (build 1.6.0-rc-b104)
Java HotSpot(TM) 64-Bit Server VM (build 1.6.0-rc-b104, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux localhost.localdomain 2.6.19 #1 SMP Thu Nov 30 10:17:45 GMT 2006 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
The behaviour of ClassLoader.loadClass in JDK6 changed when comparing it to the one in JDK5. It now throws a ClassNotFoundException when "[B" (for byte array) is passed as an argument. In JDK5, the correct Class object would be returned. This is a very nasty regression as far as I can see. I have included a very simple test case that shows the problem.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I've added a very simple test case that shows the problem in the appropriate box. It should be obvious from the test case, but a summary is:

1) Call ClassLoader.loadClass("[B")
2) A ClassNotFoundException is thrown instead of the relevant class object being returned.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test should pass, and it does with jdk1.5.0_08.
ACTUAL -
The test fails with a ClassNotFoundException. I am pasting the stack trace in the "Error Message" box.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Stack trace from the test.

java.lang.ClassNotFoundException: [B
	at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
	at j.lang.test.LoadClassTest.testLoadClassWithBytePrimitiveArray(LoadClassTest.java:14)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
	at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
	at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
	at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:71)
	at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
	at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)



REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package j.lang.test;
import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class LoadClassTest	{

	@Test
	public void testLoadClassWithBytePrimitiveArray() throws Exception {
		String byteArrayBinaryName = byte[].class.getName();
		Class c = Thread.currentThread().getContextClassLoader().
				loadClass(byteArrayBinaryName);
		assertEquals(byte[].class, c);
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use JDK 5.0 Update 8.

Comments
EVALUATION This is a complicated, icky problem. It is purely accidental that array syntax ever worked. There was never any design intent for it to succeed and we haven't seen any scenario where it makes sense though we've recieved many reports of cases where users have come to expect it to succeed. In any case, as things stood prior to my change in tiger b32, requests which use array syntax don't always succeed. So far, we have determined that it might be successful if the component type of the array has already been loaded. We have found cases where the load fails even if the component type has already been loaded, but have been unable to further characterize the failures. There may be some latent bug in the way their code works at runtime due to this instability. At this point, we do not expect to modify class loading to rectify the pre-b32 behaviour. http://java.sun.com/j2se/1.5.0/compatibility.html In Tiger we disabled the array syntax check for Tiger thus taking us back to the pre-b32 instability. At that time, we made a commitment to re-enable this check early in jdk6 development and we strongly encourage users to begin removing their dependence on array syntax. See bug 4976356 for further details. To aid testing with the expected jdk6 behaviour, we provided a new system property, "sun.lang.ClassLoader.allowArraySyntax". The default value for this property in Tiger is "true". The default is "false" in jdk6. Generally speaking reflective loading of a class by name should be accomplished by using this static method in java.lang.Class: public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException The ClassLoader.loadClass() method is more typically used for class loader delegation. Invocation of Class.forName() may eventually invoke ClassLoader.loadClass() after handling VM name resolution. In particular, for array classes, this would involve loading the array's component type. Thus, we highly recommend replacement of this code: myClassLoader.loadClass(className); With this code: Class.forName(className,false,myClassLoader); I doubt that we can fully implement array syntax. The best we might be able to do is restore the pre-Tiger unreliable behaviour. There is currently an open bug considering this possibility (6434149). I'm closing this issue as duplicate.
27-07-2007