在Java程序中执行另一个jar文件

138
我曾经编写了几个名为A.jar、B.jar的简单Java应用程序。
现在我想编写一个GUI Java程序,以便用户可以按下按钮A来执行A.jar并按下按钮B来执行B.jar。
另外,我想在我的GUI程序中输出运行时进程的详细信息。
有什么建议吗?

41
不确定为什么这个被投票否决。向他解释他的假设可能是错误的,但不要将问题投票否决。 - William Brendel
-1 基本的Java术语没有被正确使用,问题非常模糊,留下了很多需要猜测的空间。请考虑用更多的细节重新表述你的问题,或者先阅读关于Java类路径和其他基础知识的内容。 - topchef
24
@grigory:看,这就是你应该在一开始就问的问题,而不是马上点踩。没有询问更多信息就点踩没有任何好处... - William Brendel
4
@William,抱歉我比你晚按了几秒钟的按钮。我保留我的投票权并附带评论来表示反对。看到没有基本准备和/或努力去理解或呈现问题的问题很令人沮丧。对我来说,投票就像在餐厅里给小费:根据服务质量,你可以比标准的12%多或少给一些小费。所以让我们同意在这个问题上持不同意见。 - topchef
11
@topchef,我晚了11年才来看到这条消息,但你说“看到没有基本准备就提出问题令人沮丧...”。那就帮助解决它。解释一下为什么你要点踩。否则,你根本没有帮助解决问题。 - tylerr147
8个回答

85

如果我理解正确,您似乎希望在Java GUI应用程序中从单独的进程中运行jar文件。

要实现这一点,您可以使用:

// Run a java app in a separate system process
Process proc = Runtime.getRuntime().exec("java -jar A.jar");
// Then retreive the process output
InputStream in = proc.getInputStream();
InputStream err = proc.getErrorStream();

最好的实践方法是缓冲进程的输出。


21
这个解决方案是否可移植? - Pacerier
3
谢谢。我尝试了带有3个参数的exec()函数,其中包括“dir”参数,但它无法工作。只需要一个参数时,我们只需将3个.jar文件放在同一位置即可。 - Duc Tran
你知道如何获取那个Java应用程序的Runtime对象吗? - jobukkit
我们能否使用更便携的版本,使用ProcessBuilder? - Anand Rockzz
这个是可以工作的,但是如果主项目被关闭了,那么 A.jar 也会被关闭。 - Wang Liang
显示剩余2条评论

33

.jar文件不是可执行的。你需要实例化类或者调用静态方法。

编辑: 在创建.jar文件时添加Main-Class条目。

>p.mf (p.mf文件的内容)

Main-Class: pk.Test

>Test.java

package pk;
public class Test{
  public static void main(String []args){
    System.out.println("Hello from Test");
  }
}

使用 Process 类及其方法。

public class Exec
{
   public static void main(String []args) throws Exception
    {
        Process ps=Runtime.getRuntime().exec(new String[]{"java","-jar","A.jar"});
        ps.waitFor();
        java.io.InputStream is=ps.getInputStream();
        byte b[]=new byte[is.available()];
        is.read(b,0,b.length);
        System.out.println(new String(b));
    }
}

3
我认为这个想法是:在获得一个jar包的访问权限后,如何将它添加到类路径中以便从中加载类。 - Jherico
1
注意:它只能工作是因为你在PATH中有Java bin,这不适用于默认的Windows安装。 - poussma
@AVD:A.jar文件应该放在哪里? - logan
1
@logan - 在加载程序的目录中。 - KV Prajapati
这种方法会创建一个新的JVM进程还是使用现有的进程? - RahulDeep Attri

22
希望这能帮到你:
public class JarExecutor {

private BufferedReader error;
private BufferedReader op;
private int exitVal;

public void executeJar(String jarFilePath, List<String> args) throws JarExecutorException {
    // Create run arguments for the

    final List<String> actualArgs = new ArrayList<String>();
    actualArgs.add(0, "java");
    actualArgs.add(1, "-jar");
    actualArgs.add(2, jarFilePath);
    actualArgs.addAll(args);
    try {
        final Runtime re = Runtime.getRuntime();
        //final Process command = re.exec(cmdString, args.toArray(new String[0]));
        final Process command = re.exec(actualArgs.toArray(new String[0]));
        this.error = new BufferedReader(new InputStreamReader(command.getErrorStream()));
        this.op = new BufferedReader(new InputStreamReader(command.getInputStream()));
        // Wait for the application to Finish
        command.waitFor();
        this.exitVal = command.exitValue();
        if (this.exitVal != 0) {
            throw new IOException("Failed to execure jar, " + this.getExecutionLog());
        }

    } catch (final IOException | InterruptedException e) {
        throw new JarExecutorException(e);
    }
}

public String getExecutionLog() {
    String error = "";
    String line;
    try {
        while((line = this.error.readLine()) != null) {
            error = error + "\n" + line;
        }
    } catch (final IOException e) {
    }
    String output = "";
    try {
        while((line = this.op.readLine()) != null) {
            output = output + "\n" + line;
        }
    } catch (final IOException e) {
    }
    try {
        this.error.close();
        this.op.close();
    } catch (final IOException e) {
    }
    return "exitVal: " + this.exitVal + ", error: " + error + ", output: " + output;
}
}

这个答案假设'java'在路径上是可用的。如果不可用呢? - Queeg

2
以下是启动JAR文件的批处理文件,用于独立运行程序:

以下作品通过使用批处理文件启动JAR文件,在程序作为独立运行时运行:

