deployJava.js をマニアックに読み解く 最終章

deployJava.js を読むシリーズは今回が三回目、ようやく終盤です。


それではここまで来たらもう一踏ん張りです。
最後までマニアックに頑張りましょう。

580|     compareVersionToPattern: function(version, patternArray, familyMatch) {
581|         var regex = "^(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(?:_(\\d+))?)?)?$";
582|         var matchData = version.match(regex);  
583| 
584|         if (matchData != null) { 
585|             var index = 0;
586|             var result = new Array();
587| 
588|             for (var i = 1; i < matchData.length; ++i) {
589|                 if ((typeof matchData[i] == 'string') && (matchData[i] != ''))
590|                 {
591|                     result[index] = matchData[i];
592|                     index++;
593|                 }
594|             }
595| 
596|             var l = Math.min(result.length, patternArray.length);
597| 
598|             if (familyMatch) {
599|                 for (var i = 0; i < l; ++i) {
600|                     if (result[i] != patternArray[i]) return false;
601|                 }
602| 
603|                 return true;
604|             } else {
605|                 for (var i = 0; i < l; ++i) {
606|                     if (result[i] < patternArray[i]) {
607|                         return false;
608|                     } else if (result[i] > patternArray[i]) {
609|                         return true;
610|                     }
611|                 }
612|                 
613|                 return true;
614|             }
615|         } else {
616|             return false;
617|         }
618|     },

compareVersionToPattern() メソッドです。
Applet や Web Start アプリケーションが必要とするバージョンと、インストールされている JRE のバージョンとを比較する為に versionCheck() メソッドから呼び出されます。

3つの引数の意味は以下のとおり

  • version - インストールされている JRE のバージョン文字列
  • patternArray - Applet/アプリケーションが必要なバージョンの数字が格納された配列(versionCheck() メソッドの解説参照)
  • familyMatch - true の場合、製品ファミリとして比較することを指定するフラグ


581 - 594行目で引数 version を構成する数字に分解して格納した配列 result に移します。


596行目で Math.min() で result と patternArray の長さの短いほうの値を 変数 l にセットします。


598 - 603行目は製品ファミリとして比較する場合の処理です。
result と patternArray の双方で配列長の短い方の数分 (変数 l)、内容を一つずつ比較し、一致しなかった時点で false をリターンします。
例を示すと、以下のようになります。


 result = [1, 5, 0, 01] ("1.5.0_01")
 patternArray = [1, 5] ("1.5*")
この場合は配列の2番目の要素まで比較して一致するので結果は true


逆に
 result = [2, 6, 0, 10] ("2.6.0_10")
 patternArray = [1, 5] ("1.5*")
この場合は1番目の要素で不一致となり結果は false


604 - 614行目は最低バージョンを指定した場合の処理です。
result と patternArray の双方で配列長の短い方の数分 (変数 l)、内容を一つずつ比較し、result の方に小さな数字が表れた時点で false をリターンします。
そして result の方に大きな数字が表れた時点で true をリターンします。
また全て一致した場合も true をリターンします。
例を示すと、以下のようになります。


 result = [1, 5, 1, 01] ("1.5.1_01")
 patternArray = [1, 5, 0] ("1.5.0*")
この場合は配列の3番目の要素まで比較して result の数字の方が大きいので結果は true


そして
 result = [1, 5, 0, 01] ("1.5.1_01")
 patternArray = [1, 5, 0, 01] ("1.5.1_01")
この場合は全てが一致するので結果は true


逆に
 result = [1, 4, 1, 01] ("1.4.1_01")
 patternArray = [1, 5, 0] ("1.5.0*")
この場合は配列の2番目の要素で result の数字の方が小さいので結果は false


615 - 617行目では引数 version の書式が不正の場合に false を返しています。

