JDK-4155645 : "context class loader" doesn't work as intended (and is vaguely specified)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:class_loading
  • Affected Version: 1.2.0
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: solaris_2.5
  • CPU: sparc
  • Submitted: 1998-07-08
  • Updated: 2002-05-31
  • Resolved: 2002-05-31
Related Reports
Relates :  
Relates :  
Relates :  
Description
A thread's context class loader is not always set how one would expect
it to be set, such as when callbacks are dispatched, making this new
feature useless in a lot of situations and causing class resolution to
fail when it should work.  (Furthermore, these expectations of how the
context class loader should be getting set are based only on an
understanding of what this feature was intended for; the actual
specificification is extremely vague.)

A concrete example of the bug is that when applet code is invoked through
an AWT callback, the context class loader is NOT set to the applet's class
loader; thus, if the applet then tries to do anything that resolves classes
through the context class loader, it will fail.  This behavior could be
considered an AWT bug, but it's not clear that it would be feasible or even
possible to fix AWT to set the context class loader correctly in all cases,
and this problem will apply to almost any other callback facility as well.
Therefore, perhaps we should consider that this bug is a design flaw in the
context class loader mechanism.

In addition, it is difficult for code to take matters into its own hands and
set the context class loader for a piece of code that it wishes to execute,
because modifying any thread's context class loader, even the current thread's
only during the current method's execution, requires the coarse-grained
"setContextClassLoader" permission (in 1.2beta3, it only required permission
to "access" the target thread).

[This bug report is being filed to document these couple of problems/issues
with the per-thread "context class loader" feature (of the 1.2 extension
mechanism) that have been discussed recently in several arenas, but no
resolution seems forthcoming.]

	more details:

When an applet's code is executed, it should be able to expect that the
context class loader of the thread in which it was invoked will be the
class loader that loaded the applet.  This applet-related support has
seemed to be the most concrete example given for the motiviation behind
the design of the current context class loader mechanism.  The following
applet demonstrates how this expectation is not met (source files are
also attached to this bug report):

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class AppletContextClassLoader extends Applet {
    public void init() {
	outputContextLoader("init()");
	Button b = new Button("Press Me");
	b.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		outputContextLoader("processActionEvent()");
	    }
	});
	this.add(b);
    }
    public void start() { outputContextLoader("start()"); }
    public void stop() { outputContextLoader("stop()"); }
    public void destroy() { outputContextLoader("destroy()"); }
    private static void outputContextLoader(String where) {
	ClassLoader contextLoader =
	    Thread.currentThread().getContextClassLoader();
	System.err.println(where + ": context class loader = " + contextLoader);
    }
}

When this applet is executed and the "Press Me" button is pressed, the
following output is printed to the console:

init(): context class loader = sun.applet.AppletClassLoader@7589d67d
start(): context class loader = sun.applet.AppletClassLoader@7589d67d
processActionEvent(): context class loader = sun.misc.Launcher$AppClassLoader@1301d67c

Notice that when the init() and start() methods are executed, the applet's
class loader is the context class loader, but when the handler for the
button press is executed, the context class loader is the "application"
class loader.  (Note that this output required that the applet be granted
the "getClassLoader" permission, which is unusual; otherwise, the button
press would have resulted in a security exception!)

The methods called through the methods of java.applet.Applet do see the
context class loader properly set because our appletviewer implmentation
takes care to set the context class loader to the applet's class loader in
the thread that invokes those methods.  But when applet code is invoked as
part of an AWT callback, the context class loader will just be the base loader
that loads classes from CLASSPATH; AWT doesn't do anything to set the
context class loader before calling back into the applet.

