Executing a Java application in a separate process

2019-01-02 20:27发布

Can a Java application be loaded in a separate process using its name, as opposed to its location, in a platform independent manner?

I know you can execute a program via ...

Process process = Runtime.getRuntime().exec( COMMAND );

... the main issue of this method is that such calls are then platform specific.


Ideally, I'd wrap a method into something as simple as...

EXECUTE.application( CLASS_TO_BE_EXECUTED );

... and pass in the fully qualified name of an application class as CLASS_TO_BE_EXECUTED.

9条回答
素衣白纱
2楼-- · 2019-01-02 21:02
public abstract class EXECUTE {

    private EXECUTE() { /* Procedural Abstract */ }

    public static Process application( final String CLASS_TO_BE_EXECUTED ) {

        final String EXEC_ARGUMENT 
        = new StringBuilder().
              append( java.lang.System.getProperty( "java.home" ) ).
              append( java.io.File.separator ).
              append( "bin" ).
              append( java.io.File.separator ).
              append( "java" ).
              append( " " ).
              append( new java.io.File( "." ).getAbsolutePath() ).
              append( java.io.File.separator ).
              append( CLASS_TO_BE_EXECUTED ).
              toString();

        try {       

            return Runtime.getRuntime().exec( EXEC_ARGUMENT );

        } catch ( final Exception EXCEPTION ) {     

            System.err.println( EXCEPTION.getStackTrace() );
        }

        return null;
    }
}
查看更多
与风俱净
3楼-- · 2019-01-02 21:04

Two hints:

System.getProperty("java.home") + "/bin/java" gives you a path to the java executable.

((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURL() helps you to reconstruct the classpath of current application.

Then your EXECUTE.application is just (pseudocode):

Process.exec(javaExecutable, "-classpath", urls.join(":"), CLASS_TO_BE_EXECUTED)

查看更多
梦醉为红颜
4楼-- · 2019-01-02 21:04

Do you really have to launch them natively? Could you just call their "main" methods directly? The only special thing about main is that the VM launcher calls it, nothing stops you from calling main yourself.

查看更多
梦寄多情
5楼-- · 2019-01-02 21:04

Following on what TofuBeer had to say: Are you sure you really need to fork off another JVM? The JVM has really good support for concurrency these days, so you can get a lot of functionality for relatively cheap by just spinning off a new Thread or two (that may or may not require calling into Foo#main(String[])). Check out java.util.concurrent for more info.

If you decide to fork, you set yourself up for a bit of complexity related to finding required resources. That is, if your app is changing frequently and depends upon a bunch of jar files, you'll need to keep track of them all so that they can be passed out to the classpath arg. Additionally, such an approach requires to to infer both the location of the (currently executing) JVM (which may not be accurate) and the location of the current classpath (which is even less likely to be accurate, depending upon the way that the spawning Thread has been invoked - jar, jnlp, exploded .classes dir, some container, etc.).

On the other hand, linking into static #main methods has its pitfalls as well. static modifiers have a nasty tendency of leaking into other code and are generally frowned upon by design-minded folks.

查看更多
皆成旧梦
6楼-- · 2019-01-02 21:11

This might be an overkill for you, but Project Akuma does what you want and more. I found it via this entry at Kohsuke's (one of Sun's rock start programmers) fabulously useful blog.

查看更多
十年一品温如言
7楼-- · 2019-01-02 21:17

This is a synthesis of some of the other answers that have been provided. The Java system properties provide enough information to come up with the path to the java command and the classpath in what, I think, is a platform independent way.

public final class JavaProcess {

    private JavaProcess() {}        

    public static int exec(Class klass) throws IOException,
                                               InterruptedException {
        String javaHome = System.getProperty("java.home");
        String javaBin = javaHome +
                File.separator + "bin" +
                File.separator + "java";
        String classpath = System.getProperty("java.class.path");
        String className = klass.getName();

        ProcessBuilder builder = new ProcessBuilder(
                javaBin, "-cp", classpath, className);

        Process process = builder.inheritIO().start();
        process.waitFor();
        return process.exitValue();
    }

}

You would run this method like so:

int status = JavaProcess.exec(MyClass.class);

I thought it made sense to pass in the actual class rather than the String representation of the name since the class has to be in the classpath anyways for this to work.

查看更多
登录 后发表回答