621|     getBrowser: function() {
622|         var browser = navigator.userAgent.toLowerCase();
623|     
624|         if (deployJava.debug) {
625|             alert('userAgent -> ' + browser);
626|         }
627|     
628|         if ((navigator.vendor) && 
629|             (navigator.vendor.toLowerCase().indexOf('apple') != -1) &&
630|             (browser.indexOf('safari') != -1)) {
631|             if (deployJava.debug) {
632|                 alert('We claim to have detected "Safari".');
633|             }
634|             return 'Safari';
635|         } else if (browser.indexOf('msie') != -1) {
636|             if (deployJava.debug) {
637|                 alert('We claim to have detected "IE".');
638|             }
639|             return 'MSIE';
640|         } else if ((browser.indexOf('mozilla') != -1) || 
641|                    (browser.indexOf('firefox') != -1)) {
642|             if (deployJava.debug) {
643|                 alert('We claim to have detected a Netscape family browser.');
644|             }
645|             return 'Netscape Family';
646|         } else {
647|             if (deployJava.debug) {
648|                 alert('We claim to have failed to detect a browser.');
649|             }
650|             return '?';
651|         }
652|     },

getBrowser() メソッドです。
現在のブラウザを識別する文字列をリターンします。


628 - 634行目では現在のブラウザが Safari かどうかチェックしており、条件に合致したら 'Safari' という文字列をリターンしています。
635 - 639行目では IE かどうかチェックしており、条件に合致したら 'MSIE' という文字列をリターンしています。
640 - 645行目では Firefox (mozilla) かどうかチェックしており、条件に合致したら 'Netscape Family' という文字列をリターンしています。
SafariIE でも navigator.userAgent の値には 'mozilla' という文字列が含まれていますが、それらを先にチェックすることで、結果がおかしくならないようにしています。
646 - 650行目はどの条件にもあてはまらない場合で、'?' という文字列をリターンしています。

655|     testUsingActiveX: function(version) {
656|         var objectName = 'JavaWebStart.isInstalled.' + version + '.0';
657|     
658|         if (!ActiveXObject) {
659|             if (deployJava.debug) {
660|               alert ('Browser claims to be IE, but no ActiveXObject object?');
661|             }
662|             return false;
663|         }
664|     
665|         try {
666|             return (new ActiveXObject(objectName) != null);
667|         } catch (exception) {
668|             return false;
669|         }
670|     },

testUsingActiveX() メソッドです。
IE 用のテストメソッドで、getJREs() メソッドから呼び出されます。


ActiveXObject が有効で、'JavaWebStart.isInstalled.' + version + '.0' を引数に ActiveXObject のオブジェクトが生成できれば true をリターンします。
ActiveXObject が無効の場合、また上記の引数で ActiveXObject のオブジェクトが生成できない場合は false をリターンします。

673|     testForMSVM: function() {
674|         var clsid = '{08B0E5C0-4FCB-11CF-AAA5-00401C608500}';
675| 
676|         if (typeof oClientCaps != 'undefined') {
677|             var v = oClientCaps.getComponentVersion(clsid, "ComponentID");
678|             if ((v == '') || (v == '5,0,5000,0')) {
679|                 return false;
680|             } else {
681|                 return true;
682|             } 
683|         } else {
684|             return false;
685|         }
686|     },

testForMSVM() メソッドです。
これも IE 用のテストメソッドで、getJREs() メソッドから呼び出されます。


clientCapsビヘイビアを利用して、Microsoft VM が利用可能かどうかをチェックします。
clientCapsビヘイビアを利用できるよう、HTML ページに特別な記述をしなければいけません。


通常は、JRE の使用を想定すると思うので、この方法を使うのは例外的だと思います。

689|     testUsingMimeTypes: function(version) {
690|         if (!navigator.mimeTypes) {
691|             if (deployJava.debug) {
692|                 alert ('Browser claims to be Netscape family, but no mimeTypes[] array?');
693|             }
694|             return false;
695|         }
696|     
697|         for (var i = 0; i < navigator.mimeTypes.length; ++i) {
698|             s = navigator.mimeTypes[i].type;
699|             var m = s.match(/^application\/x-java-applet\x3Bversion=(1\.8|1\.7|1\.6|1\.5|1\.4\.2)$/);
700|             if (m != null) {
701|                 if (deployJava.compareVersions(m[1], version)) {
702|                     return true;   
703|                 }
704|             }
705|         }
706|         return false;
707|     },

testUsingMimeTypes() メソッドです。
これは Firefox 用のテストメソッドで、やはり getJREs() メソッドから呼び出されます。


