deployJava.js をマニアックに読み解く 第二章

deployJava.js を読むシリーズ、今回は二回目です。
長編なので全三回で構成しています。


deployJava.js も徐々にコアな部分に入っていきます。

339|      /**
340|       * Returns true if there is a matching JRE version currently installed 
341|       * (among those detected by getJREs()).  The versionPattern string is 
342|       * of the form #[.#[.#[_#]]][+|*], which includes strings such as "1.4", 
343|       * "1.5.0*", and "1.6.0_02+".  
344|       * A star (*) means "any version within this family" and a plus (+) means 
345|       * "any version greater or equal to the specified version".  "1.5.0*"
346|       * matches 1.5.0_06 but not 1.6.0_01, whereas "1.5.0+" matches both.
347|       *
348|       * If the versionPattern does not include all four version components 
349|       * but does not end with a star or plus, it will be treated as if it 
350|       * ended with a star.  "1.5" is exactly equivalent to "1.5*", and will 
351|       * match any version number beginning with "1.5".
352|       *
353|       * If getJREs() is unable to detect the precise version number, a match 
354|       * could be ambiguous.  For example if getJREs() detects "1.5", there is 
355|       * no way to know whether the JRE matches "1.5.0_06+".  versionCheck() 
356|       * compares only as much of the version information as could be detected, 
357|       * so versionCheck("1.5.0_06+") would return true in in this case.
358|       *
359|       * Invalid versionPattern will result in a JavaScript error alert.  
360|       * versionPatterns which are valid but do not match any existing JRE 
361|       * release (e.g. "32.65+") will always return false.
362|       */
363|     versionCheck: function(versionPattern)
364|     {
365|         var index = 0;
366|         var regex = "^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(?:_(\\d+))?)?)?(\\*|\\+)?$";
367| 
368|         var matchData = versionPattern.match(regex);
369| 
370|         if (matchData != null) {
371|             var familyMatch = true;
372| 
373|             var patternArray = new Array();
374| 
375|             for (var i = 1; i < matchData.length; ++i) {
376|                 // browser dependency here.
377|                 // Fx sets 'undefined', IE sets '' string for unmatched groups
378|                 if ((typeof matchData[i] == 'string') && (matchData[i] != '')) {
379|                     patternArray[index] = matchData[i];
380|                     index++;
381|                 }
382|             }
383| 
384|             if (patternArray[patternArray.length-1] == '+') {
385|                 familyMatch = false;
386|                 patternArray.length--;
387|             } else {
388|                 if (patternArray[patternArray.length-1] == '*') {
389|                     patternArray.length--;
390|                 }
391|             }
392| 
393|             var list = deployJava.getJREs();       
394|             for (var i = 0; i < list.length; ++i) {
395|                 if (deployJava.compareVersionToPattern(list[i], patternArray, 
396|                                                        familyMatch)) {
397|                     return true;
398|                 }
399|             }
400|   
401|             return false;
402|         } else {
403|             alert('Invalid versionPattern passed to versionCheck: ' + 
404|                   versionPattern);
405|             return false;
406|         }
407|     },

versionCheck() メソッドです。
コメントには・・・
現在インストールされている JRE のバージョンが引数 versionPattern にマッチしたら true をリターンする。
バージョン文字列の構成部分が4つ含まれてなく、最後が "*" でも "+" でもない場合は、それを "*" で終わっているものとして扱う。
というように書かれています。

versionPattern に指定できる書式についての説明は installJRE() メソッドと同じようですね。微妙に違っていますが言いたいことは同じなんだろうと思います。
それから getJREs() メソッドが返す値が正確でない場合の注意と、versionPattern の値が不正だった場合にエラーメッセージが表示されることが説明されています。


365 - 366行目で変数 index の初期化と、引数 versionPattern をチェックして構成部分を抽出する為の正規表現を作成しています。
368行目で versionPattern を match() でチェックして、結果を変数 matchData に格納します。


370 - 401行目は versionPattern が正規表現にマッチした場合の処理です。
371行目で変数 familyMatch を true に初期化します。これはデフォルトでバージョン文字列が "*" で終わっているように扱う為のフラグです。
373 - 382行目は正規表現のチェック結果から、4つの (\\d+) と最後の (\\*|\\+)? にマッチした部分を変数 patternArray に移し変えています。
例を示すと、以下のような状態になります。


 versionPattern が "1.5.0_01" のとき
 matchData は [1.5.0_01, 1, 5, 0, 01, undefined]
 patternArray は [1.5.0_01, 1, 5, 0, 01]


