Java Web Start
以下では特にJava Web Startを使用してSWTアプリケーションを配布する際の問題について調査する。
SWTは動作対象とする各プラットフォームに応じて、必要となるjarファイルが異なる。 つまり、windows用のjarファイルとlinux用のjarファイルは違う。これはSWTに各プラットフォーム用のネイティブライブラリを含むからである。
SWTアプリケーションを配布する方法
How to deploy SWT Applications using Java Web Startに記述がある。要するに、
http://www.eclipse.org/swtで配布されている各プラットフォーム用のzipファイルを展開してその中のswt.jarファイルをswt-native-<ws>-<os>-<arch>.jarの形に名称変更する。
- それらのjarファイルに署名する。
- jnlpファイルを記述する。以下のように、同じWindowsであっても、アーキテクチャによって異なるものになる。
<resources os="Windows" arch="x86">
<nativelib href="swt-native-win32-windows-x86.jar" />
<jar href="swt-win32-windows-x86.jar" />
</resources>
<resources os="Windows" arch="x86_64">
<nativelib href="swt-native-win32-windows-x86_64.jar" />
<jar href="swt-win32-windows-x86_64.jar" />
</resources>
<resources os="Windows" arch="amd64">
<nativelib href="swt-native-win32-windows-amd64.jar" />
<jar href="swt-win32-windows-x86_64.jar" />
</resources>しかし、SWTでのプラットフォームの切り分け方とJNLPでのそれとは異なるフォーマットになっている。つまり、SWTでは「<ws>-<os>-<arch>」となっているが、JNLLPでは「os="?" arch="?"」である。この対応表のようなものはどこにも見つからない。
<ws>-<os>-<arch>の意味
これはosgiのプラットフォームの定義方法らしい。wsは「window system」の略である。 eclipse側ではこの定義に従おうとしているが、そもそものjavaでは従っていない。そして、例えば実行中のjvm環境がこの定義に即すと何であるかが、SWT環境では検出できなくてはならないため、(基本的には)javaのSystem.getPropertyの値を変換している。これは、eclipse3.7の環境だと、org.eclipse.core.runtime.internal.adaptor.EclipseEnvironmentInfoで行われている。
/* */ package org.eclipse.core.runtime.internal.adaptor;
/* */
/* */ import java.io.PrintStream;
/* */ import java.util.Locale;
/* */ import java.util.NoSuchElementException;
/* */ import java.util.StringTokenizer;
/* */ import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
/* */ import org.eclipse.osgi.service.environment.EnvironmentInfo;
/* */ import org.eclipse.osgi.util.NLS;
/* */
/* */ public class EclipseEnvironmentInfo
/* */ implements EnvironmentInfo
/* */ {
/* */ private static EclipseEnvironmentInfo singleton;
/* */ private static String nl;
/* */ private static String os;
/* */ private static String ws;
/* */ private static String arch;
/* */ private static volatile String[] allArgs;
/* */ private static volatile String[] frameworkArgs;
/* */ private static volatile String[] appArgs;
/* */ private static final String INTERNAL_OS_SUNOS = "SunOS";
/* */ private static final String INTERNAL_OS_LINUX = "Linux";
/* */ private static final String INTERNAL_OS_MACOSX = "Mac OS";
/* */ private static final String INTERNAL_OS_AIX = "AIX";
/* */ private static final String INTERNAL_OS_HPUX = "HP-UX";
/* */ private static final String INTERNAL_OS_QNX = "QNX";
/* */ private static final String INTERNAL_OS_OS400 = "OS/400";
/* */ private static final String INTERNAL_OS_OS390 = "OS/390";
/* */ private static final String INTERNAL_OS_ZOS = "z/OS";
/* */ private static final String INTERNAL_ARCH_I386 = "i386";
/* */ private static final String INTERNAL_AMD64 = "amd64";
/* */
/* */ private EclipseEnvironmentInfo()
/* */ {
/* 53 */ setupSystemContext();
/* */ }
/* */
/* */ public static EclipseEnvironmentInfo getDefault() {
/* 57 */ if (singleton == null)
/* 58 */ singleton = new EclipseEnvironmentInfo();
/* 59 */ return singleton;
/* */ }
/* */
/* */ public boolean inDevelopmentMode() {
/* 63 */ return (FrameworkProperties.getProperty("osgi.dev") != null);
/* */ }
/* */
/* */ public boolean inDebugMode() {
/* 67 */ return (FrameworkProperties.getProperty("osgi.debug") != null);
/* */ }
/* */
/* */ public String[] getCommandLineArgs() {
/* 71 */ return allArgs;
/* */ }
/* */
/* */ public String[] getFrameworkArgs() {
/* 75 */ return frameworkArgs;
/* */ }
/* */
/* */ public String[] getNonFrameworkArgs() {
/* 79 */ return appArgs;
/* */ }
/* */
/* */ public String getOSArch() {
/* 83 */ return arch;
/* */ }
/* */
/* */ public String getNL() {
/* 87 */ return nl;
/* */ }
/* */
/* */ public String getOS() {
/* 91 */ return os;
/* */ }
/* */
/* */ public String getWS() {
/* 95 */ return ws;
/* */ }
/* */
/* */ private static void setupSystemContext()
/* */ {
/* 108 */ nl = FrameworkProperties.getProperty("osgi.nl");
/* 109 */ if (nl != null) {
/* 110 */ StringTokenizer tokenizer = new StringTokenizer(nl, "_");
/* 111 */ int segments = tokenizer.countTokens();
/* */ try {
/* 113 */ Locale userLocale = null;
/* 114 */ switch (segments)
/* */ {
/* */ case 1:
/* 117 */ userLocale = new Locale(tokenizer.nextToken(), "");
/* 118 */ break;
/* */ case 2:
/* 120 */ userLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken());
/* 121 */ break;
/* */ case 3:
/* 123 */ userLocale = new Locale(tokenizer.nextToken(), tokenizer.nextToken(), tokenizer.nextToken());
/* 124 */ break;
/* */ default:
/* 127 */ System.err.println(NLS.bind(EclipseAdaptorMsg.error_badNL, nl));
/* 128 */ userLocale = Locale.getDefault();
/* */ }
/* */
/* 131 */ Locale.setDefault(userLocale);
/* */
/* 133 */ FrameworkProperties.setProperty("osgi.nl.user", nl);
/* */ }
/* */ catch (NoSuchElementException localNoSuchElementException) {
/* */ }
/* */ }
/* 138 */ nl = Locale.getDefault().toString();
/* 139 */ FrameworkProperties.setProperty("osgi.nl", nl);
/* */
/* 143 */ os = FrameworkProperties.getProperty("osgi.os");
/* 144 */ if (os == null) {
/* 145 */ os = guessOS(FrameworkProperties.getProperty("os.name"));
/* 146 */ FrameworkProperties.setProperty("osgi.os", os);
/* */ }
/* */
/* 151 */ ws = FrameworkProperties.getProperty("osgi.ws");
/* 152 */ if (ws == null) {
/* 153 */ ws = guessWS(os);
/* 154 */ FrameworkProperties.setProperty("osgi.ws", ws);
/* */ }
/* */
/* 159 */ arch = FrameworkProperties.getProperty("osgi.arch");
/* 160 */ if (arch == null) {
/* 161 */ String name = FrameworkProperties.getProperty("os.arch");
/* */
/* 163 */ if (name.equalsIgnoreCase("i386")) {
/* 164 */ arch = "x86";
/* */ }
/* 166 */ else if (name.equalsIgnoreCase("amd64"))
/* 167 */ arch = "x86_64";
/* */ else
/* 169 */ arch = name;
/* 170 */ FrameworkProperties.setProperty("osgi.arch", arch);
/* */ }
/* */ }
/* */
/* */ public static void setAllArgs(String[] allArgs)
/* */ {
/* 176 */ allArgs = allArgs;
/* */ }
/* */
/* */ public static void setAppArgs(String[] appArgs)
/* */ {
/* 181 */ appArgs = appArgs;
/* */ }
/* */
/* */ public static void setFrameworkArgs(String[] frameworkArgs)
/* */ {
/* 186 */ frameworkArgs = frameworkArgs;
/* */ }
/* */
/* */ public static String guessWS(String osName)
/* */ {
/* 191 */ if (osName.equals("win32"))
/* 192 */ return "win32";
/* 193 */ if (osName.equals("linux"))
/* 194 */ return "gtk";
/* 195 */ if (osName.equals("macosx"))
/* 196 */ return "cocoa";
/* 197 */ if (osName.equals("hpux"))
/* 198 */ return "motif";
/* 199 */ if (osName.equals("aix"))
/* 200 */ return "motif";
/* 201 */ if (osName.equals("solaris"))
/* 202 */ return "gtk";
/* 203 */ if (osName.equals("qnx"))
/* 204 */ return "photon";
/* 205 */ return "unknown";
/* */ }
/* */
/* */ public static String guessOS(String osName)
/* */ {
/* 211 */ if (osName.regionMatches(true, 0, "win32", 0, 3)) {
/* 212 */ return "win32";
/* */ }
/* 214 */ if (osName.equalsIgnoreCase("SunOS"))
/* 215 */ return "solaris";
/* 216 */ if (osName.equalsIgnoreCase("Linux"))
/* 217 */ return "linux";
/* 218 */ if (osName.equalsIgnoreCase("QNX"))
/* 219 */ return "qnx";
/* 220 */ if (osName.equalsIgnoreCase("AIX"))
/* 221 */ return "aix";
/* 222 */ if (osName.equalsIgnoreCase("HP-UX"))
/* 223 */ return "hpux";
/* 224 */ if (osName.equalsIgnoreCase("OS/400"))
/* 225 */ return "os/400";
/* 226 */ if (osName.equalsIgnoreCase("OS/390"))
/* 227 */ return "os/390";
/* 228 */ if (osName.equalsIgnoreCase("z/OS")) {
/* 229 */ return "z/os";
/* */ }
/* 231 */ if (osName.regionMatches(true, 0, "Mac OS", 0, "Mac OS".length()))
/* 232 */ return "macosx";
/* 233 */ return "unknown";
/* */ }
/* */
/* */ public String getProperty(String key) {
/* 237 */ return FrameworkProperties.getProperty(key);
/* */ }
/* */
/* */ public String setProperty(String key, String value) {
/* 241 */ return FrameworkProperties.setProperty(key, value);
/* */ }
/* */ }※FrameworkProperties.getPropertyは基本的にSystem.getPropertyと同じと考えてよいようだ。
JNLPのosとarchとlocale
JNLPのresources属性であるosとarchは上記のosgi定義とは全く無関係である。 このため、基本的には対応しようとするプラットフォームごとにos,archを調べて指定する必要がある。 これは基本的には、System.getProperty()に"os.name", "os.arch"を指定した値と思われる。
ただし、注意すべき点としては、os,arch,localeとして指定される文字列は、空白で区切って複数指定することがでkりう。 このため、os="Windows Linux"と記述すると、「Windows XP」「Windows 7」「Linux」などに一致する。 正確に「Windows 7」に一致させるためには、「Windows\ 7」と指定しなければならない。
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7014170を参照のこと。
JNLPファイル内のJARリソースが単一の証明書によって署名されていません(resources in JNLP are not signed by same certificate)
ひどいことに、例えばSWTアプリをJWSによって配布しようとすると、このエラーが発生することがある。これは以下の二つの条件が揃ったときに発生する。
既に署名済のjarファイルに自分の署名を行った。
javaアクティベーションのactivation.jar、JavaMailのmail.jar、最近のeclipseのライブラリ(core,jfaceなど)はすべて署名がされている。そのまま署名すると、以前の署名は保持されたままた署名してしまうため、複数の署名が存在することになる。JREのバージョンが古い。
以前のJREでは、このエラーメッセージ通り、JNLPファイルから参照されるjarファイルはすべて単一の署名でなければならなかった。しかし、最近の少なくともJRE6u27では複数の署名があっても受け入れるように修正されている(しかし、この修正についてのドキュメントは見つからないので、単なるバグかも)。
jarを再署名する
http://www.jot.fm/issues/issue_2008_05/column2/index.html
ようするに、jarをほぐして、署名を削除し、再度jarを構成し、最後にjarsignerで署名するという方法。
その他のオプション
既に署名されているものをextensionとしてロードする方法もあるようだ(動作は未確認)。
http://download.oracle.com/javase/1,5.0/docs/guide/javaws/developersguide/faq.html#213
