JDK-6888528 : NoClassDefFoundError while loading class in applet with Class.forName()
  • Type: Bug
  • Component: deploy
  • Sub-Component: plugin
  • Affected Version: 6u16
  • Priority: P3
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2009-10-06
  • Updated: 2011-02-16
  • Resolved: 2009-10-22
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_18-ea"
Java(TM) SE Runtime Environment (build 1.6.0_18-ea-b02)
Java HotSpot(TM) Client VM (build 16.0-b09, mixed mode, sharing)

But also fails with 1.6.0_15 and higher versions.
1.6.0_14 works properly

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [verzi��sz��m: 5.1.2600] with SP3

EXTRA RELEVANT SYSTEM CONFIGURATION :
Browsers:
Mozilla/5.0 (Windows; U; Windows NT 5.1; hu; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)
Internet Explorer: 8.0.6001.18702

Web server:
Server version: Apache/2.2.11 (Win32)
Server built:   Dec 10 2008 00:10:06

Ant:
Apache Ant version 1.7.1

A DESCRIPTION OF THE PROBLEM :
Let's separate the Applet into two or more jar, and use signed jars, to enable higher privileges to the applet. Let there be the Applet class(TestApplet), and another class(FooClass1) in the main jar(test.jar), Applet class in the default package the other in foopackage. Let there be another class(FooClass2), which is packaged to foo.jar and placed also into foopackage.
Make the code so, the FooClass2 only loads when it is needed. (For example when users push a button.)
If the applet is loaded, and hasn't got any parts in the cache, this is working fine, classes loaded properly, applet working.
If the main jar is in the cache, but foo.jar isn't, and the applet is started, classes in test.jar working fine, but when user push the button a NoClassDefFoundError is raising from AppletClassLoader.findClass() after foo.jar is downloaded to the client machine.
As i tested everything work fine also when the FooClass1 and FooClass2 are in different packages.

Requirements for reproducing the bug:
Web server, if applet is loading locally, this everything works fine.
Ant to create the classes with the attached build.xml (build with release.sign target. You should use release target to build unsigned jars.)
A testkeystore, which contains a testkey with a pass as testpass, if there is an other useable keystore, the build.xml has to be edited before used to build signed jars.
This bug appears only with signed jars, unsigned jars working properly.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
(The following two step is needed only when all applet jars are in the cache)
- 2. Open java control panel
- 1. Clear the cache
1. Open the web page that contains the applet from a web server
2. Wait until applet is loaded
3. Close the browser
4. Reopen the browser, and load the web page again
5. Push the button which triggers the second class loading
6. Error thrown


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It would be nice,if the applet can working properly and the second class can be loaded without special preconditions exists. (The precondition which is needed for now: Nothing is in the cache from the applet codebase)
ACTUAL -
NoClassDefFoundError thrown when the main jar is in the cache, and user triggers the loading of the second class from the other jar.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Attached seperatly

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
TestApplet:
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import foopackage.FooClass1;

public class TestApplet extends JApplet {

    private static final long serialVersionUID = -8222760725492315060L;

    public void init() {
	SwingUtilities.invokeLater(new Runnable() {
	    public void run() {
		createGUI();
	    }
	});
    }

    private void createGUI() {
	JPanel panel = new JPanel();
	panel.add(new JLabel("testApplet"));
	panel.add(new JButton(new AbstractAction("fooaction") {
	    private static final long serialVersionUID = -7435162062961631773L;

	    public void actionPerformed(ActionEvent e) {
		try {
		    Class<?> cl = Class.forName("foopackage.FooClass2");
		    Object foo = cl.newInstance();
		    System.out.println(foo);
		    ;
		} catch (ClassNotFoundException e1) {
		    e1.printStackTrace();
		} catch (InstantiationException e1) {
		    e1.printStackTrace();
		} catch (IllegalAccessException e1) {
		    e1.printStackTrace();
		}
	    }
	}));

	getContentPane().add(panel);
    }

    public void start() {
	FooClass1 foo = new FooClass1();
	System.out.println(foo);
    }

    public void stop() {
    }

    public void destroy() {
    }
}

foopackage.FooClass1:
package foopackage;

public class FooClass1 {

    @Override
    public String toString() {
	return "FooClass1";
    }

}

