Locked History Actions

java/jws

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