同様に
 versionPattern が "1.5" のとき
 matchData は [1.5*, 1, 5, undefined, undefined,*]
 patternArray は [1.5*, 1, 5, *]

※例は Firefox での結果。IE では undefined の部分が空文字列になる。


384 - 391行目では patternArray の最後の要素に "*" か "+" が格納されている場合、配列長を一つ分小さくしてそれらを削除しています。
その中で "+" だった場合は、familyMatch に false をセットしてデフォルトのフラグを変更しています。
この処理で patternArray の各要素にはバージョンを構成する数字だけが格納されることになります。


393行目で getJREs() メソッドを呼び出してインストールされている JRE のバージョンのリストを取得しています。
394 - 399行目で、取得したバージョンと、patternArray、familyMatch を引数として compareVersionToPattern() メソッドを呼び出して、必要な JRE のバージョンが存在するかをチェックします。
存在していれば compareVersionToPattern() メソッドは true をリターンし、その結果 versionCheck() メソッドも true をリターンします。

401行目では、必要な JRE のバージョンが存在していない場合 false をリターンします。


402 - 405行目は versionPattern が正規表現にマッチしなかった場合の処理で、エラーメッセージを表示して false をリターンします。

410|     /**
411|      * Returns true if an installation of Java Web Start of the specified 
412|      * minimumVersion can be detected.  minimumVersion is optional, and 
413|      * if not specified, '1.4.2' will be used. 
414|      * (Versions earlier than 1.4.2 may not be detected.)
415|      */
416|     isWebStartInstalled: function(minimumVersion) {
417| 
418|         var browser = deployJava.getBrowser();
419|         if ((browser == '?') || (browser == 'Safari')) {
420|             // we really don't know - better to try to use it than reinstall
421|             return true;
422|         }
423| 
424|         if (minimumVersion == 'undefined' || minimumVersion == null) {
425|             minimumVersion = '1.4.2';
426|         }
427| 
428|         var retval = false;
429|         var regex = "^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(?:_(\\d+))?)?)?$";
430|         var matchData = minimumVersion.match(regex);
431| 
432|         if (matchData != null) {
433|             retval = deployJava.versionCheck(minimumVersion + '+');
434|         } else {
435|             if (deployJava.debug) {
436|                 alert('Invalid minimumVersion argument to isWebStartInstalled(): ' + minimumVersion);
437|             }
438|             retval = deployJava.versionCheck('1.4.2+');
439|         }
440|         return retval;
441|     },

isWebStartInstalled() メソッドです。
コメントには・・・
引数 minimumVersion で指定された Java Web Start のインストールが見つかれば true をリターンする。
minimumVersion はオプションで、指定されない場合は "1.4.2" をデフォルトとして使用する。
("1.4.2" より前のバージョンは見つけられないかも知れない)
というように書かれています。


418 - 422行目では getBrowser() メソッドを呼び出して、現在のブラウザが不明なブラウザか Safari の場合、true をリターンしています。
サポートしないブラウザに対して、ここで true をリターンしている理由はよく解りませんでした。(ご存知の方がいたら教えて下さい)
420行目のコメントの意味は、「再インストールするよりもつかって試してみるのが良いのか - 実のところ我々も知らない」ということでしょうか。


424 - 426行目は minimumVersion が省略された場合のデフォルトの値をセットしています。


428 - 430行目は変数の宣言と、minimumVersion の書式をチェックする為の正規表現を作成しています。


432 - 440行目では、minimumVersion の書式が正常(matchData が null でない)ならば、versionCheck() メソッドに minimumVersion の最後に '+' をくっつけた文字列を渡して呼び出します。
書式が正常でない場合、versionCheck() メソッドに "1.4.2+" を渡して呼び出します。
そして versionCheck() メソッドの戻り値を retval にセットして、440行目でリターンします。

