United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4155617 : Swing can't use a PLAF from a different classloader than loaded the Swing jar

Details
Type:
Bug
Submit Date:
1998-07-08
Status:
Resolved
Updated Date:
2004-09-13
Project Name:
JDK
Resolved Date:
1999-02-01
Component:
client-libs
OS:
generic,solaris_2.6,windows_nt
Sub-Component:
javax.swing
CPU:
generic,x86,sparc
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.0.2,1.2.0,1.3.0
Fixed Versions:
1.2.2 (1.2.2)

Related Reports
Backport:
Duplicate:
Duplicate:
Relates:

Sub Tasks

Description
This problem came to light when writing a Swing applet, which includes
a custom Look and Feel, downloaded from the applet's codebase.

This could be made to work with appletviewer and HotJava but not
with the Java Plugin.
With Plugin the applet would get errors like:-

"ClassNotFoundException: CustomLookAndFeel"
when making the call
UIManager.setLookAndFeel("CustomLookAndFeel")

Or if the code is changed to directly instantiate an instance of
the PLAF,

  UIManager.setLookAndFeel(claf = new CustomLookAndFeel());

then the Look and Feel loads correctly, but when you attempt to
instantiate a Swing component , you get:-
 
  UIDefaults.getUI() failed: no ComponentUI class for:
  com.sun.java.swing.JList[,0,0,0x0,invalid]
  java.lang.Error
  at java.lang.Throwable.<init>(Compiled Code)
  at java.lang.Error.<init>(Compiled Code)
  at com.sun.java.swing.UIDefaults.getUIError(UIDefaults.java:296)
  at com.sun.java.swing.UIDefaults.getUI(Compiled Code)
  at com.sun.java.swing.UIManager.getUI(Compiled Code)
  at com.sun.java.swing.JList.updateUI(JList.java:314)
  at com.sun.java.swing.JList.<init>(JList.java:237)
  at com.sun.java.swing.JList.<init>(JList.java:248)
  at CustomLookAndFeel.commonStartup(CustomLookAndFeel.java:74)
  at CustomLookAndFee.init(CustomLookAndFeel.java:21)
  at sun.applet.AppletPanel.run(Compiled Code)
  at java.lang.Thread.run(Compiled Code)

The key difference is that the Java Plugin includes the swingall.jar,
and so Swing is loaded by the native system classloader, whereas in the
case of the appletviewer, the applet tag includes the swing.jar in the
ARCHIVE tag, so it was downloaded and hence had the same classloader
as the applet and the custom look and feel.

Swing uses class.forName() to locate the UI classes and the Look and Feel
class.
class.forName() will use the class loader of the invoking class.
Since that is a Swing class, loaded from local disk, getClass.getClassLoader()
returns null.
This bug is the same problem as reported in bug 4120021.
That bug has been fixed for the case where a component is downloaded
The fix there in getUI() gets the classloader of the target component.
  ClassLoader uiClassLoader = target.getClass().getClassLoader();
and uses that to locate the class.

In this case what is needed is more like

ClassLoader uiClassLoader = (UIManager.getLookAndFeel()).getClass().getClassLoader();

That's still not quite right since it doesn't help in
the setLookAndFeel method, or solve the downloaded component case.

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


phil.race@eng 1998-08-03

Under JDK 1.2 Beta4, application classes are loaded by a classloader
instance, so this bug effectively prevents the whole custom L&F architecture
of Swing from functioning evenm for applications.

See http://java.sun.com/products/jdk/1.2/compatibility.html, Bullet #7 
for more info on this.

The following things break:
    - UIManager.setLookAndFeel("myCustomLAFName")
    - attempts to use an auxiliary L&F
    - attempts to install a single custom UI delegate
=======================================================================



                                    

Comments
SUGGESTED FIX

One suggestion was a new method which allows the programmer to supply
a classloader.
I think if possible it would be preferable to have Swing work out the
right thing to do.
Eg for a look and feel, the default would be to load the UI classes from
the same loader as the LookAndFeel class.

                                     
2004-09-14
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
1.2.2
swing1.1

FIXED IN:
1.2.2
swing1.1

INTEGRATED IN:
1.2.2


                                     
2004-09-14
EVALUATION

