SWT
SWTはなぜDLLが要らない(ように見える)のか
大昔のSWTはjarファイルとDLLファイル(Windowsの場合)とに分離しており、これをアプリケーションで使うにはアプリ自体にはjarを付属させ、dllファイルは「パス」に置くしかなかった(起動時にjava.library.pathを指定してもよいが)。しかし、現在のswtはswt.jarのみをアプリに付属させれば良いようになっている。
しかし当然のことながら、相変わらずDLLは必要なのであり、しかもこれはOS側がロードするものなので、それぞれのDLLはそれぞれ一つのファイルとなっていなければならない。一体どうやっているのか?
以下はswt3.7での検証結果である。
jarの中にすべてのdllが含まれている
例えば、win32-win32-x86用のswt.jarを展開してみると、以下のようなファイルが現れる。
2011/09/05 10:43 <DIR> . 2011/09/05 10:43 <DIR> .. 2011/06/13 20:51 187 external.xpt 2011/09/05 10:43 <DIR> META-INF 2011/09/05 10:43 <DIR> org 2011/05/26 19:13 53,248 swt-awt-win32-3735.dll 2011/05/26 19:13 118,784 swt-gdip-win32-3735.dll 2011/05/26 19:13 77,824 swt-webkit-win32-3735.dll 2011/05/26 19:13 61,440 swt-wgl-win32-3735.dll 2011/05/26 19:13 430,080 swt-win32-3735.dll 2011/05/26 19:13 77,824 swt-xulrunner-win32-3735.dll 2011/06/13 20:51 13 version.txt 8 個のファイル 819,400 バイト
このように、実はswt.jarのトップに必要なdllが格納された状態になっている。 これをどうやって実行時に「Windowsにロードさせる」だろうか?
DLLロードまでの経緯
ここで、実際にDLLがロードされるまでの経緯を追ってみる。これはswtのソースコードを見ればすぐにわかる。 例えば、org.eclipse.swt.awt.SWT_AWT.javaを見てみる。これは、SWTからSwingの機能を呼び出すためのクラスである。 冒頭に、以下の部分がある。
static synchronized void loadLibrary () { if (loaded) return; loaded = true; Toolkit.getDefaultToolkit(); /* * Note that the jawt library is loaded explicitly * because it cannot be found by the library loader. * All exceptions are caught because the library may * have been loaded already. */ try { System.loadLibrary("jawt"); } catch (Throwable e) {} Library.loadLibrary("swt-awt"); }
とりあえず、「System.loadLibrary("jawt")」は無視し、「Library.loadLibrary」("swt-awt")に注目する。 このLibraryとは、org.eclipse.swt.internal.Libraryであるが、このloadLIbraryを見ると、
public static void loadLibrary (String name) { loadLibrary (name, true); } .... public static void loadLibrary (String name, boolean mapName) { String prop = System.getProperty ("sun.arch.data.model"); //$NON-NLS-1$ if (prop == null) prop = System.getProperty ("com.ibm.vm.bitmode"); //$NON-NLS-1$ if (prop != null) { if ("32".equals (prop) && IS_64) { //$NON-NLS-1$ throw new UnsatisfiedLinkError ("Cannot load 64-bit SWT libraries on 32-bit JVM"); //$NON-NLS-1$ } if ("64".equals (prop) && !IS_64) { //$NON-NLS-1$ throw new UnsatisfiedLinkError ("Cannot load 32-bit SWT libraries on 64-bit JVM"); //$NON-NLS-1$ } } /* Compute the library name and mapped name */ String libName1, libName2, mappedName1, mappedName2; if (mapName) { String version = System.getProperty ("swt.version"); //$NON-NLS-1$ if (version == null) { version = "" + MAJOR_VERSION; //$NON-NLS-1$ /* Force 3 digits in minor version number */ if (MINOR_VERSION < 10) { version += "00"; //$NON-NLS-1$ } else { if (MINOR_VERSION < 100) version += "0"; //$NON-NLS-1$ } version += MINOR_VERSION; /* No "r" until first revision */ if (REVISION > 0) version += "r" + REVISION; //$NON-NLS-1$ } libName1 = name + "-" + Platform.PLATFORM + "-" + version; //$NON-NLS-1$ //$NON-NLS-2$ libName2 = name + "-" + Platform.PLATFORM; //$NON-NLS-1$ mappedName1 = mapLibraryName (libName1); mappedName2 = mapLibraryName (libName2); } else { libName1 = libName2 = mappedName1 = mappedName2 = name; } StringBuffer message = new StringBuffer(); /* Try loading library from swt library path */ String path = System.getProperty ("swt.library.path"); //$NON-NLS-1$ if (path != null) { path = new File (path).getAbsolutePath (); if (load (path + SEPARATOR + mappedName1, message)) return; if (mapName && load (path + SEPARATOR + mappedName2, message)) return; } /* Try loading library from java library path */ if (load (libName1, message)) return; if (mapName && load (libName2, message)) return; /* Try loading library from the tmp directory if swt library path is not specified */ String fileName1 = mappedName1; String fileName2 = mappedName2; if (path == null) { path = System.getProperty ("user.home"); //$NON-NLS-1$ File dir = new File (path, SWT_LIB_DIR); if ((dir.exists () && dir.isDirectory ()) || dir.mkdirs ()) { path = dir.getAbsolutePath (); } else { /* fall back to using the home dir directory */ if (IS_64) { fileName1 = mapLibraryName (libName1 + SUFFIX_64); fileName2 = mapLibraryName (libName2 + SUFFIX_64); } } if (load (path + SEPARATOR + fileName1, message)) return; if (mapName && load (path + SEPARATOR + fileName2, message)) return; } /* Try extracting and loading library from jar */ if (path != null) { if (extract (path + SEPARATOR + fileName1, mappedName1, message)) return; if (mapName && extract (path + SEPARATOR + fileName2, mappedName2, message)) return; } /* Failed to find the library */ throw new UnsatisfiedLinkError ("Could not load SWT library. Reasons: " + message.toString()); //$NON-NLS-1$ } }
前半は、与えられた名称をプラットフォームに合わせて適切な名称に変換している。この場合には「swt-awt」を「swt-awt-win32-3735」に変換しているものと思われる。後半はそれをロードするわけであるが、まずは、swt.library.pathや通常のjavaライブラリパスを優先してロードしてみる。
それが失敗すると、「user.home」のSWT_LIB_DIRからロードしようとする。このSWT_LIB_DIRはファイルの先頭で「SWT_LIB_DIR = ".swt" + SEPARATOR + "lib" + SEPARATOR + os() + SEPARATOR + arch();」などと定義されている。 例えば、Windows 7(32ビット)でユーザ名がfooの場合には、「c:\users\foo\.swt\lib\win32\x86」となる。
それにも失敗すると、
/* Try extracting and loading library from jar */ if (path != null) { if (extract (path + SEPARATOR + fileName1, mappedName1, message)) return; if (mapName && extract (path + SEPARATOR + fileName2, mappedName2, message)) return; }
として、jarファイルからdllを抜き出してそれをロードするが、これはextract,loadメソッドを見ればわかるが単純な処理である。 単にjar中のリソースとしてdllファイルをオープンし、先のパスのファイルとしてコピーし、それをSystem.loadLibraryでロードしている。
Java Web Startの場合
以上は、swtを使用するスタンドアロンアプリの場合だが、Java Web Startの場合でも全く同じになる。 通常、Java Web Startにネイティブライブラリを含めるには、nativelibというタグを使わなくてはならないが、swtの場合にはあたかもネイティブライブラリ無しのjarファイルであるかのように扱ってよい。
<resources os="Windows" arch="x86"> <jar href="lib/win32-win32-x86/swt.jar"/> </resources>
もちろん、swt.jarが署名されていることが前提である。署名しなければ、、「c:\users\foo\.swt\lib\win32\x86」のようなディレクトリに書きこむことはできず、そもそもネイティブライブラリの呼び出し自体ができない。