444|     /**
445|      * Outputs a launch button for the specified JNLP URL.  When clicked, the 
446|      * button will ensure that an appropriate JRE is installed and then launch 
447|      * the JNLP application.  minimumVersion is of the form #[.#[.#[_#]]], and 
448|      * is the minimum JRE version necessary to run this JNLP application.  
449|      * minimumVersion is optional, and if it is not specified, '1.4.2' 
450|      * will be used.
451|      * If an appropriate JRE or Web Start installation is detected, 
452|      * the JNLP application will be launched, otherwise installLatestJRE() 
453|      * will be called.
454|      *
455|      * After installLatestJRE() is called, the script will attempt to detect 
456|      * that the JRE installation has completed and launch the JNLP application,
457|      * but there are circumstances (such as when the JRE installation 
458|      * requires a browser restart) when this cannot be fulfilled.
459|      */
460|     createWebStartLaunchButton: function(jnlp, minimumVersion) { 
461| 
462|         if (deployJava.returnPage == null) {
463|             // if there is an install, come back and run the jnlp file
464|             deployJava.returnPage = jnlp;
465|         }
466| 
467|         var url = 'javascript:' +
468|                   'if (!deployJava.isWebStartInstalled(&quot;' + 
469|                       minimumVersion + '&quot;)) {' + 
470|                       'if (deployJava.installLatestJRE()) {' + 
471|                         'if (deployJava.launch(&quot;' + jnlp + '&quot;)) {}' +
472|                       '}' +
473|                   '} else {' +
474|                       'if (deployJava.launch(&quot;' + jnlp + '&quot;)) {}' +
475|                   '}';
476| 
477|         document.write('<' + 'a href="' + url + 
478|                        '" onMouseOver="window.status=\'\'; ' +
479|                        'return true;"><' + 'img ' +
480|                        'src="' + deployJava.launchButtonPNG + '" ' + 
481|                        'border="0" /><' + '/' + 'a' + '>');
482|     },

createWebStartLaunchButton() メソッドです。
コメントには・・・
指定された JNLP の URL を起動するボタンを書き出す。
適切な JRE または Web Start のインストールが見つかれば、JNLP アプリケーションを起動する。そうでなければ installLatestJRE() メソッドを呼び出す。
というように書かれています。

引数 minimumVersion の書式に関する説明は他と同様なので省略します。
installLatestJRE() メソッドが呼び出した後、起動を試みるが、ブラウザの再起動を要求される可能性があるので、試みは失敗するかもというのも runApplet() メソッドの説明と同様です。


462 - 465行目は returnPage プロパティが null だったときに、引数 jnlp の値を returnPage プロパティに格納しています。


467 - 475行目では Web Start がインストールされているか、さもなくば 最新の JRE をインストールして launch() メソッドを呼び出すような、'javascript:' 擬似スキームの URL を組み立ています。


そして477 - 481行目で document.write() で <a> タグを書き出しています。

485|     /**
486|      * Launch a JNLP application, (using the plugin if available)
487|      */
488|     launch: function(jnlp) {
489|         if (deployJava.isPluginInstalled()) {
490|             return deployJava.getPlugin().launch(jnlp);
491|         } else {
492|             document.location=jnlp;
493|             return true;
494|         }
495|     },

launch() メソッドです。
コメントには・・・
JNLP アプリケーションを起動する。(利用可能ならばプラグインで)
というように書かれています。


489 - 490行目は isPluginInstalled() メソッドを呼び出してプラグインがインストールされているならば、引数 jnlp を渡してプラグインの launch() メソッドを呼び出し、その戻り値を直接リターンします。
491 - 494行目ではプラグインがインストールされていない場合に、document.location に引数 jnlp をセットして、true をリターンします。


if/else ブロックの処理の違いは何でしょう。
直感的には、if ブロックがバージョン比較的最近の(1.4.2以降?) JRE を使用している場合。
else ブロックの document.location に JNLP の URL を直接セットする方法は、それ以前の古い JRE を使用している場合でしょうか。
記憶は定かじゃないですが昔、J2SE 1.3 の頃、まだ Web Start が出始めた頃、IE のアドレスバーに直接 JNLP URL を書いてアプリケーションを起動してテストとかしたような・・・当然その頃にも Web Start のランチャーはあったのですが。
今後違いを解明したいと思います。ご存知の方がいましたら教えてください。

498|     /*
499|      * returns true if the ActiveX or XPI plugin is installed
500|      */
501|     isPluginInstalled: function() {
502|         var plugin = deployJava.getPlugin();
503|         if (plugin && plugin.jvms) {
504|             return true;
505|         } else {
506|             return false;
507|         }
508|     },

isPluginInstalled() メソッドです。
コメントには・・・
ActiveX または XPI プラグインがインストールされていたら true をリターンする。
というように書かれています。


中身はとても単純で getPlugin() メソッドを呼び出し、プラグインが取得でき、そのプラグインに jvms プロパティが存在すれば true を、そうでなければ false をリターンしています。

510|     /* 
511|      * returns true if the plugin is installed and AutoUpdate is enabled
512|      */
513|     isAutoUpdateEnabled: function() {
514|         if (deployJava.isPluginInstalled()) {
515|             return deployJava.getPlugin().isAutoUpdateEnabled();
516|         }
517|         return false;
518|     },

isAutoUpdateEnabled() メソッドです。
コメントには・・・
プラグインがインストールされていて AutoUpdate が有効であれば true をリターンする。
というように書かれています。