navigator.mimeType を調べて version が利用可能かをチェックします。
navigator.mimeType は現在のブラウザで対応可能な MIME タイプを表す MimeType オブジェクトの配列(のようなオブジェクト)です。
MimeType オブジェクトの type プロパティが699行目の正規表現に合致した場合、compareVersions() メソッドを呼び出して要求する version が利用可能かどうかを調べます。
利用可能であれば、true をリターンしますが、!navigator.mimeTypes の式が true と評価される場合(対応可能な MIME タイプが存在しないということは考えられないですが。因みに IE ではこの配列は空ですがプロパティ自体は存在しています。)は、false をリターンします。
また MIME タイプを全て調べて version が利用可能でない場合も false をリターンします。

710|     testUsingPluginsArray: function(version) {
711|         if ((!navigator.plugins) || (!navigator.plugins.length)) {
712|             if (deployJava.debug) {
713|                 alert ('Browser claims to be Safari, but no plugins[] array?');
714|             }
715|             return false;
716|         }
717| 
718|         for (var i = 0; i < navigator.plugins.length; ++i) {
719|             s = navigator.plugins[i].description;
720|     
721|             if (s.search(/^Java Switchable Plug-in/) != -1) {
722|                 return true;
723|             }
724|     
725|             m = s.match(/^Java (1\.4\.2|1\.5|1\.6|1\.7).* Plug-in/);
726|             if (m != null) {
727|                 if (deployJava.compareVersions(m[1], version)) return true; 
728|             }
729|         }
730|         return false;
731|     },

testUsingPluginsArray() メソッドです。
これは Safari 用のテストメソッドで、getJREs() メソッドから呼び出されます。
現時点では Safari は未対応とのことですが、メソッドだけは用意されています。


navigator.plugins を調べて version が利用可能かをチェックします。
navigator.mimeType は現在のブラウザで利用可能なプラグインを表す Plugin オブジェクトの配列(のようなオブジェクト)です。
処理の内容は殆ど testUsingMimeTypes と同様の為、解説は省略します。

733|     IEInstall: function() {
734|     
735|         location.href = deployJava.getJavaURL + 
736|             ((deployJava.returnPage != null) ?
737|             ('&returnPage=' + deployJava.returnPage) : '') +
738|             ((deployJava.locale != null) ?
739|             ('&locale=' + deployJava.locale) : '') +
740|             ((deployJava.brand != null) ? ('&brand=' + deployJava.brand) : '') +
741|             ((deployJava.installType != null) ? 
742|              ('&type=' + deployJava.installType) : '');
743| 
744|          // should not actually get here
745|          return false;
746|     },

IEInstall() メソッドです。
installLatestJRE() メソッドから呼び出されます。
getJavaURL プロパティをもとに URL を組み立てて IE 用の JRE インストールページに移動します。


実際には、getJavaURL プロパティが保持する URL へ移動すると、更に別ページへリダイレクトします。
returnPage、locale、brand、installType のプロパティが値をそれぞれ保持している場合は、パラメータとしてそれらを URL に連結してサーバーに渡しています。

748|     done: function (name, result) {
749|     },

done() メソッドです。
実装は空です。

751|     FFInstall: function() {
752| 
753|         location.href = deployJava.getJavaURL + 
754|             ((deployJava.returnPage != null) ?
755|             ('&returnPage=' + deployJava.returnPage) : '') +
756|             ((deployJava.locale != null) ?
757|             ('&locale=' + deployJava.locale) : '') +
758|             ((deployJava.brand != null) ? ('&brand=' + deployJava.brand) : '') +
759|             ((deployJava.installType != null) ? 
760|                 ('&type=' + deployJava.installType) : '');
761| 
762|          // should not actually get here
763|          return false;
764|     },

FFInstall() メソッドです。
installLatestJRE() メソッドから呼び出されます。
getJavaURL プロパティをもとに URL を組み立てて Firefox 用の JRE インストールページに移動します。
とは言え、メソッド名は違うものの、内容は IEInstall() メソッドと同じです。

