Java/Reflection notes: Invoking a static main() method from a dinamically loaded class.

Maybe for some wild reason, your Java application will need to execute a pre launcher that won’t know about the Main class it’s supposed to invoke until it’s being executed. For example, you have distributed your Java application but you used pack200 to compress your jars, and your new application launcher will unpack everything, but up to this point, it can’t do an import of the main class, since the jar that contained it, wasn’t available to the virtual machine when java was invoked.

So, your launcher class, finishes unpacking your jars, adds the jars to the current classloader, and now you need to invoke your Main.main(String[] args).

This is how I managed to do it, using Java’s reflection mechanisms

[java]
public final void startMain(String[] args) {
//this will create a new class loader out of all jars available, see below on next
//code section of this blog post
addJars2Classpath();

try {
//Instantiate the main class, and execute it’s static main method
Class clazz = jarsClassloader.loadClass("com.mycomp.somepackage.Main");
Class[] argTypes = { args.getClass(), };
Object[] passedArgs = { args };
Method main = clazz.getMethod("main",argTypes);
main.invoke(null, passedArgs);
} catch (Exception e) {
//oh oh
e.printStackTrace();
}

} //startMain
[/java]

In case you’re interested in how to add new Jars to the classpath during runtime, this is how I managed to do it:

[java]
/**
* Adds all the newly available jars to the classpath
*/
public final void addJars2Classpath() throws Exception {
//Create a new class loader with all the jars.
Object[] jars = getFiles(getApplicationResourcesJavaFolder(), ".jar");
URL[] jarUrls = new URL[jars.length];

for (int i=0; i < jars.length; i++) {
URL jarURL = null;

try {
jarURL = new URL("jar:file:"+(String) jars[i]+"!/");
} catch (Exception e) {
//LOG.error("Bad URL for jar ("+jarFile+"):n"+e.toString()+" ("+jarURL+")n");
return;
}

jarUrls[i] = jarURL;
}

//and this guy here, is the classloader used to load
jarsClassloader = URLClassLoader.newInstance(jarUrls);
} //addJar2Classpath

/**
* Get a Object<String> array that contains the names of the files
* that end with ‘type’ on the given folderPath
*
* e.g
*
* String[] jarFiles = getFiles(".",".jar");
*
*/
public final Object[] getFiles(String folderPath, String type) {
File f = new File(folderPath);

String[] files = f.list();

Vector results = new Vector();

for (int i=0; i < files.length; i++) {
if (files[i].endsWith(type)) {
results.add((String) files[i]);
}
}

if (results.size() == 0)
return null;

return results.toArray();
} //getFiles
[/java]