|
|
EclipseプラグインからJavaのクラスを実行する(mainメソッドを呼び出す)為に、Eclipseの実行構成を利用する。
Eclipseの実行構成(デバッグ構成)では、クラスパスや実行するクラス・引数を指定して実行できる。
また、通常のアプリケーションとして実行する他に、JUnitやアプレットとして実行することが出来る。
依存プラグインは「org.eclipse.debug.core」。Eclipseにおけるアプリケーションの実行はデバッグ機能なんですなw
「org.eclipse.jdt.launching」も使う。
ワークスペース上のjava1というプロジェクト内のjp.hishidama.Exampleを呼び出す例。
呼び出し処理はIWorkspaceRunnableで記述することにする。
import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
public class LaunchTask implements IWorkspaceRunnable {
@Override
public void run(IProgressMonitor monitor) throws CoreException {
monitor.beginTask("launch実験", 100);
try {
ILaunchConfiguration config = createConfiguration(new SubProgressMonitor(monitor, 20));
launch(new SubProgressMonitor(monitor, 80), config, false);
} finally {
monitor.done();
}
}
private ILaunchConfiguration createConfiguration(IProgressMonitor monitor) throws CoreException {
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfigurationWorkingCopy config = type.newInstance(null, Activator.PLUGIN_ID);
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true);
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, true);
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, "java1");
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, "jp.hishidama.exampleClasspathProvider");
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, "jp.hishidama.Example");
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, "");
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, "abc def ghi");
return config;
}
DebugPluginを用いて実行情報を定義する。
typeには普通のアプリケーションを指定する。(他にもアプレットのIDとかが用意されている)
それから、実行情報として、対象プロジェクト・クラスパス・クラス名・VM引数・実行時引数を指定する。(引数に「${〜}」そのものは渡せないので注意)
クラスパス以外は文字列なので見れば分かるだろう。
クラスパスの定義方法は独特なので、後述。
private void launch(IProgressMonitor monitor, ILaunchConfiguration config, boolean debugMode) throws CoreException {
monitor.beginTask("launch", 100);
try {
String mode = debugMode ? ILaunchManager.DEBUG_MODE : ILaunchManager.RUN_MODE;
boolean build = false;
boolean register = false;
ILaunch launch = config.launch(mode, new SubProgressMonitor(monitor, 20), build, register);
if (!launch.hasChildren()) {
throw new OperationCanceledException();
}
monitor.worked(10);
while (!launch.isTerminated()) {
checkCancel(monitor);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
launch.terminate();
throw new OperationCanceledException();
}
monitor.worked(1);
}
launch.terminate();
} finally {
monitor.done();
}
}
実行情報(config)のlaunchメソッドを呼び出すことで、実際に実行が開始される。
このメソッドはブロックされないので、制御がすぐに戻ってくる。
終了したかどうかをポーリングする必要がある。
modeは、デバッグモードかどうか。(「実行」と「デバッグ」の違いだと思われる)
buildは、実行前にビルドするかどうか。
registerは、実行構成を保存するかどうか。
launchを作ったら、中にプロセスがちゃんと存在しているかどうかを確認した方が良さそう。[2013-05-10]
というのは、編集途中(保存していない)のファイルがあるときに保存するかどうかのダイアログが出るのだが、キャンセルするとlaunchインスタンスは作られるが中のプロセスは存在しない状態(hasChildren()がfalse)になる為。
この場合、isTerminated()がずっとfalseのままなので、hasChildren()をチェックしないと無限ループになってしまう。
private static void checkCancel(IProgressMonitor monitor) {
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
}
}
環境変数はconfigに指定する。[2014-12-28]
import java.util.HashMap; import java.util.Map; import org.eclipse.debug.core.ILaunchManager;
ILaunchConfigurationWorkingCopy config = 〜;
Map<String, String> map = new HashMap<String, String>();
map.put("ENV_NAME", "value");
config.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, map);
クラスパス(ClasspathProvider)はextensionで定義する。
<extension
point="org.eclipse.jdt.launching.classpathProviders">
<classpathProvider
id="jp.hishidama.exampleClasspathProvider"
class="org.eclipse.jdt.launching.StandardClasspathProvider">
</classpathProvider>
</extension>
StandardClasspathProviderは、プロジェクト内のビルドパスで定義されているクラスパスを返すクラス。
自分で独自のクラスパスを指定したい場合は、StandardClasspathProviderを継承したクラスを作ってcomputeUnresolvedClasspath()をオーバーライドする。
呼び出すハンドラーは以下の様な感じになる。
import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.ui.progress.WorkbenchJob;
public class LaunchHandler extends AbstractHandler {
public Object execute(ExecutionEvent event) throws ExecutionException {
WorkbenchJob job = new WorkbenchJob("launch-job") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
LaunchTask task = new LaunchTask();
try {
task.run(monitor);
} catch (CoreException e) {
return e.getStatus();
}
return Status.OK_STATUS;
}
};
job.schedule();
return null;
}
}
WorkbenchJobは(Jobクラスと同様に)scheduleメソッドで実行する。
今回の例で実行されるクラス。何の変哲も無い。
package jp.hishidama; import java.util.Arrays;
public class Example {
public static void main(String[] args) throws InterruptedException {
System.out.println(Arrays.toString(args));
}
}
ところで、実行は出来たのだが、Eclipseのコンソール上に何も表示されなかったorz
→コンソールへ出力する方法
上記の方法では、Eclipseのコンソール上に何も出力されない。[2013-02-25]
どうも、LaunchManagerにlaunchを登録しないと表示されないようだ。
ILaunch launch = config.launch(mode, new SubProgressMonitor(monitor, 20), build, register);
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
launchManager.addLaunch(launch);
monitor.worked(10);
アドバイス: ashigeruさん
クラスパスプロバイダーのIDからクラスパスエントリー(クラスパスプロバイダーに含まれるライブラリー・jarファイル)を取得するのもランチャーを利用する。[2013-06-14]
import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.IRuntimeClasspathProvider; import org.eclipse.jdt.launching.JavaRuntime;
ILaunchManager launches = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = launches.getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfigurationWorkingCopy copy = type.newInstance(null, Activator.PLUGIN_ID);
copy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, "jp.hishidama.exampleClasspathProvider");
IRuntimeClasspathProvider provider = JavaRuntime.getClasspathProvider(copy);
IRuntimeClasspathEntry[] unresolved = provider.computeUnresolvedClasspath(copy);
// JREコンテナーを除外
List<IRuntimeClasspathEntry> unlist = new ArrayList<IRuntimeClasspathEntry>(unresolved.length);
for (IRuntimeClasspathEntry ce : unresolved) {
if (ce.getClasspathEntry().getEntryKind() == IClasspathEntry.CPE_CONTAINER && ce.getPath().toPortableString().contains("JRE_CONTAINER")) {
continue;
}
unlist.add(ce);
}
IRuntimeClasspathEntry[] cp = provider.resolveClasspath(unlist.toArray(new IRuntimeClasspathEntry[unlist.size()]), copy);
for (IRuntimeClasspathEntry ce : cp) {
String location = ce.getLocation(); //jarファイル等のパス
〜
}
launchでは、アプリケーションの引数や環境変数の値に「${」「}」
という文字そのものを渡すことは出来ない。[2014-12-28]
例えば「target/hadoopwork/${execution_id}」という文字列を引数として渡そうとすると、以下のような例外が発生する。
!MESSAGE Reference to undefined variable execution_id at org.eclipse.core.internal.variables.StringSubstitutionEngine.resolve(StringSubstitutionEngine.java:262) at org.eclipse.core.internal.variables.StringSubstitutionEngine.substitute(StringSubstitutionEngine.java:195) at org.eclipse.core.internal.variables.StringSubstitutionEngine.performStringSubstitution(StringSubstitutionEngine.java:87) at org.eclipse.core.internal.variables.StringVariableManager.performStringSubstitution(StringVariableManager.java:574) at org.eclipse.core.internal.variables.StringVariableManager.performStringSubstitution(StringVariableManager.java:350) at org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate.getProgramArguments(AbstractJavaLaunchConfigurationDelegate.java:511) at org.eclipse.jdt.launching.JavaLaunchDelegate.launch(JavaLaunchDelegate.java:63) at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:858) 〜
${}で囲まれた部分を変数とみなして置換しようとし、置換できる値が無い(execution_idという変数が定義されていない)からエラーということらしい。
${}をエスケープするような仕組みは提供されていない。詰んだorz
変数の置換を行うStringVariableManagerでは、拡張ポイントで変数を定義しておくことが出来る。
(IDynamicVariableResolverを使うには、「依存関係」の「必須プラグイン」で「org.eclipse.core.variables」を追加する必要がある)
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.variables.IDynamicVariable; import org.eclipse.core.variables.IDynamicVariableResolver;
public class MyVariableResolver implements IDynamicVariableResolver {
@Override
public String resolveValue(IDynamicVariable variable, String argument) throws CoreException {
return "value"; // 変数の値
}
}
<extension
point="org.eclipse.core.variables.dynamicVariables">
<variable
description="example"
name="execution_id"
resolver="com.example.MyVariableResolver"
supportsArgument="false">
</variable>
</extension>
拡張ポイントのvariableのname属性で定義した名前の変数が来ると、resolverのクラスが呼ばれる。
が、このresolveValue()で${}入りの値を返すと、ご丁寧にそれも置換しようとする。
したがって、「${execution_id}」を返すようにしても、以下のようなエラーになってしまう。詰んだorz
!MESSAGE Variable execution_id does not accept arguments at org.eclipse.core.internal.variables.StringSubstitutionEngine.performStringSubstitution(StringSubstitutionEngine.java:107) at org.eclipse.core.internal.variables.StringVariableManager.performStringSubstitution(StringVariableManager.java:574) at org.eclipse.core.internal.variables.StringVariableManager.performStringSubstitution(StringVariableManager.java:350) at org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate.getProgramArguments(AbstractJavaLaunchConfigurationDelegate.java:511) at org.eclipse.jdt.launching.JavaLaunchDelegate.launch(JavaLaunchDelegate.java:63) at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:858) 〜