これも単純ですね。
isPluginInstalled() メソッドを呼び出してプラグインがインストールされているならば、プラグインの isAutoUpdateEnabled() メソッドを呼び出し、その結果をリターンしています。
プラグインがインストールされていないときは、false をリターンします。

520|     /* 
521|      * sets AutoUpdate on if plugin is installed
522|      */
523|     setAutoUpdateEnabled: function() { 
524|         if (deployJava.isPluginInstalled()) { 
525|             return deployJava.getPlugin().setAutoUpdateEnabled(); 
526|         }
527|         return false;
528|     },

setAutoUpdateEnabled() メソッドです。
コメントには・・・
プラグインがインストールされていたら AutoUpdate をセットする。
というように書かれています。


isPluginInstalled() メソッドを呼び出してプラグインがインストールされているならば、プラグインの setAutoUpdateEnabled() メソッドを呼び出し、その結果をリターンしています。
プラグインがインストールされていないときは、false をリターンします。

530|     /*
531|      * sets the preferred install type : null, online, kernel
532|      */
533|     setInstallerType: function(type) {
534|         deployJava.installType = type;
535|         if (deployJava.isPluginInstalled()) {
536|             return deployJava.getPlugin().setInstallerType(type);
537|         }
538|         return false;
539|     },

setInstallerType() メソッドです。
コメントには・・・
好ましいインストールタイプをセットする。
というように書かれています。


isPluginInstalled() メソッドを呼び出してプラグインがインストールされているならば、引数 type を渡してプラグインの setInstallerType() メソッドを呼び出し、その結果をリターンしています。
プラグインがインストールされていないときは、false をリターンします。


インストールタイプは null、online、kernel が選択できるようです。
kernel を指定したインストールでは、JRE ライブラリを小さなバンドルに分割してダウンロードします。
これによってユーザの待ち時間が短くなるようです。
でもその裏側では残りのバンドルがダウンロードされていて、最終的には完全な JRE としてインストールされるようです。
こちらに詳しい情報があります。
http://java.sun.com/javase/ja/6/6u10faq.html#JKernel

541|     /*
542|      * sets additional package list - to be used by kernel installer
543|      */
544|     setAdditionalPackages: function(packageList) {
545|         if (deployJava.isPluginInstalled()) {
546|             return deployJava.getPlugin().setAdditionalPackages(
547|                                                      packageList);
548|         }
549|         return false;
550|     },

setAdditionalPackages() メソッドです。
コメントには・・・
Kernel インストーラによって使用される、追加パッケージのリストをセットする。
というように書かれています。


isPluginInstalled() メソッドを呼び出してプラグインがインストールされているならば、引数 packageList を渡してプラグインの setAdditionalPackages() メソッドを呼び出し、その結果をリターンしています。
プラグインがインストールされていないときは、false をリターンします。

552|     /*
553|      * sets preference to install Early Access versions if available
554|      */
555|     setEarlyAccess: function(enabled) {
556|         deployJava.EAInstallEnabled = enabled;
557|     },

setEarlyAccess() メソッドです。
コメントには・・・
入手可能ならば EA バージョンのインストールを優先するようにセットする。
というように書かれています。


556行目で EAInstallEnabled プロパティに引数 enabled をセットしています。

559|     /*
560|      * Determines if the next generation plugin (Plugin II) is default
561|      */
562|     isPlugin2: function() {
563|         if (deployJava.isPluginInstalled()) {
564|             try {
565|                 return deployJava.getPlugin().isPlugin2();
566|             } catch (err) {
567|                 // older plugin w/o isPlugin2() function - just fall through
568|             }
569|         }
570|         return false;
571|     },

isPlugin2() メソッドです。
コメントには・・・
次世代のプラグイン (Plugin II) がデフォルトであるかを明らかにする。
というように書かれています。


isPluginInstalled() メソッドを呼び出してプラグインがインストールされているならば、プラグインの isPlugin2() メソッドを呼び出し、その結果をリターンしています。
プラグインがインストールされていないとき、または isPlugin2() メソッドを呼び出してエラーをキャッチした場合、false をリターンします。

574|     getPlugin: function() {
575|         deployJava.refresh();
576|         var ret = document.getElementById('deployJavaPlugin');
577|         return ret;
578|     },

getPlugin() メソッドです。
ここからはコメントがありません。
内部だけでの利用を意図しいるのでしょう。


refresh() メソッドを呼び出した後、document.getElementById() で 'deployJavaPlugin' を ID に持つ要素を取得して返しています。
refresh() メソッドでは、 タグを書き出して ID 属性に 'deployJavaPlugin' をセットしています。

第二章はここまで。
次は最終章です。