Java Tip 70: Create objects from jar files!

Combine a custom ClassLoader with a JarResources manager to instantiate classes stored in jar files

In Java Tip 49, (coauthored with Arthur Choi) we learned how to extract Java resources from jar (Java Archive) and zip archives, and in Jack Harich’s Java Tip 39, we learned how to create custom class loaders to load class files via the local filesystem or the network. In this new Java Tip, we’re going to put that knowledge to work, loading classes from jar files.

Look Ma, no hands!

Given the capabilities provided for us by Jack’s MultiClassLoader and Arthur’s JarResources classes, creating a new class loader to load class files from jar archives is about as easy as programming gets:

public class JarClassLoader extends MultiClassLoader
    {
    private JarResources    jarResources;
    public JarClassLoader (String jarName)
    {
    // Create the JarResource and suck in the jar file.
    jarResources = new JarResources (jarName);
    }
    protected byte[] loadClassBytes (String className)
    {
    // Support the MultiClassLoader's class name munging facility.
    className = formatClassName (className);
    // Attempt to get the class data from the JarResource.
    return (jarResources.getResource (className));
    }
    }

The JarClassLoader subclasses MultiClassLoader, inheriting the tricky bits usually associated with writing a class loader. It uses an instance of the JarResources class to extract the jar file contents. So, all we have to do to make JarClassLoader useful is to implement the loadClassBytes() method hook from MultiClassLoader.

We create the instance of JarResources in the constructor (passing along the name of the jar archive file). We then use that jarResources object to extract the databytes representing the requested class file out of the jar archive.

Work it!

Now, let’s put our sweat equity to work, by creating a demo, or test application. In this case, we’ll create our test application within a static class nested within the JarClassLoader class:

    /*
     * Internal Testing application.
     */
    public static class Test
    {
    public static void main(String[] args) throws Exception
        {
        if (args.length != 2)
        {
        System.err.println
            ("Usage: java JarClassLoader " +
             "<jar file name> <class name>");
        System.exit (1);
        }
        /*
         * Create the jar class loader and use the first argument
         * passed in from the command line as the jar file to use.
         */
        JarClassLoader jarLoader = new JarClassLoader (args [0]);
        /* Load the class from the jar file and resolve it. */
        Class c = jarLoader.loadClass (args [1], true);
        /*
         * Create an instance of the class.
         *
         * Note that created object's constructor-taking-no-arguments
         * will be called as part of the object's creation.
         */
        Object o = c.newInstance();
        /* Are we using a class we specifically know about? */
        if (o instanceof TestClass)
        {
        // Yep, lets call a method  we know about.  */
        TestClass tc = (TestClass) o;
        tc.doSomething();
        }
        }
    }   // End of nested Class Test.

The comments in the code are pretty self-explanatory, but there’s an extra trick I want to highlight: At the end of the test application, we use the instanceof operator to test the created object to see if it’s a class we want to do something special with. For testing purposes, I created a simple, do-almost-nothing class called TestClass.java, which our test application knows about. If the test application is told to test for TestClass and actually finds that class in the specified jar file, it will invoke the doSomething() method. Otherwise, if the test application is given any other class, all it does is create an object of that class.

For the complete source code for this Java Tip, see Resources. There you’ll find the test.jar file that I’ve created to facilitate running the test application. It contains the TestClass.class and Howdy.class files. Howdy.class comes from <a href="https://images.techhive.com/downloads/idge/imported/article/jvw/1999/03/howdy.java">Howdy.java</a>, which is just a simple class that does nothing but print out a message when it’s constructed.

After compiling the JarClassLoader.java file, you can run this test application using something like:

% java 'JarClassLoader$Test' test.jar Howdy

and

% java JarClassLoader$Test test.jar TestClass

Note the quotation marks. They’re there because the dollar sign ($) is used to indicate variables in many command-line shell programs (like csh, tcsh, and so on). Regardless of which quoting method works for you on your system, be sure to try running it with both Howdy and TestClass to see the differences.

Conclusion

There you have it! You can now load and instantiate classes from Java archive files. I’ll leave you with one homework assignment: Modify this group of classes so you can load and instantiate the classes from jar files, which you download over the network rather than from local files.

John D. Mitchell is the chief technology
officer (CTO) of the MageLang
Institute. He coauthored Making Sense of Java: A Guide for
Managers and has published articles in programming journals.
In addition to writing the Java Tips column for
JavaWorld, he moderates the
comp.lang.tcl.announce and comp.binaries.geos
newsgroups.

Source: www.infoworld.com