HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

There have been a lot of new APIs and facilities added to Java 1.1 that have received a lot of fanfare: the Java Database Connectivity API, the new event model, JavaBeans and so on. A lot of new functionality has also been added very quietly, probably because it's not quite as glamorous. I'd like to spend this month's column talking about one of these quiet new additions to Java, one that I have found to be very useful. This new addition is the new "resource" method added to the Java 1.1 ClassLoader class.

Java 1.0's ClassLoader class allows you to do one thing: load .CLASS files into the Java VM. The ClassLoader was a great thing, but I found it difficult to use for one major reason: you couldn't easily load resources associated with classes. For example, I wrote a hierarchical tree class and I wanted a couple of .GIF files to be available for use by my class. (One image is an "expanded node" indicator and the other is a "contracted node" indicator.)

Well, in Java 1.0 it was rather difficult to write classes which are dependant on image resources. I had to write a lot of code that detected if my hierarchical list was part of an applet or an application, and to construct URLs for my image files as best I could.

All that tortuous programming is gone in Java 1.1. Using the new ClassLoader "resource" methods, you can load arbitrary resource files as easily as you load .CLASS files. Take a look at Listing 1. This code loads an image file named "Example.gif" from the local file system. The listing uses the ClassLoader static class method loadSystemResource(). This method works by looking on the local file system for a file named "Example.gif" anywhere in the local system's classpath.

If you think about it, you can see how system classes (classes loaded from the local file system) are loaded. The class name first gets converted to a .CLASS file name. For example, the class name "com.maso.MyClass" gets converted to "com\maso\MyClass.class". The new string is nothing .more than a URL, or relative path name of a file. The second step is to use the static ClassLoader class method, getSystemResource(), to locate the .CLASS file. This will search the classpath for the given file. Once found, the third step is to load the .CLASS file into the Java VM. So, a system resource is any data file found along the system's class path, which usually includes .CLASS files.

Listing 2 shows how the same data files are loaded from within an applet. Within an applet environment, when you don't know what files are located on the local system's classpath you must use a different ClassLoader class method to load resource files. The getResource() method is an abstract method in the ClassLoader class, because it is up to each individual ClassLoader how to search for named resources.

The AppletViewer's AppletClassLoader uses this technique to find resource files when its getResource() method is called.

  • p First, the static getSystemResource() method is called to see if the resource file lives on the local file system. (Note that this is a deviation from Java 1.0's strict security. Using the AppletViewer's Applet-ClassLoader, now you can load arbitrary data files from any directory in the classpath from the local file system. In Java 1.0 you couldn't load anything but classes from the local file system.)
  • Second, any JAR files associated with the applet are searched for the resource. JAR files, essentially, are zip archives. The new ARCHIVE field of the APPLET tags in HTML is used to specify a list of JAR files that include Java classes and other data resources available to the applet.
  • Third, the applet's codebase (the URL where the applet was served from) is searched.
The first and third step existed in Java 1.0. That is, Java 1.0 worked the same as if the second step listed above were removed. So, the image files being loaded in Listing 2 can be located in one of three different places to be loaded corrected by the code: on the local file system (in the classpath), within a JAR file downloaded from the HTTP server or in the same directory as the applet .CLASS file on the HTTP server.

The big trick of this month's column is listed in Listing 3, which is the code listing of a utility class I like to use in my applications and applets in Java 1.1. The loadPackageResource() method of this class is able to load a data file associated with a given class and package, no matter what the context the code is running in. That is, the method will work just as well if is called from within an application (Listing 1), or an applet (Listing 2). For that matter, this method should also work if it is being called from other, more exotic contexts such as within a server-side Servlet, a JavaBean within an ActiveX/JavaBean bridge in a Visual Basic program, etc.

The loadPackageResource() method works by first determining the package name of the reference class. It is assumed that a package resource will be located in a directory named for the package the reference class is in. That is, if I asked the loadPackageResource() method to load a file named "Example.gif", using a reference class "com.maso.MyClass", then I would expect the loadPackageResource() method to look for a resource with the full name "com\maso\Example.gif". I want the method to locate the file using the same search technique used to locate the .CLASS file for the class MyClass.

I find the class shown in Listing 3 to be extremely useful now. It is probably one of the greatest optimizations in my Java code that I have experienced in converting to Java 1.1 from Java 1.0. No longer do I have to write complex class or context-specific code to load images (and different code to load audio files or arbitrary binary data files). Instead, I just call my trusty loadPackageResource() with a reference to the class that wants to use the resource data.

Listing 4 is a short excerpt of code demonstrating how the loadPackageResource() method would be used. This listing shows how a HierarchicalTree class could load its expanded and contracted node images. This same code will work regardless of whether the hierarchical list is being created within an applet or an application.

Note: Last month I challenged readers to come up with examples of using Java exceptions to optimize or make better Java implementations. Well, I haven't received anything yet. I encourage you readers to send in tricks of any sort, which I'll be sure to pass along (with proper attribution, of course).

About the Author
Brian Maso is a programming consultant working out of Portland, OR. He is the co-author of The Waite Group Press's upcoming release, "The Java API SuperBible." Before Java, he spent five years corralled in the MS Windows branch of programming, working for such notables as the Hearst Corp., first DataBank, and Intel. Readers are encouraged to contact Brian with any comments or questions.

	

Listing 1: Loading any resource from the local system requires 1) getting a URL
using getSystemResource(); and 2) getting the content of the URL.

…
try {
    URL urlImage = ClassLoader.getSystemResource(“Example.gif”);
    Image img = urlImage.getContent();
} catch (NullPointerException npe) {
    // No such resource found in the classpath…
} catch (IOException ioe) {
    // problem reading from the file, but file was found.
}
…

Listing 2: Loading a resource from a ClassLoader, such as in an applet context, 
uses pretty much the same two steps as in Listing 1, but using different method calls.

…
try {
    ClassLoader appletCL = getClass().getClassLoader();
    URL urlImage = appletCL.getResource(“Example.gif”);
    Image img = urlImage.getContent();
} catch (NullPointerException npe) {
    // Either local class’ classl loader is the System class loader,
    // or the image resource could not be found by the applet class loader.
} catch (IOException ioe) {
    // problem reading the resource
}
…

Listing 3: The PackageResources class is a utility class with a single static method: 
getPackageResource(). This method is made to work in either an application or an applet context.

public class PackageResources {
    public static Object getPackageResource(String strResource, Class refClass) {
        ClassLoader cl = refClass.getClassLoader();

        String strPackageName = refClass.getName();
        int i = strPackageName.lastIndexOf(‘.’);
        if(i == -1)
            strPackageName = “”;
        else
            strPackageName = strPackageName.substring(i);

        String str = (strPackageName.length()>0 ? strPackageName + “/” : “”) + strResource;

        if(null == cl)
            return ClassLoader.getSystemResource(str);
        return cl.getResource(str);
    }
}

Listing 4: This code snippet shows how you would use the getPackageResource() method 
in Listing 3 to load an image.

…
Image img = PackageResources.getPackageResource(“Example.gif”, getClass());
…


 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.