I believe the descpription is correct about the source of this problem.
When Swing is available via the plugin or on the browsers classpath, 
the Class.forName() call used to load the L&F classes doesn't see
classes that are available via the applet class loader.  To remedy the
problem I've added support for specifying the class loader to be used
for loading UI classes: if an "ClassLoader" entry exists in the defaults
table it will be used to load UI classes.  

One can set the class loader for all UI classes all the time
like this:

    UIManager.put("ClassLoader", someClassLoader);

Generally it's preferable to just specify the class loader for the
current default look and feel:

    UIManager.getLookAndFeelDefaults().put("ClassLoader", someClassLoader);

In earlier versions of swing-1.1beta, if a JComponent has a non-null
class loader then the components class loader is used to load its
UI class.  This new property "ClassLoader" overrides this behavior.

Here's an example that demonstrates this new property.  Note that one
still must create the LookAndFeel class directly, rather than using
the convenience version of UIManager.setLookAndFeel() that accepts
a class name.

/** 
 * DemoApplet.java
 * 
 * Sample applet that sets the default look and feel to be 
 * DemoLookAndFeel.  This applet uses the special "ClassLoader"
 * UIManager property to specify the class loader to be used
 * for UI classes.
 */

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;


public class DemoApplet extends JApplet
{
    public DemoApplet() {
	super();

	/* This will prevent the "SystemEventQueue" access message
	 * from appearing in the Java console when this applet is 
	 * run in a browser.
	 */
	getRootPane().putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE);
    }

    public void init() 
    {
	try {
	    UIManager.setLookAndFeel(new DemoLookAndFeel());
	}
	catch (Exception e) {
	    e.printStackTrace();
	}
	
	UIManager.getLookAndFeelDefaults().put("ClassLoader", getClass().getClassLoader());

	JPanel p = new JPanel(new FlowLayout());
	final JLabel l = new JLabel("");
	
	ActionListener helloWorldAction = new ActionListener() {
	    int n = 0;
	    public void actionPerformed(ActionEvent e) {
		l.setText("Hello World " + n);
		n += 1;
	    }
	};
	JButton b = new JButton("Hello World");
	b.addActionListener(helloWorldAction);

	p.add(b);
	p.add(l);
	getContentPane().add(p);
    }
}


/**
 * DemoLookAndFeel.java
 * 
 * Trivial subclass of MetalLookAndFeel.  Defines a new mapping
 * for "ButtonUI", the JButton ComponentUI class.
 */

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;

import javax.swing.plaf.metal.MetalLookAndFeel;

public class DemoLookAndFeel extends MetalLookAndFeel
{
    protected void initClassDefaults(UIDefaults table)
    {
        super.initClassDefaults(table);
        table.putDefaults(new Object[] {"ButtonUI", "DemoButtonUI"});
    }
}


/**
 * DemoButtonUI.java
 * 
 * Trivial subclass of MetalButtonUI.  We've hardwired the
 * focus clolor here (to green) so that one can tell
 * that the button isn't sporting the standard Metal look 
 * and feel. 
 */

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;

import javax.swing.plaf.metal.MetalButtonUI;
import javax.swing.plaf.ComponentUI;

public class DemoButtonUI extends MetalButtonUI
{
    private final static DemoButtonUI demoButtonUI = new DemoButtonUI(); 

    public static ComponentUI createUI(JComponent c) {
        return demoButtonUI;
    }

    /* So we know that the DemoButtonUI class is actually
     * in use.
     */
    protected Color getFocusColor() {
	return Color.green;
    }
}


<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
  <head>
    <title>DemoApplet</title>
  </head>

  <body>
    <h1>DemoApplet</h1>
    <p>
    To run this applet a copy of swing.jar must be in the local
    directory.  
    <hr>
    <h2>Here's the applet:</h2>
    <applet 
      code=DemoApplet 
      codebase=. 
      archive="swing.jar" 
      width=600 
      height=200 
      alt="Your browser understands the &lt;APPLET&gt; tag but isn't running the applet, for some reason.">
      Your browser is completely ignoring the &lt;APPLET&gt; tag!
    </applet>
    <hr>
    <a href="DemoApplet.java">The source</a>.

<!-- Created: Fri Apr 10 16:47:50 PDT 1998 -->
<!-- hhmts start -->
Last modified: Thu Oct 29 17:27:21 PST 1998
<!-- hhmts end -->
  </body>
</html>


hans.muller@Eng 1998-10-29
                                     
1998-10-29



Hardware and Software, Engineered to Work Together