質問

プラットフォームに依存しない方法で、場所ではなく名前を使用して、Javaアプリケーションを別のプロセスにロードできますか?

...経由でプログラムを実行できることを知っています

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

...このメソッドの主な問題は、そのような呼び出しがプラットフォーム固有であることです。


理想的には、メソッドを次のような単純なものにラップします...

EXECUTE.application( CLASS_TO_BE_EXECUTED );

...アプリケーションクラスの完全修飾名を CLASS_TO_BE_EXECUTED として渡します。

役に立ちましたか?

解決

2つのヒント:

System.getProperty(" java.home")+" / bin / java" は、Java実行可能ファイルへのパスを提供します。

((URLClassLoader)Thread.currentThread()。getContextClassLoader())。getURL()は、現在のアプリケーションのクラスパスを再構築するのに役立ちます。

その後、 EXECUTE.application は単なる(擬似コード)です:

Process.exec(javaExecutable、" -classpath&quot ;, urls.join("")、CLASS_TO_BE_EXECUTED)

他のヒント

これは、提供されている他の回答の一部を統合したものです。 Javaシステムのプロパティは、プラットフォームに依存しない方法で、javaコマンドへのパスとクラスパスを考え出すのに十分な情報を提供します。

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();
    }

}

次のようにこのメソッドを実行します。

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

クラスがクラスパスに存在しなければ動作しないため、名前の文字列表現ではなく実際のクラスを渡すのが理にかなっていると思いました。

@stepanchegの答えを展開すると、実際のコードは(テストの形式で)そのようになります。

import org.junit.Test;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.stream.Collectors;

public class SpinningUpAJvmTest {
    @Test
    public void shouldRunAJvm() throws Exception {
        String classpath = Arrays.stream(((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURLs())
                .map(URL::getFile)
                .collect(Collectors.joining(File.pathSeparator));
        Process process = new ProcessBuilder(
                System.getProperty("java.home") + "/bin/java",
                "-classpath",
                classpath,
                MyMainClass.class.getName()
                // main class arguments go here
        )
                .inheritIO()
                .start();
        int exitCode = process.waitFor();
        System.out.println("process stopped with exitCode " + exitCode);
    }
}

これはあなたにとってはやり過ぎかもしれませんが、 Project Akuma はあなたが望むことなどを行います。 Kohsukeのこのエントリで見つけましたSunのロックスタートプログラマーの)非常に有用なブログ。

ProcessBuilder APIをチェックアウトしましたか? 1.5以降で利用可能です

http://java.sun.com /javase/6/docs/api/java/lang/ProcessBuilder.html

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;
    }
}

本当にネイティブに起動する必要がありますか?彼らの「メイン」と呼んでもらえますか?直接メソッド? mainについての唯一の特別なことは、VMランチャーがそれを呼び出すことです。あなた自身がmainを呼び出すことを止めるものは何もありません。

TofuBeerが言わなければならなかったことに続いて、本当に別のJVMを分岐する必要があるのですか? JVMは最近の並行性を本当によくサポートしているので、新しいスレッドを1つまたは2つスピンオフするだけで、比較的安価で多くの機能を得ることができます(Foo#main(String [])への呼び出しが必要な場合とそうでない場合があります)。詳細については、java.util.concurrentをご覧ください。

分岐することにした場合、必要なリソースの検索に関連する複雑さを少し設定します。つまり、アプリが頻繁に変更され、多数のjarファイルに依存している場合、それらをすべて追跡してクラスパス引数に渡すことができるようにする必要があります。さらに、このようなアプローチでは、(現在実行中の)JVMの場所(正確ではない可能性があります)と現在のクラスパスの場所(生成される方法によっては正確である可能性はさらに低い)の両方を推測する必要がありますスレッドが呼び出されました-jar、jnlp、展開された.classes dir、コンテナなど)。

一方、静的な#mainメソッドへのリンクには落とし穴もあります。静的修飾子は、他のコードに漏れるという厄介な傾向があり、一般的にデザイン志向の人々によって嫌われています。

これをJava GUIから実行するときに発生する問題は、バックグラウンドで実行されることです。 そのため、コマンドプロンプトはまったく表示されません。

これを回避するには、" cmd.exe"を介してjava.exeを実行する必要があります。および「開始」。 理由はわかりませんが、" cmd / c start"実行中にコマンドプロンプトが表示されます。

ただし、" start"の問題アプリケーションへのパスにスペースがある場合 (java exeへのパスは通常、 C:\ Program Files \ Java \ jre6 \ bin \ java.exeまたは同様のもの)、 その後、" c:\ Program"が見つかりません

で開始が失敗します

C:\ Program Files \ Java \ jre6 \ bin \ java.exeを引用符で囲む必要があります 次に、java.exeに渡すパラメーターについての苦情を開始します。 "システムはファイル-cpを見つけることができません。"

" Program Files"のスペースのエスケープバックスラッシュを使用しても機能しません。 だから、アイデアはスペースを使用しないことです。 bat拡張子を持つ一時ファイルを生成し、そこにスペースを入れてコマンドを入れます バットを実行します。 ただし、開始からバットを実行しても、終了しても終了しません。 したがって、「終了」を入力する必要があります。バッチファイルの最後。

これはまだ不愉快なようです。

だから、代替案を探して、「プログラムファイル」のスペースで引用スペースを使用することを見つけました。実際にはstartで動作します。

上記のEXECUTEクラスで、文字列ビルダーの追加先を変更します。

append( "cmd /C start \"Some title\" " ).
append( java.lang.System.getProperty( "java.home" ).replaceAll(" ", "\" \"") ).
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 ).
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top