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