foopackage.FooClass2:
package foopackage;

public class FooClass2 {

    @Override
    public String toString() {
	return "FooClass2";
    }

}

build.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project name="applettest" default="javac" basedir=".">

	<target name="javac" description="Creates java classes.">
		<mkdir dir="bin" />
		<javac srcdir="." destdir="bin" />
	</target>

	<target name="release" depends="clean, javac" description="Create jar files to ./release">
		<mkdir dir="release" />
		<jar jarfile="release/foo.jar" includes="foopackage/FooClass2.class" basedir="bin" />
		<jar jarfile="release/test.jar" includes="TestApplet2*.class foopackage/FooClass1.class" basedir="bin">
			<manifest>
				<attribute name="Class-Path" value="foo.jar" />
			</manifest>
		</jar>
	</target>

	<target name="release.sign" description="Create jar signed jar files to ./release">
		<signjar jar="release/test.jar" alias="testkey" storepass="testpass" signedjar="release/test.jar"
			keystore="testkeystore" />
		<signjar jar="release/foo.jar" alias="testkey" storepass="testpass" signedjar="release/foo.jar"
			keystore="testkeystore" />
	</target>

	<target name="clean" description="Cleans all output folder.">
		<delete dir="bin" />
		<delete dir="release" />
	</target>

</project>

index.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>TestApplet</title>
<script language="Javascript">
  var _app = navigator.appName;

  document.write("browser; ",_app,"<br/>");
  
  if (_app == 'Mozilla' || _app == 'Netscape') {
    document.write('<embed CODEBASE="."',
    	    	   'ARCHIVE="test.jar"',
    	    	   'CODE="TestApplet"',
                   'width="200"',
                   'height="200"',
                   'type="application/x-java-applet;version=1.6"/>');
    }
  else if (_app == 'Microsoft Internet Explorer') {
    document.write('<OBJECT ',
                   'classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"',
                   'width="200"',
                   'height="200">',
                   '<PARAM name="code" value="TestApplet">',
                   '<PARAM name="archive" value="test.jar">',
                   '</OBJECT>');
    }
  else {
    document.write('<p>Sorry, unsupported browser.</p>');
    }
</script>
</body>
</html>

Testkeystore should be created, or the whole stuff with testkeystore is downloadable from: http://csomalin.csoma.elte.hu/~pifta/test2.zip
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I have found several thing but that are not really acceptible:

1. downgrade to 1.6.0_14 java.
2. Turn off NG Plugin
3. Clear the cache everytime before loading the applet
4. Redesign the applet and separate the classes and create jars in a way where one package is placed only into one jar file. (For an app which has many classes this is also not really an option (our has 3000 classes with approx 150 jars))

Release Regression From : 6u14
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
EVALUATION This problem can't be reproduced with 6u18 b04 PIT build or even with 6u15. Following the test steps in the description section, I got the following exception: Exception in thread "AWT-EventQueue-2" java.lang.SecurityException: class "foopackage.FooClass2"'s signer information does not match signer information of other classes in the same package at java.lang.ClassLoader.checkCerts(Unknown Source) at java.lang.ClassLoader.preDefineClass(Unknown Source) at java.lang.ClassLoader.defineClass(Unknown Source) at java.security.SecureClassLoader.defineClass(Unknown Source) at sun.applet.AppletClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at sun.applet.AppletClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClassInternal(Unknown Source) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Unknown Source) at TestApplet$2.actionPerformed(TestApplet.java:32) at javax.swing.AbstractButton.fireActionPerformed(Unknown Source) at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.setPressed(Unknown Source) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source) at java.awt.Component.processMouseEvent(Unknown Source) at javax.swing.JComponent.processMouseEvent(Unknown Source) at java.awt.Component.processEvent(Unknown Source) at java.awt.Container.processEvent(Unknown Source) at java.awt.Component.dispatchEventImpl(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source) The above exception is expected because the FooClass2.class wasn't signed but test.jar was signed. The testcase ran fine after the following steps: Placed FooClass2.class in foo.jar and TestApplet*.class and FooClass1.class in test.jar, and then signed both jar files using the same certificate. Modified the archive tag in the html file to include foo.jar as follows: <PARAM name="archive" value="test.jar,foo.jar"> Closing it as "not reproducible".
22-10-2009