JDK-4155617 : Swing can't use a PLAF from a different classloader than loaded the Swing jar
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.0.2,1.2.0,1.3.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,solaris_2.6,windows_nt
  • CPU: generic,x86,sparc
  • Submitted: 1998-07-08
  • Updated: 2004-09-13
  • Resolved: 1999-02-01
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 Other
1.2.0Resolved 1.2.2 1.2.2Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
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
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
14-09-2004

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.
14-09-2004

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
29-10-1998