public static void startExtJarProgram(){
        String extJar = Paths.get("C:\\absolute\\path\\to\\batchfile.bat").toString();
        ProcessBuilder processBuilder = new ProcessBuilder(extJar);
        processBuilder.redirectError(new File(Paths.get("C:\\path\\to\\JavaProcessOutput\\extJar_out_put.txt").toString()));
        processBuilder.redirectInput();
        try {
           final Process process = processBuilder.start();
            try {
                final int exitStatus = process.waitFor();
                if(exitStatus==0){
                    System.out.println("External Jar Started Successfully.");
                    System.exit(0); //or whatever suits 
                }else{
                    System.out.println("There was an error starting external Jar. Perhaps path issues. Use exit code "+exitStatus+" for details.");
                    System.out.println("Check also C:\\path\\to\\JavaProcessOutput\\extJar_out_put.txt file for additional details.");
                    System.exit(1);//whatever
                }
            } catch (InterruptedException ex) {
                System.out.println("InterruptedException: "+ex.getMessage());
            }
        } catch (IOException ex) {
            System.out.println("IOException. Faild to start process. Reason: "+ex.getMessage());
        }
        System.out.println("Process Terminated.");
        System.exit(0);
    }

在 batchfile.bat 中,我们可以这样说:
@echo off
start /min C:\path\to\jarprogram.jar

谢谢 @Dawood Morris,当您的jar文件需要相当大量的资源(如内存等)时,这真的很有帮助。 - UmeshPathak

1
如果JAR文件在您的类路径中,并且您知道它的主类,您可以直接调用主类。以DITA-OT为例:
import org.dita.dost.invoker.CommandLineInvoker;
....
CommandLineInvoker.main('-f', 'html5', '-i', 'samples/sequence.ditamap', '-o', 'test')

请注意,这将使下属的jar与您的jar共享内存空间和类路径,可能会导致干扰。如果您不想让这些内容污染,请使用其他选项,如上所述:
  • 创建一个包含该jar的新ClassLoader。这更加安全;如果您设计时考虑到将使用外部jar,则至少可以将新jar的知识隔离到核心ClassLoader中。这就是我们在公司为插件系统所做的,主应用程序是一个小型Shell,具有ClassLoader工厂、API副本和知道真正应用程序是第一个插件的知识,需要构建ClassLoader。插件是一对被一起压缩的接口和实现jar文件。所有ClassLoader共享所有接口,而每个ClassLoader只知道自己的实现。堆栈有点复杂,但通过了所有测试并且运行得很好。
  • 使用Runtime.getRuntime.exec(...)(完全隔离jar,但具有运行系统命令的常规“查找应用程序”、“正确转义字符串”、“特定于平台的WTF”和“OMG系统线程”陷阱)。

1

首先我们创建一个名为FirstFileOutput的类,其中包含一个输出一行到稳定输出和一个输出一行到稳定错误的主方法。在完成第一个过程后,我们将再次创建一个名为RuntimeExecCheck的类,该类将在启动进程时运行我们的FirstFileOutput类,然后RuntimeExecCheck类将从FirstFileOutput读取稳定输出和稳定错误,并输出结果。

package check;

public class FirstFileOutput{

    public static void main(String[] args) {
        System.out.println("This is output to stable output");
        System.err.println("This is output to stable error");
    }
}



package check;

import java.io.InputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class RuntimeExecCheck {

    public static void main(String[] args) {
        try {
            Runtime runTime = Runtime.getRuntime();
            Process process = runTime.exec("java -classpath C:\\projects\\workspace\\check\\bin check.FirstFileOutput");
            InputStream inputStream = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(inputStream);
            InputStream errorStream = process.getErrorStream();
            InputStreamReader esr = new InputStreamReader(errorStream);

            int n1;
            char[] c1 = new char[1024];
            StringBuffer stableOutput = new StringBuffer();
            while ((n1 = isr.read(c1)) > 0) {
                stableOutput.append(c1, 0, n1);
            }
            System.out.println("Stable Output: " + stableOutput.toString());

            int n2;
            char[] c2 = new char[1024];
            StringBuffer stableError = new StringBuffer();
            while ((n2 = esr.read(c2)) > 0) {
                stableError.append(c2, 0, n2);
            }
            System.out.println("Stable Error: " + stableError.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这个答案假设'java'在路径上是可用的。如果不可用呢? - Queeg

0
您可以使用以下代码:
    Process process = Runtime.getRuntime().exec(command_string);
    BufferedInputStream successBufferedInputStream = new BufferedInputStream(process.getInputStream());
    BufferedInputStream errorBufferedInputStream = new BufferedInputStream(process.getErrorStream());
    synchronized (process) {
        process.waitFor();
    }

    if (errorBufferedInputStream.available() != ZERO) {
        errorBufferedInputStream.close();
        System.out.println("error stream after jar execution");
    }

    if (process.exitValue() != ZERO) {
       System.out.println("error jar execution");
    }

    if (successBufferedInputStream.available() == ZERO) {
       System.out.println("error");
    }

    StringBuilder response = new StringBuilder();
    while ((successBufferedInputStream.available()) > ZERO) {
        response.append((char) successBufferedInputStream.read());
    }
    successBufferedInputStream.close();

-4
如果你使用的是Java 1.6版本,那么也可以进行以下操作:
import javax.tools.JavaCompiler; 
import javax.tools.ToolProvider; 

public class CompilerExample {

    public static void main(String[] args) {
        String fileToCompile = "/Users/rupas/VolatileExample.java";

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        int compilationResult = compiler.run(null, null, null, fileToCompile);

        if (compilationResult == 0) {
            System.out.println("Compilation is successful");
        } else {
            System.out.println("Compilation Failed");
        }
    }
}

4
我不认为这回答了原帖作者的问题。 - Sri Harsha Chilakapati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接