情報科学屋さんを目指す人のメモ

方法・手順・解説を書き残すブログ。私と同じことを繰り返さずに済むように。

Eclipseプラグインから外部プログラムを実行する方法

Eclipse (60) Eclipseプラグイン (3) Java (37)

Eclipseプラグインから、外部プログラムを呼び出し、その標準出力を専用のコンソールに表示する方法についてのメモをシェアします。実際の実装は、Remote Exec プラグインのソースを読んでみてください。

DebugPlugin.execを使う

Javaから外部プログラムを実行するには、Runtime.execやApache Commons Execのexecなどがあり、それを使うのも無理ではないはずです。

ただ、実はプラグイン用のexecみたいなのがあって、今回はそちらを使います。存在に気が付かないと、普通のexecを使ってしまいますよね。

JavaLaunchConfigurationDelegate#launch

今回は、Debug configurationやRun ConfigurationでLaunchをクリックしたときに起動することを動作を考えたいので、JavaLaunchConfigurationDelegateのlaunchメソッドで呼び出すことを考えます。

DebugPlugin.execの使い方

基本的に次のように使います。

DebugPlugin.exec(new String[]{"ping", "192.168.1.1"}, getWorkingDirectory(configuration));

execの一つ目の引数は、コマンドラインの配列なのですが、これはコマンドラインを複数配列に保存するのではなく、引数ごとに区切って配列にしろ、というものです。

そして、2つめの引数はWorkingDirectoryで、ここではgetWorkingDirectoryを使います。

エラー:Exception occurred executing command line.

次のエラーが出てしまうことがあります。

Exception occurred executing command line.
Cannot run program "ping 192.168.1.1": CreateProcess error=2, Žw’肳‚ꂽƒtƒ@

なぜか最後の部分が文字化けしてしまうのですが、これは「プログラム(実行ファイル)が見つからない」という意味のようです(参考:java.io.IOException)。

もちろん本当にプログラムが存在しなければそれが原因なのですが、見落としがちなのが、DebugPlugin.execの引数の書き方のミスです。コマンドライン部分のString配列を「new String[]{"ping 192.168.1.1"}」のように引数を分割せずに一続きに書くとエラーになってしまいます。最初の引数を実行ファイルへのパスだと思って探してしまうようです。自分はこれにハマりました。参考:When Runtime.exec() won't | JavaWorld

もう少し細かく挙動を観察すると、「new String[] {"program.exe", "-a", "-b c"}」と書いたとき、実行されるのは「"program.exe" "-a" "-b c"」のように区切られたもののようです。

実行結果を取得してConsoleに表示する

これだけだと、何が起こったかプラグイン側が今ひとつ把握できません(戻り値の取得くらいは簡単だけど)。

ちなみに、ここでSystem.outをしても、それはeclipse自体の標準出力に行ってしまうので、Consoleに表示される、eclipseから実行される側のプログラムとは勝手が違います。

そこで、専用のConsoleを作成し、そこに出力するようにしてみました。

		final String CONSOLE_NAME = "console-name";
		ConsolePlugin consolePlugin = ConsolePlugin.getDefault();
		IConsoleManager consoleManager = consolePlugin.getConsoleManager();
		IConsole[] consoles = consoleManager.getConsoles();
		MessageConsole console = null;
		for (IConsole console0 : consoles) {
			if (CONSOLE_NAME.equals(console0.getName())) {
				console = (MessageConsole) console0;
				break;
			}
		}
		if (console == null) {
			console = new MessageConsole(CONSOLE_NAME, "consoleType", null, null, false);
			consoleManager.addConsoles(new IConsole[]{console});
		}
		
		final MessageConsoleStream out = console.newMessageStream();
		
		Process rawProc = DebugPlugin.exec(new String[]{"ping", "192.168.1.1"}, getWorkingDirectory(configuration));
		
		IProcess proc = DebugPlugin.newProcess(launch, rawProc, null);
		proc.getStreamsProxy().getOutputStreamMonitor().addListener(new IStreamListener() {
			@Override
			public void streamAppended(String text, IStreamMonitor monitor) {
				out.println(text);
			}
		});

参考にしてみてください。

コメント(0)

新しいコメントを投稿