2

Possible Duplicate:
Execute another jar in a java program

Basically I want to run an external .jar from the one I'm working on now.

I.e. I want to run foo.jar from bar.jar

I've tried using Runtime and Process to execute "java -jar foo.jar", but it opens foo.jar and then it closes immediately. Any tips?

Community
  • 1
  • 1
2kan
  • 95
  • 2
  • 10

3 Answers3

3

The easiest solution (as Thorn pointed out) would be to have the jar as a build-time dependency and invoke it statically from your code:

ExternalJarMainClass.main(new String[]{"arguments", "to", "main"});

But if that is not possible, you can use a URLClassLoader to load the jar dynamically. If the jar is indeed runnable, then you can read the main class from META-INF/MANIFEST.MF and invoke main via reflection.

This is a different approach from creating a separate process, as the external code will run in the same process as your application. Perhaps this is desirable, perhaps not - that depends on the situation.

Below's a (hastily written and flawed) sample helper class that does just that.

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JarRunner {

    private final Method entryPoint;

    public JarRunner(File jarFile) throws
            ClassNotFoundException,
            IOException,
            NoSuchMethodException {
        URL jarUrl = jarFile.toURI().toURL();
        URLClassLoader loader = URLClassLoader.newInstance(
                new URL[]{jarUrl});
        URL manifestUrl = loader.findResource("META-INF/MANIFEST.MF");
        String manifest = resourceToString(manifestUrl);
        Class<?> clazz = loader.loadClass(findMainClassName(manifest));
        entryPoint = clazz.getMethod("main", String[].class);
    }

    public void run(String[] argsToMain) throws
            IllegalAccessException,
            IllegalArgumentException,
            InvocationTargetException {
        entryPoint.invoke(null, (Object) argsToMain);
    }

    private static String resourceToString(URL url) throws IOException {
        InputStream contentStream = url.openStream();
        try {
            BufferedReader r = new BufferedReader(
                    new InputStreamReader(contentStream));
            StringBuilder sb = new StringBuilder();
            String line = null;
            do {
                line = r.readLine();
                if (line != null) {
                    sb.append(line).append('\n');
                }
            } while (line != null);
            return sb.toString();
        } finally {
            contentStream.close();
        }
    }

    private static String findMainClassName(String manifest) {
        Matcher m = MAIN_CLASS_PATTERN.matcher(manifest);
        if (m.find()) {
            return m.group(1);
        }
        return null;
    }

    private static final Pattern MAIN_CLASS_PATTERN = 
            Pattern.compile("Main-Class: (.+)");
}

Sample usage:

JarRunner jr = new JarRunner(new File("path/to/MyJar.jar"));
jr.run(new String[]{"arg1", "arg2"});
Community
  • 1
  • 1
perp
  • 3,883
  • 4
  • 31
  • 29
2

Can you run foo.jar directly? Does it have a manifest with a main method?

I am guessing that you can. So you want to launch the main method inside of a class like foo.Main

Option 1: Include foo.jar in the classpath. If you are using an IDE, then this just means adding foo.jar as a library. Now you are free to import the package (lets call the package foo) and launch your second java program from a single line of Java code: foo.Main.main(null); Most likely you would want to do this in a separate thread:

class FooRunner extends Thread { 
   public void run() { 
       foo.Main.main(null);
   } 
}

and then you would launch with this:

FooRunner secondaryApp = new FooRunner();
secondaryApp.start();

Option 2 You can load the classes in the Foo package at runtime using a class loader. See the Javadocs for java.lang.ClassLoader and this example of a CustomClassLoader

Thorn
  • 4,015
  • 4
  • 23
  • 42
1

Check java -jar foo.jar runs correctly from command line. Also ensure java is there in the path. It may be better to provide absolute path to java.exe in the arguments.

Please consider using ProcessBuilder instead of Runtime.

Jayan
  • 18,003
  • 15
  • 89
  • 143