So is this a bug in AWT?  Before making any callback, should it figure
out the context class loader for the component that it is calling into,
perhaps by remembering what the context class loader was when the event
listener was registered, and set it to that?  Is this even possible (or
feasible to implement reliably) given the various layers of event
dispatch code?  (I don't know.)  Furthermore, is this a burden that we
want to place on all code that does event dispatching or other sorts of
callbacks?  What about finalizers, for example?

Alternatively, is it responsibility of the callback code itself to set its
own context class loader?  If this is true, then the other problem regarding
the coarse-grained permission required to set a context class loader is
particularly serious.  For example, if applet code needs to set its own
context class loader, then it needs to be granted "setContextClassLoader"
permission, which should not be granted to arbitrary applets...



The other problem is that the facility is strongly limited by the overly
coarse-grained permission required to set one's thread's context class loader.
In 1.2beta3, only "access" to the target thread was required to set its
context class loader.  Perhaps this was too lenient, but now with 1.2beta4,
the "setContextClassLoader" RuntimePermission is required to modify *any*
thread's context class loader, so code can't even set the context loader for
the current thread.  I presume that the (quite sensible) worry is that without
some security check, any code could code change the context class loader of its
caller, which is bad because how could the caller ever be prepared for the
ramifications of that occurrence.  But there is no problem with an arbitrary
method setting its thread's context class loader just for the duration of its
execution, which is exactly all that most code will want to do, i.e. 
something like this:

(ignoring for the moment that try/finally is now deemed unreliable...)

	ClassLoader savedCcl = Thread.currentThread().getContextClassLoader();
	try {
	    Thread.currentThread().setContextClassLoader(otherLoader);
	    // do some stuff in context of "otherLoader" ...
	} finally {
	    Thread.currentThread().setContextClassLoader(savedCcl);
	}

But we can't grant not-fully-trusted code the ability to set the context class
loader only for its own needs like this without giving it the ability to mess
up its caller's context, so any code that wants to set the context class loader
will need to need to require that it be granted "setContextClassLoader"
permission, which is a lot of trust to require, especially if no other explicit
permissions are otherwise necessary to grant to it.

Some real complaints about the difficulty with this coarse-grained
"setContextClassLoader" permission have been reported on 1.2 RMI applications
in development that want to supply downloadable proxy classes that need to
deserialize a MarhsalledObject received from a remote server using a
particular class loader to resolve classes from, and this class loader is
not the context class loader, nor is it even on the execution stack.  So the
context class loader needs to be set during the harmless operation, but the
dangerous "setContextClassLoader" permission is required.

peter.jones@East 1998-07-08

Comments
WORK AROUND To work around the fact that the your class loader isn't set when you need to invoke an operation that attempts to resolve classes for you using the context class loader, you could take the responsibility of setting the context class loader to the appropriate loader to resolve from, although that requires that you have the "setContextClassLoader" RuntimePermission...
11-06-2004

PUBLIC COMMENTS A thread's context class loader is not always set how one would expect it to be set, such as when callbacks are dispatched, making this new feature useless in a lot of situations and causing class resolution to fail when it should work. (Furthermore, these expectations of how the context class loader should be getting set are based only on an understanding of what this feature was intended for; the actual specificification is extremely vague.) A concrete example of the bug is that when applet code is invoked through an AWT callback, the context class loader is NOT set to the applet's class loader; thus, if the applet then tries to do anything that resolves classes through the context class loader, it will fail. This behavior could be considered an AWT bug, but it's not clear that it would be feasible or even possible to fix AWT to set the context class loader correctly in all cases, and this problem will apply to almost any other callback facility as well. Therefore, please consider this bug being a design flaw in the context class loader mechanism. In addition, it is difficult for code to take matters into its own hands and set the context class loader for a piece of code that it wishes to execute, because modifying any thread's context class loader, even the current thread only during the current method's execution, requires the coarse-grained "setContextClassLoader" permission (in 1.2beta3, it only required permission to "access" the target thread).
10-06-2004

EVALUATION Since the applet event model has been changed in 1.2fcs to support one event queue per-applet the bug that Peter points out in his attachment has been fixed. As for the other daemon threads that invoke user callbacks we might still need to fix some on a case-by-case basis but according to the AWT group both AWT and SWING should be OK with the recent changes to the event model. I also talked to Jim Graham about image decoder callbacks and he didn't think that supporting the context class loader was important as these callbacks are performance critical (one invoked for each image line) and are typically used only for image filtering. The context class loader will be supported once Jim moves to a per-Applet event model like AWT, but this will not likely happen for 1.2. Finally, we certainly need to better document all this and how the context class loader needs to be supported for extensions and system code implementing callback threads. david.connelly@Eng 1998-09-10 Testcase appears to work fine in latest JDK 1.4.1 b13 release. Closing as not reproducible in latest JDK. AS described in the comments section the larger issues of this report could not be resolved for 1.2 fcs, but I feel RFE's should be filed for tiger.. Referencing this report. ###@###.### 2002-05-30
30-05-2002

SUGGESTED FIX To solve the most concrete example of the bug described in this report, perhaps AWT could be made to set the context class loader in its event dispatch threads before making a callback to the appropriate class loader for the callback code, either by examining the target object, or by storing the context class loader that was in effect when the event listener was registered. I'm not sure how feasible this would be to implement in the AWT event architecture, however. And this same care would need to be taken by all other system or library (extension?) callback mechanisms (finalizer invocation being one example). Alternatively, perhaps Thread.getContextClassLoader() could be changed to employ a somewhat more dynamic mechanism, relieving some of the burden from all callback dispatchers. See the "Comments" section for some musings. Unfortunately, I haven't thought of any compelling solutions to the problem that a coarse-grained, trust-requiring permission must be granted to code that needs to set its context class loader for safe, non-intrusive purposes. Or rather, I haven't been able to think of any solutions that do not require API changes. With the precedent of the doPrivileged() closure technique, for example, something like doWithContextClassLoader() would address this problem, because it could execute a piece of code with a particular context class loader set, but the context class loader of the caller wouldn't be affects, and no permissions would need to be required of the code that uses it. peter.jones@East 1998-07-08
08-07-1998