766|     // return true if 'installed' (considered as a JRE version string) is
767|     // greater than or equal to 'required' (again, a JRE version string).
768|     compareVersions: function(installed, required) {
769| 
770|         var a = installed.split('.');
771|         var b = required.split('.');
772|     
773|         for (var i = 0; i < a.length; ++i) {
774|             a[i] = Number(a[i]);
775|         }
776|         for (var i = 0; i < b.length; ++i) {
777|             b[i] = Number(b[i]);
778|         }
779|         if (a.length == 2) {
780|             a[2] = 0;      
781|         }
782|     
783|         if (a[0] > b[0]) return true;
784|         if (a[0] < b[0]) return false;
785|     
786|         if (a[1] > b[1]) return true;
787|         if (a[1] < b[1]) return false;
788|     
789|         if (a[2] > b[2]) return true;
790|         if (a[2] < b[2]) return false;
791|     
792|         return true;
793|     },

compareVersions() メソッドです。
コメントには・・・
引数 installed が required より大きいか等しければ true をリターンする。
と書かれています。

2 つの引数は JRE のバージョン文字列です。
770 - 771行目で 2 つの引数を "." で区切って配列に変換しています。
773 - 778行目ではそれぞれの配列の各値を数値に変換しています。
779 - 781行目は、配列 a (元は installed) の配列長が 2 個の場合、3 番目の要素に 0 を格納しています。


783 - 790行目では双方の配列の各要素の値の大小を比較して、a の方が大きければ true 、小さければ false をリターンします。

796|     enableAlerts: function() {
797|         deployJava.debug = true;
798|     },

enableAlerts() メソッドです。
debug プロパティを true にセットします。

800|     poll: function() {
801| 
802|         deployJava.refresh();
803|         var postInstallJREList = deployJava.getJREs();           
804| 
805|         if ((deployJava.preInstallJREList.length == 0) && 
806|             (postInstallJREList.length != 0)) {
807|             clearInterval(deployJava.myInterval);
808|             if (deployJava.returnPage != null) {
809|                 location.href = deployJava.returnPage;
810|             };
811|         }
812| 
813|         if ((deployJava.preInstallJREList.length != 0) && 
814|             (postInstallJREList.length != 0) &&
815|             (deployJava.preInstallJREList[0] != postInstallJREList[0])) {
816|             clearInterval(deployJava.myInterval);
817|             if (deployJava.returnPage != null) {
818|                 location.href = deployJava.returnPage;
819|             }
820|         }
821| 
822|     },

poll() メソッドです。
installLatestJRE() メソッドで EA 版の JRE をインストールする際に、setInterval() で 3 秒おきに呼び出されるように設定されます。
これは JRE のインストールが完了したかどうかを監視する為に定期的に呼び出されます。


802行目で refresh() メソッドを呼び出します。
803行目で getJREs() メソッドを呼び出して、結果を変数 postInstallJREList に格納します。


805 - 806行目で preInstallJREList プロパティの length が 0 で、 postInstallJREList の length が 0 でないかの条件を判定します。
ここで preInstallJREList プロパティとは installLatestJRE() メソッドの中でセットされた、インストール前の JRE の数を格納しています。
つまり refresh() メソッドを呼び出して、JRE の数が増えたかを確認しているのです。
条件に合致したらインストールが成功したとみなして、807行目で clearInterval() を呼び出して、以降の poll() メソッドの呼び出しをキャンセルします。
808 - 810行目で更に returnPage プロパティが null 出ない場合に、location.href に returnPage プロパティをセットして元のページへの移動を試みます。


813 - 815行目では preInstallJREList プロパティの length が 0 でなく、 postInstallJREList の length も 0 でなくて、双方の配列の先頭の値が異なる場合、インストールが成功したとみなします。
その後は、807 - 810行目の処理と同じです。

824|     writePluginTag: function() {
825|         var browser = deployJava.getBrowser();
826|         if (browser == 'MSIE') {
827|             document.write('<' + 
828|                 'object classid="clsid:CAFEEFAC-DEC7-0000-0000-ABCDEFFEDCBA" ' +
829|                 'id="deployJavaPlugin" width="0" height="0">' +
830|                 '<' + '/' + 'object' + '>');
831|         } else if (browser == 'Netscape Family') {
832|             if (navigator.mimeTypes != null) for (var i=0; 
833|                     i < navigator.mimeTypes.length; i++) {
834|                 if (navigator.mimeTypes[i].type == deployJava.mimeType) {
835|                     if (navigator.mimeTypes[i].enabledPlugin) {
836|                         document.write('<' + 
837|                             'embed id="deployJavaPlugin" type="' + 
838|                             deployJava.mimeType + '" hidden="true" />');
839|                     }
840|                 }
841|            }
842|         }
843|     },

