Java SWTを使用したGUI。私はANTスクリプト(以下のフラグメント)を使用してアプリケーションをパッケージ化しています。
<jar destfile="./build/jars/swtgui.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class" value="org.swtgui.MainGui" />
<attribute name="Class-Path" value="." />
</manifest>
<fileset dir="./build/classes" includes="**/*.class" />
<zipfileset excludes="META-INF/*.SF" src="lib/org.Eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar" />
</jar>
これにより単一のjarが生成され、WindowsではダブルクリックしてGUIを実行できます。欠点は、Windows SWTパッケージをjarに明示的にパッケージ化する必要があったことです。
他のプラットフォーム(主にLinuxおよびOS X)でアプリケーションを実行できるようにしたいと考えています。これを行う最も簡単な方法は、適切なSWTファイルを個別のJARにパッケージ化したプラットフォーム固有のjarを作成することです。
これを行うより良い方法はありますか?複数のプラットフォームで実行される単一のJARを作成することは可能ですか?
SWT FAQ から参照される動作中の実装があります。
このアプローチは現在、ANTタスクとして使用できます。 SWTJar
[編集] SWTJarが更新され、上記のAlexey Romanovのソリューションを使用するようになりました。
まず、すべてのアプリケーションクラスを含むjarを作成します。
<!-- UI (Stage 1) -->
<jarjar jarfile="./build/tmp/intrace-ui-wrapper.jar">
<fileset dir="./build/classes" includes="**/shared/*.class" />
<fileset dir="./build/classes" includes="**/client/gui/**/*.class" />
<zipfileset excludes="META-INF/*.MF" src="lib/miglayout-3.7.3.1-swt.jar"/>
</jarjar>
次に、以下のすべてを含むようにjarを作成します。
これがbuild.xmlのフラグメントです。
<!-- UI (Stage 2) -->
<jarjar jarfile="./build/jars/intrace-ui.jar">
<manifest>
<attribute name="Main-Class" value="org.intrace.client.loader.TraceClientLoader" />
<attribute name="Class-Path" value="." />
</manifest>
<fileset dir="./build/classes" includes="**/client/loader/*.class" />
<fileset dir="./build/tmp" includes="intrace-ui-wrapper.jar" />
<fileset dir="./lib" includes="swt-*.jar" />
<zipfileset excludes="META-INF/*.MF" src="lib/jar-in-jar-loader.jar"/>
</jarjar>
このローダークラスは、jar-in-jar-loaderを使用して、2つのjarからクラスをロードするClassLoaderを作成します。
このクラスローダーを取得したら、リフレクションを使用して実際のアプリケーションのメインメソッドを起動できます。
public class TraceClientLoader
{
public static void main(String[] args) throws Throwable
{
ClassLoader cl = getSWTClassloader();
Thread.currentThread().setContextClassLoader(cl);
try
{
try
{
System.err.println("Launching InTrace UI ...");
Class<?> c = Class.forName("org.intrace.client.gui.TraceClient", true, cl);
Method main = c.getMethod("main", new Class[]{args.getClass()});
main.invoke((Object)null, new Object[]{args});
}
catch (InvocationTargetException ex)
{
if (ex.getCause() instanceof UnsatisfiedLinkError)
{
System.err.println("Launch failed: (UnsatisfiedLinkError)");
String Arch = getArch();
if ("32".equals(Arch))
{
System.err.println("Try adding '-d64' to your command line arguments");
}
else if ("64".equals(Arch))
{
System.err.println("Try adding '-d32' to your command line arguments");
}
}
else
{
throw ex;
}
}
}
catch (ClassNotFoundException ex)
{
System.err.println("Launch failed: Failed to find main class - org.intrace.client.gui.TraceClient");
}
catch (NoSuchMethodException ex)
{
System.err.println("Launch failed: Failed to find main method");
}
catch (InvocationTargetException ex)
{
Throwable th = ex.getCause();
if ((th.getMessage() != null) &&
th.getMessage().toLowerCase().contains("invalid thread access"))
{
System.err.println("Launch failed: (SWTException: Invalid thread access)");
System.err.println("Try adding '-XstartOnFirstThread' to your command line arguments");
}
else
{
throw th;
}
}
}
private static ClassLoader getSWTClassloader()
{
ClassLoader parent = TraceClientLoader.class.getClassLoader();
URL.setURLStreamHandlerFactory(new RsrcURLStreamHandlerFactory(parent));
String swtFileName = getSwtJarName();
try
{
URL intraceFileUrl = new URL("rsrc:intrace-ui-wrapper.jar");
URL swtFileUrl = new URL("rsrc:" + swtFileName);
System.err.println("Using SWT Jar: " + swtFileName);
ClassLoader cl = new URLClassLoader(new URL[] {intraceFileUrl, swtFileUrl}, parent);
try
{
// Check we can now load the SWT class
Class.forName("org.Eclipse.swt.widgets.Layout", true, cl);
}
catch (ClassNotFoundException exx)
{
System.err.println("Launch failed: Failed to load SWT class from jar: " + swtFileName);
throw new RuntimeException(exx);
}
return cl;
}
catch (MalformedURLException exx)
{
throw new RuntimeException(exx);
}
}
private static String getSwtJarName()
{
// Detect OS
String osName = System.getProperty("os.name").toLowerCase();
String swtFileNameOsPart = osName.contains("win") ? "win" : osName
.contains("mac") ? "osx" : osName.contains("linux")
|| osName.contains("nix") ? "linux" : "";
if ("".equals(swtFileNameOsPart))
{
throw new RuntimeException("Launch failed: Unknown OS name: " + osName);
}
// Detect 32bit vs 64 bit
String swtFileNameArchPart = getArch();
String swtFileName = "swt-" + swtFileNameOsPart + swtFileNameArchPart
+ "-3.6.2.jar";
return swtFileName;
}
private static String getArch()
{
// Detect 32bit vs 64 bit
String jvmArch = System.getProperty("os.Arch").toLowerCase();
String Arch = (jvmArch.contains("64") ? "64" : "32");
return Arch;
}
}
[編集]上記のように、「jar-in-jarクラスローダー」を探している人のために:EclipseのJDT(Java IDE Eclipse)。org.Eclipse.jdt.ui_ * version_number * .jarをアーカイバーで開くと、ファイルjar-in-jar-loader.Zipが中にありますこれをjar-in-jar-loader.jarに名前変更しました。
intrace-ui.jar -これは、上記のプロセスを使用して構築したjarです。この単一のjarは、win32/64、linux32/64、およびosx32/64のいずれでも実行できるはずです。
[編集]この回答は現在、 SWT FAQ から参照されています。
同じ問題が発生しました。まだ試していませんが、すべてのプラットフォームにswt.jar
のバージョンを含め、main
メソッドの先頭に動的に正しいバージョンをロードする予定です。
更新:うまくいきました。 build.xml
にはすべてのjarが含まれます:
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_linux_gtk_x86.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_macosx_x86.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_win32_x86.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_linux_gtk_x64.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_macosx_x64.jar"/>
<zipfileset dir="/home/aromanov/workspace/foo/lib" includes="swt_win32_x64.jar"/>
そして私のmain
メソッドはこれを呼び出すことから始まります:
private void loadSwtJar() {
String osName = System.getProperty("os.name").toLowerCase();
String osArch = System.getProperty("os.Arch").toLowerCase();
String swtFileNameOsPart =
osName.contains("win") ? "win32" :
osName.contains("mac") ? "macosx" :
osName.contains("linux") || osName.contains("nix") ? "linux_gtk" :
""; // throw new RuntimeException("Unknown OS name: "+osName)
String swtFileNameArchPart = osArch.contains("64") ? "x64" : "x86";
String swtFileName = "swt_"+swtFileNameOsPart+"_"+swtFileNameArchPart+".jar";
try {
URLClassLoader classLoader = (URLClassLoader) getClass().getClassLoader();
Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addUrlMethod.setAccessible(true);
URL swtFileUrl = new URL("rsrc:"+swtFileName); // I am using Jar-in-Jar class loader which understands this URL; adjust accordingly if you don't
addUrlMethod.invoke(classLoader, swtFileUrl);
}
catch(Exception e) {
throw new RuntimeException("Unable to add the SWT jar to the class path: "+swtFileName, e);
}
}
[編集]「jar-in-jarクラスローダー」をお探しの方へ:EclipseのJDTに含まれています(Java IDE Eclipse上に構築)。) org.Eclipse.jdt.ui_*version_number*.jar
とアーカイバを組み合わせると、ファイルjar-in-jar-loader.Zip
が中に見つかります。
すべてを単一のjarファイルにまとめてjar-in-jarを使用するつもりがない場合は、デプロイされたアプリケーションのlibディレクトリに各ターゲットプラットフォームの名前付きSWT jarを含めることで、この問題を解決することもできます。
_lib/swt_win_32.jar
lib/swt_win_64.jar
lib/swt_linux_32.jar
lib/swt_linux_64.jar
_
実行時にJavaシステムプロパティ_"os.name"
_および_"os.Arch"
_を調べ、System.getProperty(String name)
を使用して正しいjarファイル名を作成します。 。
次に、通常は保護されているメソッドURLClassloader.addURL(URL url)
を呼び出して、最初のSWTクラスが必要になる前に、システムクラスローダーのクラスパスに正しいjarを追加することで、少しいたずらなリフレクションを使用できます(OO純粋主義者は今すぐ目をそらします!)。
コードのにおいに耐えられる場合は、ここに実際の例を示します http://www.chrisnewland.com/select-correct-swt-jar-for-your-os-and-jvm-at- runtime-191
ここでのすべての回答が、すべてのSWT JARを単一の巨大なアプリケーションJARファイルにパッケージ化するようアドバイスしているのは非常に奇妙です。私見、これはSWTの目的に厳密に反しています:各プラットフォームにSWTライブラリがあるため、各プラットフォームに適切なSWTライブラリのみをパッケージ化することになっています。これは非常に簡単です。ANTビルドで、win32、win64、linux32、linux64、mac64の5つのビルドプロファイルを定義するだけです(mac32も実行できますが、最新のMacはすべて64ビットです)。
とにかく、OSにアプリケーションを適切に統合したい場合は、OS固有の処理をいくつか行う必要があり、ビルドプロファイルを再び使用します。デスクトップアプリケーションの場合、開発者とユーザーの両方のすべてのプラットフォームに1つのアプリパッケージを用意するのは不便です。
Src = "lib/org.Eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar"内の太字で選択されたテキストを、linuxで指定されたswt jarファイルで置き換えます