Loading Java Applications

Executing an application in Java is hard. When you work with a language that compiles to native code, the artifact created from your build process is a native executable. If you are using an interpreted language or a language that compiles to bytecode, a runtime environment is required to run the program.

Invoking the Java runtime is simple but for a typical enterprise application, many parameters are required to be passed to the runtime. These parameters consist of arguments to the virtual machine (like memory options), program parameters and classpath parameters.

For a large application, there are often many .jar files that need to be included on the classpath. The software author can create a shell script or small native executable that calls Java with all of the appropriate jar files.

A good alternative to having to specify all of the .jar files in the script is to use a simple boot loader jar. The following two classes make a very simple boot loader that can be used to start a much larger application.


public class ClassLoaderFactory {
    public static ClassLoader createClassLoader(File[] files) {
        List<URL> urlList = new ArrayList<URL>();

        for (File file: files) {
            addURLs(file, urlList);
        }
        URL[] urls = urlList.toArray(new URL[0]);
        return new URLClassLoader(urls, 
            ClassLoaderFactory.class.getClassLoader());
    }

    private static void addURLs(File file, List<URL> list) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                addURLs(files[i], list);
            }
        }
        else if (file.isFile() && file.getName().endsWith(".jar")) {
            try {
                list.add(file.toURI().toURL());
            }
            catch (MalformedURLException e) {
                // This should not happen. Ignore this file.
            }
        }
    }
}

public class Launcher {
    public static void main(String[] args) {
        String mainClass = null;
        List<File> libraryList = new ArrayList<File>();
        List<String> actualArgsList = new ArrayList<String>();

        for (String arg: args) {
            if (arg.startsWith("-l")) {
                String libraryPath = arg.substring(2);
                libraryList.add(new File(libraryPath));
            }
            else if (mainClass == null) {
                mainClass = arg;
            }
            else {
                actualArgsList.add(arg);
            }
        }

        if (mainClass == null) {
            System.err.println("No main class specified");
            System.exit(-1);
        }

        // Load classloader based on the library path
        ClassLoader classLoader = 
            ClassLoaderFactory.createClassLoader(
            libraryList.toArray(new File[0]));
        Thread.currentThread().setContextClassLoader(classLoader);

        // Execute the main class
        Class bootClass;
        try {
            bootClass = classLoader.loadClass(mainClass);
            Method main = bootClass.getMethod("main", 
                new Class[] {String[].class});
            main.invoke(null, new Object[] {
                actualArgsList.toArray(new String[0])});
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

You will also need to create a META-INF/MANIFEST.MF file along with these classes. This should specify which is the main class to be run. The contents should be as follows:

Manifest-Version: 1.0
Implementation-Title: XML Transport Bootloader
Main-Class: com.whitehill.bootstrap.Launcher

Those classes and manifest should be packaged into a single jar file called boot.jar. Note that these should be the only classes in boot.jar and it should not include any classes for your running application. If not, you may end up with some classloading issues.

Now to run your application, create the following directory structure

/ – Root directory where the application is run from. You can place a batch file or .exe to start the software here.
/lib – Directory to contain all the .jar files for the application. You can break it up into subdirectories for organizational purposes.

Now to run the application, you can simply run the following:

	java -jar boot.jar -llib com.softwareco.MainClass

This command will start the Java Virtual Machine and run the boot.jar application. This application parses the arguments “-llib com.softwareco.MainClass” and adds all .jar files in the lib directory to the classpath and runs the com.softwareco.MainClass of your application. This command can be placed into a script to make it easier for the user and include any JVM options.