writePluginTag() メソッドです。
プラグインを実行するためのタグを書き出します。


826 - 830行目はブラウザが IE の場合に タグを出力しています。
831 - 842行目はブラウザが Firefox の場合にの処理です。
navigator.mimeTypes 配列の各 MymeType オブジェクトのタイプが mimeType プロパティの値と等しく、また MymeType オブジェクトの enabledPlugin の値が null でない場合、 タグを書き出します。


このタグの ID 属性に "deployJavaPlugin" という値を設定しています。
getPlugin() メソッドと refresh() メソッドではこの ID を使って、プラグインを取得しています。

845|     refresh: function() {
846|         navigator.plugins.refresh(false);
847| 
848|         var browser = deployJava.getBrowser();
849|         if (browser == 'Netscape Family') {
850|             var plugin = document.getElementById('deployJavaPlugin');
851|             // only do this again if no plugin
852|             if (plugin == null) {
853|                 if (navigator.mimeTypes != null) for (var i=0;
854|                     i < navigator.mimeTypes.length; i++) {
855|                     if (navigator.mimeTypes[i].type == deployJava.mimeType) {
856|                         if (navigator.mimeTypes[i].enabledPlugin) {
857|                             document.write('<' +
858|                                 'embed id="deployJavaPlugin" type="' +
859|                                 deployJava.mimeType + '" hidden="true" />');
860|                         }
861|                     }
862|                }
863|             }
864|         }
865|     },

refresh() メソッドです。
新しくインストールされたプラグインを有効にする為に呼び出されます。


846行目で、 navigator.plugins.refresh(false) を呼び出して、新たにインストールしたプラグインを使用可能にします。
このメソッドは IE ではサポートされていません。


849 - 864行目はブラウザが Firefox の場合にの処理です。
850行目で 'deployJavaPlugin' を引数として document.getElementById() を呼び出します。
これでプラグインを取得できれば、以降は何もしません。


853 - 862行目はプラグインが取得できなかった場合の処理です。
この処理は、 タグを書き出す処理で、writePluginTag() メソッドで詳しく見たので、省略します。

867|     do_initialize: function() {
868|         deployJava.writePluginTag();
869|         if (deployJava.locale == null) {
870|             var loc = null;
871| 
872|             if (loc == null) try {
873|                 loc = navigator.userLanguage;
874|             } catch (err) { }
875| 
876|             if (loc == null) try {
877|                 loc = navigator.systemLanguage;
878|             } catch (err) { }
879|     
880|             if (loc == null) try {
881|                 loc = navigator.language;
882|             } catch (err) { }
883|     
884|             if (loc != null) {
885|                 loc.replace("-","_")
886|                 deployJava.locale = loc;
887|             }
888|         }
889|     }

do_initialize() メソッドです。
いよいよ最後のメソッドの定義です。


868行目で writePluginTag() メソッドを呼び出して、object/embed タグを書き出します。
869 - 888行目では locale プロパティが null の場合に、navigator の userLanguage、systemLanguage、language を順にロケールを探します。
navigator.userLanguage は IE で値が "ja" です。
navigator.language は Fireforx で値が "ja" 、Sarari で値が "ja-JP" です。
systemLanguage は過去の IE のものらしいです。


ロケールが取得できたならば885行目で "-" を "_" に置換して、886行目で locale プロパティに設定します。

891| };
892| deployJava.do_initialize();


891行目で deployJava の定義は終わり。
892行目で do_initialize() メソッドを呼び出して deployJava オブジェクトを初期化します。


以上、長かったですが deployJava.js がようやく読み終わりました。
これで Deployment Toolkit の便利な機能 JRE のインストールと、Applet/Web Start アプリケーションの起動の仕組みが本の少しでも理解できたかと思います。


deployJava.js 読んでいるときは気付かず、後から知って購入したのですが、技術評論者の Java Expert #03 で「Applet Reloaded」と題してこの周辺の技術について記事が掲載されていました。
その中では次世代プラグインのワクワクな新機能や、ビックリする Draggable Applet、新しくなった LiveConnect などが紹介されています。


Java Expert #03

Java Expert #03