This looks like a fine idea, so we can have several pluggable
ClassLoader Architecture options, sounds very interesting.
I need to think about how I might also utilise this pluggable
architecture before I get back to you. See if I can achieve the
required functionality with the existing interface, or whether I'd need
to add a method.
Cheers,
Peter.
Gregg Wonderly wrote:
The primary issue that my previous posting was trying to sort out, was
the fact that inside of the core of River class loading, is the use of
several methods on the RMIClassLoader class, which then results in
calls to the configured and loaded instance of the RMIClassLoaderSpi
implementation. In containers and IDE environments, where
classloading is customized and managed very differently than what JSE
does by itself, getting an RMIClassLoaderSpi instance to be used
through the normal means, can be nearly impossible.
So, I am investigating not using the RMIClassLoaderSpi mechanisms that
are accessed through RMIClassLoader.
Instead, I have an interface defined (this is what I have so far) as
follows:
public interface CodebaseClassAccess {
public Class loadClass(String codebase, String name )
throws IOException, ClassNotFoundException;
public Class loadClass(String codebase, String name,
ClassLoader defaultLoader )
throws IOException,ClassNotFoundException;
public Class loadProxyClass(String codebase, String[] interfaceNames,
ClassLoader defaultLoader )
throws IOException,ClassNotFoundException;
public String getClassAnnotation( Class cls );
public ClassLoader getClassLoader(String codebase) throws
IOException;
public ClassLoader createClassLoader( URL[] urls,
ClassLoader parent,
boolean requireDlPerm,
AccessControlContext ctx );
public ClassLoader getParentContextClassLoader();
public ClassLoader getSystemContextClassLoader( ClassLoader
defaultLoader );
}
This is an SPI interface. There is a class with static methods that
mirrors this interface. That class has a setter on it to set the
implementation of this
interface to use. Without a call to the setter, the default is the
use of RMIClassLoaderSpi.
I have an implementation that I am using in my netbeans development
that is shown below.
There are three important issues that this covers.
1) It covers the fact that the use of null as a "default" parent
classloader is not going to work in many cases, because the jini/river
jars are likely not in the JVM system class loader.
2) My changes for allowing "never-preferred" classes to prohibit
downloading
jars until services will be activated, need to know what loader to
use for
non-preferred classes since it is not usually going to be the JVM
system
class loader.
3) Class.forName( name ) will only use the preferred class loader or
system
class loader and in some cases, there is more control needed.
In the end, I am making changes that start to allow other class
loading/packaging/platforming to be plugged into the
PreferredClassLoader. I'm doing this, because I think that if we want
Jini to work in other places, we have to be able to also honor the
ability to plugin Jini services that need preferred classes to work as
part of their bug fixing strategy.
I've got some stuff working now, but there is still more work to do to
provide a useful package to try out.
Gregg Wonderly
public class NetbeansCodebaseClassLoaderAccess
implements CodebaseClassAccess {
public static PreferredClassProvider provider = new
PreferredClassProvider();
public @Override ClassLoader getParentContextClassLoader() {
return Lookup.getDefault().lookup(ClassLoader.class);
}
public @Override Class loadClass( String codebase,
String name,
ClassLoader defaultLoader ) throws IOException,
ClassNotFoundException {
return provider.loadClass( codebase, name, defaultLoader );
}
public @Override Class loadProxyClass(String codebase, String[]
interfaceNames,
ClassLoader defaultLoader) throws IOException,
ClassNotFoundException {
return provider.loadProxyClass( codebase, interfaceNames,
defaultLoader );
}
public @Override String getClassAnnotation( Class cls ) {
return provider.getClassAnnotation( cls );
}
public @Override ClassLoader getClassLoader(String codebase)
throws IOException {
return provider.getClassLoader( codebase );
}
public @Override Class loadClass(String codebase, String name)
throws IOException, ClassNotFoundException {
return provider.loadClass( codebase, name,
Thread.currentThread().getContextClassLoader() );
}
public @Override ClassLoader createClassLoader( final URL[] urls,
final ClassLoader parent,
final boolean requireDlPerm, final AccessControlContext
ctx) {
return AccessController.doPrivileged(new
PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return new PreferredClassLoader(urls, parent,
null, requireDlPerm);
}
}, ctx );
}
public ClassLoader getSystemContextClassLoader( ClassLoader
defaultLoader ) {
// defaultLoader will be the PreferredClassLoader instance.
We need to just
// return that loader instead of the
defaultLoader.getClass().getClassLoader()
// business that usually occurs. That business would change
the class loading
// scope to completely remove all view of the system and the
codebase classes.
return defaultLoader != null ? defaultLoader :
Lookup.getDefault().lookup(ClassLoader.class);
}
}