[jcifs] java.net.MalformedURLException further analysis

Glass, Eric eric.glass at capitalone.com
Thu Dec 12 21:51:38 EST 2002


> > which would further attempt to load the class using the 
> context classloader
> > for the current thread (which is set to the web application 
> classloader by
> > Tomcat).
> > 
> 	This is the one thing that I still do not quite 
> understand. A Resin user also
> 	reported a case just like Andrea's:
> 
> 	  
> http://lists.samba.org/pipermail/jcifs/2002-November/002799.html
> 
> 	So java.net.URL cannot see jcifs.smb.Handler because it 
> is only in the thread
> 	context classloader?


Sort of; it is possible to have a scenario where the jCIFS classes are not
available to the thread context classloader either.  I think you will see
issues anywhere the jCIFS classes are loaded by a classloader other than the
system classloader -- that is (generally speaking), anytime it is installed
somewhere not normally used to load classes by the VM.  For Sun's JVM, I
think the complete set of locations in which the system classloader will
look is comprised of the locations specified in the following system
properties:

    sun.boot.class.path (a list of locations)
    java.endorsed.dirs (a list of directories containing jar files)
    java.ext.dirs (a list of directories containing jar files)
    java.class.path (a list of locations)

The servlet issue is a special case of this.  You will probably see this in
most servlet containers if you attempt to install the jCIFS classes under
the application-specific classpath.

A typical scenario producing this issue might be:

a) The VM starts up, using the system classloader, which loads classes from
the "default" locations.  This is the classloader which will be used to load
the URL class.

b) During the preparation of the web application, the servlet container
creates a classloader for the application.  This classloader loads classes
from the jar files in the application's WEB-INF/lib directory (including in
this case the jCIFS jar) and the class hierarchy under WEB-INF/classes.

c) The servlets in the web application are loaded via this classloader and
initialized.

d) During a servlet's execution, assume it does:

    Class.forName("jcifs.Config");
    URL foo = new URL("smb://host/stuff");

e) The Class.forName call above loads the jcifs.Config class using the
defining classloader of the current class (the servlet class).  Since the
servlet class was loaded via the web application classloader, this is the
classloader used to load the Config class.

f) The URL class attempts to load "jcifs.smb.Handler" as mentioned in the
previous messages, i.e.

    Class.forName("jcifs.smb.Handler");

g) The Class.forName call above attempts to load the jcifs.smb.Handler class
using the defining classloader of the current class (the URL class).  Since
the URL class was loaded via the system classloader, this is the classloader
which is used to try and locate the Handler class (which will fail).

h) The URL class then attempts to load "jcifs.smb.Handler" explicitly using
the system classloader, which is redundant; this obviously fails as well,
and you get the MalformedURLException.


The jCIFS classes would be available to the thread context classloader if
the container explicitly set the context classloader of the thread
dispatching the request to the web application classloader; that is, the
container created a thread to handle an incoming request, and did something
like:

    Thread t = new ServiceThread();
    t.setContextClassLoader(webappClassLoader);
    t.start();

Adding an attempt to load from the thread context classloader to the URL
class would basically just give it another place to look in the event that
the necessary classes aren't available to the system classloader; it isn't
guaranteed that this location will have them either.

What would ideally be done is that the URL class would attempt to load
classes from the defining classloader of the *calling* class.  This approach
is used in ResourceBundle:

    /*
     * Automatic determination of the ClassLoader to be used to load
     * resources on behalf of the client.  N.B. The client is getLoader's
     * caller's caller.
     */
    private static ClassLoader getLoader() {
        Class[] stack = getClassContext();
        /* Magic number 2 identifies our caller's caller */
        Class c = stack[2];
        ClassLoader cl = (c == null) ? null : c.getClassLoader();
        if (cl == null) {
            cl = ClassLoader.getSystemClassLoader();
        }
        return cl;
    }

The "gotcha" being that getClassContext() above is a native method used to
access the calling stack -- this information is not normally available.

One approach to detecting the unavailability of the jCIFS classes to the URL
class would be to add something like the following to the static init block
of jcifs.Config:

    try {
        Class.forName("jcifs.smb.Handler", false,
                URL.class.getClassLoader());
    } catch (SecurityException ex) {
        // ignore
    } catch (ClassNotFoundException ex) {
        throw new IllegalStateException("The jCIFS SMB URL Handler is " +
                "not available to the URL class.");
    } catch (NoClassDefFoundError ex) {
        throw new IllegalStateException("The jCIFS SMB URL Handler is " +
                "not available to the URL class.");
    }


This would detect that the classloader used to load the URL class is not
able to access the jcifs.smb.Handler class.  For people like me, who use
jCIFS primarily for NTLM authentication, this would have an unfortunate
(albeit relatively minor) side effect; the NTLM filter apparently doesn't
currently depend on SMB URLs, and functions properly when installed under
the web application.  Preventing such a deployment would break this
(although the workaround would be fairly simple -- install jCIFS as an
extension library).  Another option would be to print a warning rather than
throwing an exception if a "bad" installation is detected.

Eric
 
 
**************************************************************************
The information transmitted herewith is sensitive information intended only
for use by the individual or entity to which it is addressed. If the reader
of this message is not the intended recipient, you are hereby notified that
any review, retransmission, dissemination, distribution, copying or other
use of, or taking of any action in reliance upon this information is
strictly prohibited. If you have received this communication in error,
please contact the sender and delete the material from your computer.



More information about the jcifs mailing list