目次
LiveConnect は、Java アプレットがブラウザ内の JavaScript エンジンと通信したり、Web ページ上の JavaScript がアプレットと対話したりできるようにする、Web ブラウザの機能です。LiveConnect の概念は Netscape Web ブラウザで生まれ、これまでは Mozilla と Firefox ブラウザが LiveConnect の機能をもっとも完全にサポートしていました。しかし、JavaScript と Java 間の呼び出しは、どの Web ブラウザでも以前からなんらかの方法で可能でした。
新しい Java Plug-In では、Java 言語と JavaScript 言語間の相互運用階層が書き換えられました。Firefox ファミリのブラウザでは、Java に関する専門知識が一掃され、Java Plug-In がほかのスクリプト作成可能なプラグインと同じように動作します。
Java Plug-In は、すべての LiveConnect 機能をすべての Web ブラウザに実装する責任を負っているため、次のように多くの利点をもたらします。
- 信頼性の向上。Java/JavaScript の呼び出しを行なっても、Web ブラウザが不安定になる心配がありません。まれな状況 (アプレットの終了時など) での Java/JavaScript の動作が大幅に改善されました。
- パフォーマンスの向上。Java/JavaScript 呼び出しがオペレーティングシステムのプロセス境界を越えるようになったにもかからわず、ほとんどの場合、新しい Java Plug-In はどのブラウザやプラットフォームでも従来の Java Plug-In と同等またはそれ以上の速度で動作し、場合によっては劇的に高速化されます。パフォーマンステストによれば、新しいプラグインは従来のプラグインの 2 倍を超える速度で動作します。
- ブラウザ間の移植性の向上。Java を呼び出す JavaScript コード (またはその逆) をあるブラウザファミリから別のブラウザファミリに移行するときに、コードの移植のしやすさが大幅に向上しました。
- 機能の追加。静的 Java メソッドの呼び出し、新しい Java オブジェクトのインスタンス化、および Sun 以外のパッケージの JavaScript からの読み出しなど、以前の Mozilla 固有の LiveConnect 機能がすべてのブラウザで利用可能になりました。
この仕様では、新しい Java Plug-In での Java/JavaScript ブリッジの動作を記述するとともに、Java Plug-In の代替実装が Java/JavaScript 混在コードの移植性をサポートするためにどのように動作すべきかを規定します。この仕様は、以前のどの LiveConnect 仕様よりも優先されます。
Web ページ上の JavaScript は、そのページに埋め込まれた Java アプレットと対話できます。Java オブジェクトのメソッドの呼び出し、Java オブジェクトのフィールドの取得と設定、Java 配列の要素の取得と設定、Java オブジェクトのインスタンスの新規作成などができます。次の各節では、サポートされるすべての操作、およびブラウザ内の JavaScript エンジンと Java 仮想マシンとの間で値の受け渡しを行う方法について説明します。
JavaScript は、任意の public Java クラスの任意の public メソッドを呼び出すことができます。特定のアプレットに関して JavaScript エンジンに公開される「ルート」(最初の) Java オブジェクトは、そのアプレット自体を表す java.lang.Applet のインスタンスです。オブジェクトを返す Java メソッドを呼び出すと、そのオブジェクトは JavaScript エンジンに公開され、その public メソッドを呼び出すこともできます。
例
- Java コード:
public class MethodInvocation extends Applet {
public void noArgMethod() { ... }
public void someMethod(String arg) { ... }
public void someMethod(int arg) { ... }
public int methodReturningInt() { return 5; }
public String methodReturningString() { return "Hello"; }
public OtherClass methodReturningObject() { return new OtherClass(); }
}
public class OtherClass {
public void anotherMethod();
}
- Web ページと JavaScript コード:
<applet id="app"
archive="examples.jar"
code="MethodInvocation" ...>
</applet>
<script language="javascript">
app.noArgMethod();
app.someMethod("Hello");
app.someMethod(5);
var five = app.methodReturningInt();
var hello = app.methodReturningString();
app.methodReturningObject().anotherMethod();
</script>
- 実行可能なテストケース
- 完全なソースコード (MethodInvocation.html / .java)
JavaScript は、任意の public Java クラスの任意の public フィールドにアクセスし、Java オブジェクトのフィールドを設定および取得できます。
例
- Java コード:
public class FieldAccess extends Applet {
public int intField = 5;
public String stringField = "Hello";
public OtherClass otherField = new OtherClass();
}
public class OtherClass {
public int intField = 6;
public String stringField = "Testing";
}
- Web ページと JavaScript コード:
<applet id="app"
archive="examples.jar"
code="FieldAccess" ...>
</applet>
<script language="javascript">
var val = app.intField;
app.intField = 6;
val = app.stringField;
app.stringField = "Goodbye";
val = app.otherField.intField;
app.otherField.intField = 7;
val = app.otherField.stringField;
app.otherField.stringField = "1, 2, 3";
</script>
- 実行可能なテストケース
- 完全なソースコード (FieldAccess.html / .java)
JavaScript コードは、Java 配列にアクセスして配列を変更できます。JavaScript コードに渡された (または返された) Java 配列は、通常の JavaScript 配列と同じように見えます。Java 配列には「length」フィールドがあり、通常の JavaScript 配列のインデックス指定構文を使用して Java 配列にアクセスできます。Java 仮想マシンから JavaScript エンジンへの Java 配列の引き渡しは、参照渡しで行われます。つまり、JavaScript コードで行われた変更は、同じ配列を参照する Java コードからも見ることができます。
このような JavaScript からアクセス可能な配列では、Java 配列のセマンティクスがそのまま保持されます。配列の末尾を越えて要素を追加したり、個々の配列要素を削除したりすることはできません。これらの操作を実行しようとすると、JavaScript エンジンに例外が発生します。
JavaScript コードは、Java コードに配列を渡すこともできます。Java のメソッドが配列型を引数として取る場合、JavaScript はその引数に JavaScript 配列オブジェクトまたはリテラルを渡すことができます。Java Plug-In は、新しい Java 配列を割り当て、後述の JavaScript-to-Java のデータ型変換で説明する変換規則を使用して JavaScript 配列の各要素を適切な Java の型に変換します。多次元配列は、疎な JavaScript 配列と同じようにサポートされます。JavaScript 配列内の配列要素が定義されていない場合は、Java 配列の変換時にデフォルト値 (基本型の値としては 0、オブジェクトの値としては null) が使用されます。配列データは必然的にコピーされるため、Java コードがこれらの配列に対して行なった変更は JavaScript エンジンには反映されません。
例
- Java コード:
public class ArrayAccess extends Applet {
public int[] methodReturning123() { return new int[] { 1, 2, 3 } }
public void methodExpecting123(int[] arg) { ... }
}
- Web ページと JavaScript コード:
<applet id="app"
archive="examples.jar"
code="ArrayAccess" ...>
</applet>
<script language="javascript">
var arr = app.returns123();
swapElements0And2(arr);
app.expects321(arr);
app.expects321([ 3, 2, 1 ]);
// Returns 2-dimensional Java array
arr = app.returns1Through9();
// Transform this into 9..1
swapElements0And2(arr);
swapColumns0And2(arr);
app.expects9Through1(arr);
app.expects9Through1([[9, 8, 7], [6, 5, 4], [3, 2, 1]]);
</script>
- 実行可能なテストケース
- 完全なソースコード (ArrayAccess.html / .java)
新しい Java Plug-In には、アプレットオブジェクトに接続され、そのアプレットから参照できる Java パッケージへのアクセスを提供する Packages 合成キーワードがあります。JavaScript コードでは、このキーワードを使用して次の操作を実行できます。
- Java クラスの static メソッドの呼び出し
- Java クラスの static フィールドの設定と取得
- Java オブジェクトのインスタンスの新規作成
JavaScript コードでは、アプレットごとの Packages キーワードを使用して、ユーザー定義クラスにもアクセスできます。これにより、Java アプリケーションのきめ細かいスクリプト作成を行う機能がブラウザに提供されます。
例
- Java コード:
package com.mycompany;
public class PackageAccess extends Applet {
}
public class MyClass {
public static int staticField = 5;
public static void staticMethod() { ... }
public void instanceMethod() { ... }
}
- Web ページと JavaScript コード:
<applet id="app"
archive="examples.jar"
code="com.mycompany.PackageAccess" ...>
</applet>
<script language="javascript">
var val = app.Packages.com.mycompany.MyClass.staticField;
app.Packages.com.mycompany.MyClass.staticField = 6;
app.Packages.com.mycompany.MyClass.staticMethod();
val = new app.Packages.com.mycompany.MyClass();
val.instanceMethod();
</script>
- 実行可能なテストケース
- 完全なソースコード (PackageAccess.html / .java)
Mozilla ファミリのブラウザでは、従来から、Java アプレットが含まれない Web ページ上でも JavaScript から Java 言語へのアクセスがサポートされています。このブラウザファミリには、JavaScript コードで使用できるグローバルな java、netscape、および Packages キーワードがあり、前述のアプレットごとの Packages キーワードと同じような方法で static メソッドの呼び出し、static フィールドへのアクセス、および Java クラスのインスタンスの新規作成を行うことができます。
Web ページで複数のアプレットを使用できる場合は、これらのキーワードのセマンティクスが問題になります。(たとえば、com.mycompany パッケージ内の) ある特定のアプレットのユーザー定義クラスにアクセスする場合、グローバルな Packages キーワードでは参照すべきアプレットを特定できません。新しい Java Plug-In では、Web ブラウザに複数の Java 仮想マシンインスタンスを接続してアプレットを実行する方法がサポートされます。その場合、これらのグローバルキーワードのセマンティクスはさらに複雑になります。
このため、グローバルな java、netscape、および Packages JavaScript キーワードの使用は推奨されていません。これらのキーワードは Firefox ブラウザでも引き続き機能しますが、これらのキーワードを使用した既存のコードは、新しいアプレットごとの Packages キーワードを使用したコードに移行することを強くお勧めします。これらのグローバルなキーワードを使用してユーザー定義クラスにアクセスすることはできません。アクセスしようとすると、定義されていない結果が生じます。
Java 言語はメソッドのオーバーロードをサポートします。このため、名前が同じで引数の型のセットが異なるメソッドが複数存在する可能性があります。Java コンパイラ (javac) は、特定の呼び出し位置で呼び出されるメソッドのバージョンを、引数の数と型に基づいて判定します。対象となっているメソッドのバージョンを明確に判定できない場合、javac はエラーを報告し、呼び出し側のコードのコンパイルに失敗します。
JavaScript コードがオーバーロードされた Java メソッドを呼び出すときは、JavaScript エンジンからそのメソッドに渡される値の数と型に基づいて、メソッドのどのバリアントを呼び出すべきかを、コンパイル時ではなく実行時に判定する必要があります。この処理は動的に行う必要があるため、Java コンパイラではなく Java Plug-In がこのオーバーロードされたメソッドの解決を担当します。
Java Plug-In は、現時点ではコードベースのアルゴリズムを使用して、オーバーロードされたメソッドのどのバリアントを呼び出すべきかを判定します。特定のオーバーロードされたバリアントを呼び出すコストは、入力引数のそれぞれをメソッドのシグニチャーで要求される型に変換するための変換コストを合計したものです。コストがもっとも低いメソッドが呼び出されます。コストが同じメソッドが 2 つある場合は、呼び出しがあいまいであることを示す例外が JavaScript エンジンに発生します。
JavaScript エンジンから Java に値を渡すときにサポートされる組み込みの変換セットについては、次の節の「データ型の変換」を参照してください。
アプリケーションがこのオーバーロード解決アルゴリズムに依存し過ぎることを避けるため、さまざまな変換コストの正確な値は、現時点では意図的に明示されていません。目的は、オーバーロードされたどのメソッドを呼び出すべきかが入力引数の検査から明らかな場合に、コストベースのアルゴリズムがあいまいさを報告しないようにすることにあります。しかし、開発者へのヒントとして、現在適用されている規則を次に示します。
- 次の変換はコストがもっとも低くなります。
- 数値型から類似の Java 基本型へ
- null から任意の非基本型へ
- JSObject から JSObject へ
- クラス型からクラス型へ (型が同一の場合)
- 次の変換はコストがより高くなります。
- 数値型から別の基本型へ
- 文字列から数値型へ
- クラス型からスーパークラス型へ。これは、あるメソッドが引数としてサブクラスを取り、別のメソッドが引数としてスーパークラスを取り、サブクラスのインスタンスが渡された場合の、オーバーロードの解決に役立ちます。
Java Plug-In は、クラス階層における値と該当する型の間の「距離」の指標を計算します。
- 次の変換はコストがさらに高くなります。
- 次の変換はコストがいっそう高くなります。
- JSObject から文字列へ
- JSObject から Java 配列へ
- 次の場合は、変換不可能として (負の変換コストが) 報告されます。
- 任意の値から
java.lang.Class で表されない型へ
- null から基本型へ
- JSObject から前述の型を除く任意の型へ
- 前述の変換を除く任意の変換
いずれはオーバーロードされたメソッドの解決アルゴリズムを変更する必要が生じることも予想されますが、現在の実装によって高度なアプリケーションの開発が可能になると期待されます。このアルゴリズムの改良、特にこのアルゴリズムでうまくいかない状況のテストケースについて、フィードバックをお寄せください。
JavaScript から Java メソッドの呼び出し、Java フィールドの設定、または Java クラスのコンストラクタへの引数の引き渡しを行うと、1 つ以上の値が JavaScript エンジンから Java に渡されます。Java が静的に型付けする言語であるのに対して、JavaScript は動的に型付けする言語であるため、JavaScript エンジンから入力された値を Java 仮想マシンが要求する適切なデータ型に変換する必要があります。次の各表に、JavaScript から Java に値を渡すときにサポートされるデータ型変換を示します。これらの表に示されていない変換を実行しようとすると、JavaScript エンジンに例外が発生します。
Java-to-JavaScript 呼び出し (netscape.javascript.JSObject の call や eval など) の戻り値も、同じ変換規則に従います。これらの戻り値は、常に java.lang.Object に変換されます。
各ベンダーの JavaScript エンジンは、数値を異なる方法で表します。たとえば、ネイティブですべての数値を倍精度浮動小数点値として表す JavaScript エンジンもありますが、ネイティブでほかの種類の数値 (整数値など) を表すことができる JavaScript エンジンもあります。
この仕様では、JavaScript エンジンの数値表現に関する保証はしません。後述の変換が適用される前に、JavaScript の値が利用可能なもっとも近い表現形式 (int や double など) を使用して Java 仮想マシンに転送されることが前提になります。
| Java パラメータの型 |
変換規則 |
byte char short int long float double
|
- 値は、Java キャストを使用して変換されます。Java キャストのセマンティクスは Java 言語の仕様で定義されています。前述のように、JavaScript エンジンから入力される変換元の値の表現は指定されません。
- Java リフレクションとは異なり、ビット幅を狭める変換がサポートされます。
|
java.lang.Byte java.lang.Character java.lang.Short java.lang.Integer java.lang.Long java.lang.Float java.lang.Double java.lang.Object
|
- 値は、基本型の規則に従って変換され、適切な Java ボクシングオブジェクトにボクシングされます。
- JavaScript の数値を
java.lang.Object に変換するときは、JavaScript 値の表現にもっとも一致するボクシングオブジェクトが使用されます。これらの変換では、結果が java.lang.Number のインスタンスになることだけが保証されます。
|
| java.lang.String |
値は、文字列に変換されます。たとえば、次のようになります。
JavaScript エンジンが異なれば数値に使用される内部表現も異なるため、Java 文字列の正確な形式に関する保証はありません。しかし、実装では妥当なもっとも近い形式を使用するために最善の努力を行うべきです。たとえば、JavaScript エンジンが数値 237 に対して整数表現を使用する場合、Java 文字列は "237.0" などではなく、"237" になるべきです。
これらの変換された文字列をほかの文字列値と比較するには、== 演算子ではなく equals() メソッドを使用します。
|
| boolean |
- 0 または非数の値は、false に変換されます。
- その他の値は、true に変換されます。
|
JavaScript のブール値は、Java メソッドにパラメータとして渡されたときに次の規則に従って変換されます。
| Java パラメータの型 |
変換規則 |
| boolean |
すべての値は、Java の同等な値に直接変換されます。
|
java.lang.Boolean java.lang.Object
|
java.lang.Boolean のインスタンスが新規作成されます。同じ基本値を持つ 1 つのインスタンスではなく、パラメータごとに新しいインスタンスが作成されます。
|
| java.lang.String
|
値は、文字列に変換されます。true は "true" になり、false は "false" になります。これらの変換された文字列をほかの文字列値と比較するには、== 演算子ではなく equals() メソッドを使用します。
|
byte char short int long float double
|
true は 1 になり、false は 0 になります。
|
JavaScript の文字列は、Java メソッドにパラメータとして渡されたときに次の規則に従って変換されます。
| Java パラメータの型 |
変換規則 |
java.lang.String java.lang.Object
|
java.lang.String のインスタンスが新規作成され、JavaScript 文字列の Unicode 文字が格納されます。
|
byte short int long float double
|
適切な Java ボクシング型の valueOf メソッド (java.lang.Byte.valueOf()、java.lang.Short.valueOf() など) を呼び出すことによって、文字列が基本型に変換されます。
|
|
char
|
java.lang.Short.decode() メソッドを使用して文字列が基本 short 値に変換され、そこから基本 char 値にキャストされます。
|
| boolean |
空の文字列は false になります。その他の値はすべて true になります。
|
JavaScript の配列は、Java メソッドにパラメータとして渡されたときに次の規則に従って変換されます。
| Java パラメータの型 |
変換規則 |
1 次元配列型: byte[] short[] char[] int[] long[] float[] double[] オブジェクト型配列 (Object[]、String[] など)
|
新しい Java 配列が割り当てられ、この節で説明されている変換規則を使用して JavaScript 配列の各要素が適切な型に変換されます。JavaScript 配列が疎である (一部の要素が定義されていない) 場合、それらの値は基本値であれば 0、オブジェクト値であれば null に変換されます。この節で説明されている規則に従って JavaScript 配列の値を変換できなかった場合は、JavaScript エンジンに例外が発生します。
|
多次元配列型: (int[][]、String[][] など)
|
新しい Java 配列が割り当てられ、この節で説明されている規則 (この規則と 1 次元配列の基本事例を含む) に従って各要素が変換されます。
|
| java.lang.String
|
JavaScript エンジンの規則に従って JavaScript 配列が文字列表現に変換されます。これらの変換された文字列をほかの文字列値と比較するには、== 演算子ではなく equals() メソッドを使用します。
|
Java に渡されるその他の JavaScript オブジェクトは、次の規則に従って変換されます。
| Java パラメータの型 |
変換規則 |
| netscape.javascript.JSObject
|
JavaScript オブジェクトへの参照が Java に渡されます。
|
java.lang.String
|
オブジェクトの toString メソッドが呼び出され、その結果が新しい String インスタンスとして返されます。
|
前に JavaScript エンジンに返された Java オブジェクトが再び Java に渡されると、そのオブジェクトに目的の型との代入互換性があるかどうかだけが確認されます。この規則の唯一の例外は、java.lang.String への自動変換です。
| Java パラメータの型 |
変換規則 |
| Java オブジェクトと代入互換性があるクラスまたはインタフェース型
|
元のオブジェクトが Java に再び渡されます。
|
java.lang.String
|
元のオブジェクトの toString メソッドが呼び出され、その結果が新しい String インスタンスとして返されます。
|
JavaScript の null 値または undefined 値は、Java メソッドにパラメータとして渡されたときに次の規則に従って変換されます。
| Java パラメータの型 |
変換規則 |
任意のクラス 任意のインタフェース型
|
値は null になります。
|
byte char short int long float double
|
値は 0 になります。
|
|
boolean
|
値は false になります。
|
特定のアプレットインスタンスに対する最初の JavaScript-to-Java 呼び出しは、次のいずれかの場合に可能です。
- そのアプレットインスタンスの
Applet.init() が完了している場合
- アプレットが最初の Java-to-JavaScript 呼び出しを行う場合
Java Plug-In は、ブラウザが特定のアプレットに対して JavaScript-to-Java 呼び出しを行おうとした場合に、次のいずれかが起きるまでブラウザが JavaScript エンジンに制御を返さずに待機することを保証します。
- そのアプレットインスタンスの
Applet.init() が完了する
- アプレットが最初の Java-to-JavaScript 呼び出しを行う
- アプレットの初期化中にエラーが発生する
前述の (1) と (2) については、どちらかの条件が満たされると、そのアプレットインスタンスに対する後続のすべての JavaScript-to-Java 呼び出しがすぐに実行されます。(3) については、そのアプレットインスタンスに対する最初および後続の JavaScript-to-Java 呼び出しで、JavaScript エンジンに例外が発生します。
デフォルトでは、特定のアプレットに対する以前の呼び出しで返された特定のアプレットまたはオブジェクトに対して JavaScript-to-Java 呼び出しを行うと、その呼び出しは Java 側でそのアプレットに関連付けられたワークスレッドによって処理されます。ワークスレッドは、ユーザーコードによって作成されたどのスレッドとも異なることが保証され、各アプレットに 1 つだけ作成されます。
アプレットが Java-to-JavaScript 呼び出しを行い、その結果として JavaScript-to-Java 呼び出しが行われると、その呼び出しは Java-to-JavaScript 呼び出しを行なったスレッド上で行われます。これは、ラウンドトリップのシナリオと呼ばれます。ラウンドトリップの JavaScript-to-Java 呼び出しは Java から JavaScript へのダウンコールに対応して行われるため、開始スレッドはアップコールを処理するスレッドです。
最近の Web ブラウザの JavaScript エンジンは、Web ページに関して概念的にシングルスレッド化されています。しかし、Java 言語は本来マルチスレッド化されています。複数の Java スレッドが JavaScript エンジンを呼び出そうとすると、常に 1 つのスレッドだけが許可されます。ほかのスレッドは、最初の Java-to-JavaScript 呼び出しの完了または JavaScript-to-Java 呼び出しの実行のどちらかが発生するまで待機します。どちらの場合も、JavaScript エンジンは再び Java-to-JavaScript 呼び出しが可能な状態になります。複数の Java スレッドが JavaScript エンジンを同時に呼び出そうとした場合は、呼び出しを続行するためにいずれかのスレッドが任意に選択されます。
マルチスレッドに関するこれらの規則により、求められているシングルスレッドによる方法だけで JavaScript エンジンへのアクセスが行われることが保証されます。ただし、ユーザーコードでは、望ましい結果を得るために、依然としてマルチスレッド化された状況では適切な同期を行う必要があります。
Web ブラウザから返されたすべての Java オブジェクトのスコープは、特定のアプレットインスタンスの内部です。アプレット自体が JavaScript エンジンに公開される最初のオブジェクトです。そのアプレットオブジェクト (アプレットごとの Packages キーワードを含む) の呼び出しによって、JavaScript エンジンにほかの Java オブジェクトが返される可能性があります。これらのオブジェクトは、オブジェクトのメソッド呼び出しやフィールド取得などで返される可能性がある後続のオブジェクトと同様に、オブジェクトを返したアプレットインスタンスに関連付けられています。
通常、JavaScript エンジンによって保持される Java オブジェクトへの参照は持続的参照として機能するため、そのオブジェクトはホスト JVM 内でガベージとして収集されません。JavaScript エンジンが参照を削除したあとは、JavaScript エンジンのガベージコレクタが実行され、最終的には対象となる Java オブジェクトへの持続的参照が、到達不可能になったときに JVM のガベージコレクタによって削除され、あとで再生されます。
しかし、Web ページ上の特定のアプレットが (たとえば、HTML 文書オブジェクトモデルからそのアプレットを削除したり、別の Web ページに切り替えたりすることによって) 破棄されると、JavaScript エンジンがそのアプレットインスタンスの内部をスコープとする Java オブジェクトに保持していた参照はすぐに無効化されます。関連する Java オブジェクトは (それらのオブジェクトへの持続的参照がほかに存在しなければ) すぐにガベージコレクションの対象になり、それらのオブジェクトへの JavaScript 参照に対してさらに操作を行おうとすると、JavaScript エンジンに例外が発生します。
新しい Java Plug-In は、複数の JVM インスタンスの Web ブラウザへの同時接続をサポートします。同じ Web ページにホストされた 2 つのアプレットは、実際には 2 つの別個の JVM で実行される可能性があります。これは、Web ブラウザ内の JavaScript エンジンとの対話にどのように影響するでしょうか。
意外にも、影響はおそらくほとんどありません。Web ページ上の JavaScript は、アプレットがどの JVM インスタンスで実行されているかに関係なく、どのアプレットとも同じように対話できます。JavaScript を使用して、あるアプレットから Java オブジェクトを取得し、別のアプレットに渡すことができます。追加設定される規則は次の 1 つだけです。
- Java オブジェクトを特定の JVM インスタンスに (メソッドのパラメータやフィールドの値などとして) 渡すときは、その JVM インスタンスがそのオブジェクトの供給元である必要がある。
JVM インスタンス間でのオブジェクトの直列化はサポートされません。オブジェクトをその供給元の JVM インスタンスから別のインスタンスに渡そうとすると、JavaScript エンジンに例外が発生します。
Java プラットフォームのセキュリティーアーキテクチャーは、セキュリティー保護されたサンドボックスという概念を提供します。未署名の信頼できないコードは、制限されたアクセス権セットを使用して実行されます。特に、インターネットからダウンロードされた Java バイトコードは、ローカルファイルシステムに直接アクセスできず、コードの供給元のネットワークホストにしか接続できません。これらの制限により、未知のコードがユーザーのプライバシーを侵害したり、ユーザーのコンピュータを使用して任意のネットワークホストに対してサービス拒否攻撃を実行したりする可能性はありません。
ネットワーク接続の実行など、特定のアクセス権を必要とする操作が試行されると、Java プラットフォームのセキュリティーマネージャーが現在のスレッドで実行されているすべてのコードを列挙します。これには、信頼できるコードと信頼できないコード (特に、複数の供給元を持つ信頼できないコード) の両方が含まれます。セキュリティーマネージャーは、現在実行中のすべてのコードに関連付けられたアクセス権セットの共通部分を取得します。この最小限の共通部分は、現時点で許可されるアクセス権を表し、操作の実行に必要なアクセス権と比較されます。現在のアクセス権が操作の実行に必要なアクセス権を満たす場合は、処理が続行されます。満たさない場合は、SecurityException が発生します。
前述のように、LiveConnect がない場合、信頼できないアプレットはそのコードの供給元であるホスト (通常は codebase、ただし archive パラメータには絶対 URL を指定可能) に接続できます。しかし、Web ページ上の JavaScript コードがアプレットを呼び出して、代わりに操作を実行させる場合は、追加のセキュリティー対策を実施する必要があります。これを怠ると、任意の Web ページが特定のサイト上にホストされたアプレットを再利用し、そのアプレットを使用してそのサイトにネットワーク接続する可能性があります。
以前のバージョンの Java Plug-In では、JavaScript-to-Java 呼び出しのセキュリティーがブラウザごとにやや異なる方法でモデル化されていました。これは、Java 仮想マシンを Web ブラウザにフックする方法や、ブラウザと Java Plug-In のどちらがセキュリティーモデルの特定部分の適用を担当するかが、ブラウザごとに異なるためです。新しい Java Plug-In の導入により、この動作はブラウザ間で統一され、次のように要約されます。
- Web ページ上の JavaScript コードは、ページ上のアプレットに対していつでも JavaScript-to-Java 呼び出しを行うことができます。
MAYSCRIPT 属性は不要になりました。
- JavaScript-to-Java 呼び出しを行うと、その JavaScript コードは、コードの供給元がドキュメントベース (ドキュメントが格納されているディレクトリの URL) である信頼できないアプレットから供給されたものとしてモデル化されます。
直前で述べたことはとても単純ですが、いくつかの微妙な影響を及ぼします。
- HTML ドキュメントとアプレットがどちらも同じサイトから供給された場合は、Web ページ上の JavaScript が代わりに供給元のホストへのネットワーク接続を行うことができます。
- HTML ドキュメントとアプレットが異なるサイトから供給された場合は、Web ページ上の JavaScript が代わりにネットワーク接続を行うことはできません。
- 「署名付き JavaScript」という概念は、一般には使用されない Mozilla 独自の機能だったため、サポートされなくなりました。より高いレベルのアクセス権を JavaScript コードに付与することはできません。開発者は、
AccessController.doPrivileged を使用して信頼できない JavaScript コードにむやみに追加のアクセス権を与えるアプレットの API を公開しないように注意してください。より高いレベルのアクセス権を JavaScript コードに付与する必要がある開発者には、検証可能な HTTPS 接続を介してアプレットを提供し、アプレットをホストしている Web ページのドキュメントベースがアプレットのコードの予想される供給元と同じであることを保証するためのチェックを行うことをお勧めします。
- Web ページには複数のサイトに由来する JavaScript コードを含めることができますが、Java Plug-In には JavaScript コードの各部分の供給元を参照する機能はありません。Web ページ上で実行されるアプレットに対して望ましくない呼び出しを行うおそれがある信頼できないサイトから JavaScript コードをインポートしないようにすることが、Web ページの開発者の責任です。
新しい LiveConnect のセキュリティーモデルの動作は、どのブラウザでも同じであり、以前の明示されなかった動作に比べて大幅に理解と説明がしやすくなりました。実際のアプリケーションを使用したテストでは、新しいセキュリティーモデルが高い下位互換性を備えていることが示されました。特に新しいセキュリティーモデルで互換性の問題が発生した場合は、フィードバックをお寄せください。
Java アプレットは、その周囲の Web ページや Web ブラウザ内の JavaScript エンジンと対話できます。Java コードは、JavaScript 関数の呼び出し、JavaScript オブジェクトのフィールドの取得、設定、削除、JavaScript 配列の要素の取得と設定、および JavaScript コードのスニペットの評価を行えます。Java アプレットは、JavaScript DOM API または Java Common DOM API を介して HTML 要素を追加、削除、移動することで、Web ページを動的に変更できます。次の各節では、サポートされるすべての操作、および Java 仮想マシンと Web ブラウザ内の JavaScript エンジンとの間で値の受け渡しを行う方法について説明します。
Java コード内では、JavaScript オブジェクトのすべてのインスタンスが netscape.javascript.JSObject のインスタンスとして表されます。JavaScript から Java コードのメソッドを呼び出すときは、JavaScript オブジェクトを引数として渡すことができます。そのためには、メソッドの対応する正式なパラメータを JSObject 型として定義する必要があります。
また、Java アプレットは JSObject クラスの static な getWindow メソッドを呼び出し、アプレットインスタンスを引数として渡すことにより、Java-to-JavaScript 通信をブートストラップすることもできます。このメソッドは、アプレットを含む JavaScript ウィンドウオブジェクトへの参照を取得します。この参照は、その後の評価、関数の呼び出し、変数の取得などに使用できます。
Java コード内で JavaScript オブジェクトを使用するときは、netscape.javascript.JSException 型の例外を処理する try...catch ステートメントの内部に JavaScript オブジェクトの呼び出しを配置してください。これによって、Java コードで JavaScript コード実行時のエラーを処理できます。これらのエラーは、Java では JSException 型の例外として表されます。
例
- Java コード:
public class JavaJSTest extends Applet {
public void start() {
JSObject window = JSObject.getWindow(this);
// Test function calls
String str = (String) window.eval("getString();");
if (!str.equals("Hello, world!")) {
throw new RuntimeException(); // test failed
}
Number num = (Number) window.eval("getNumber()");
if (num.intValue() != 5) {
throw new RuntimeException(); // test failed
}
// Test field access
JSObject res = (JSObject) window.eval("new cities();");
if (!((String) res.getMember("b")).equals("Belgrade")) {
throw new RuntimeException(); // test failed
}
res.setMember("b", "Belfast");
if (!res.getMember("b").equals("Belfast")) {
throw new RuntimeException(); // test failed
}
res.removeMember("b");
try {
res.getMember("b");
throw new RuntimeException(); // test failed
} catch (JSException e) {
// Member should not be present any more
}
// Test array access
res = (JSObject) window.eval("getTestArray();");
if (!((String) res.getSlot(0)).equals("foo") ||
!((String) res.getSlot(1)).equals("bar")) {
throw new RuntimeException(); // test failed
}
res.setSlot(1, "baz");
if (!((String) res.getSlot(1)).equals("baz")) {
throw new RuntimeException(); // test failed
}
res.setSlot(2, "qux"); // Define new array element
if (!((String) res.getSlot(2)).equals("qux")) {
throw new RuntimeException(); // test failed
}
}
}
- Web ページと JavaScript コード:
<applet id="app"
archive="examples.jar"
code="JavaJSTest" ...>
</applet>
<script language="javascript">
// Return a string value to Java
function getString() {
return "Hello, world!";
}
function getNumber() {
return 5;
}
// Make an object with city names and an index letter.
function cities() {
this.a = "Athens";
this.b = "Belgrade";
this.c = "Cairo";
}
function getTestArray() {
return [ "foo", "bar" ];
}
</script>
- 実行可能なテストケース
- 完全なソースコード (JavaJSTest.html / .java)
新しい Java Plug-In は、JSException クラスの基本機能 (特に、その詳細なメッセージとスタックトレース) のみを利用します。JavaScript から特定のオブジェクトをスローする機能や、そのオブジェクトを Java 側の JSException で利用可能にする機能は、Web ブラウザ間で移植できないため、使用しないください。
JavaScript を呼び出すには、Java コードで netscape.javascript.JSObject クラスと netscape.javascript.JSException クラスを使用します。Java 2, Standard Edition version 1.4 のリリース以降、これらのクラスは Java Development Kit または Java Runtime Environment に含まれる jar ファイル jre/lib/plugin.jar で提供されています。これらの JavaScript クラスを参照する場合は、コンパイル時クラスパスに plugin.jar を追加する必要があります。このためには、Java IDE を使用するか (Java IDE を使用している場合)、または Java コンパイラ javac に -classpath コマンド行引数を渡します。
実行時は、Java Plug-In が自動的にこれらのクラスをアプレットで使用できるようにするため、アプレットやアプレットの設定方法を変更する必要はありません。
Java から JavaScript に渡された値は、次のように変換されます。
- Java の数値用のボクシングオブジェクト (
java.lang の Byte、Character、Short、Int、Long、Float、および Double クラス) は、アンボクシングされ、利用可能なもっとも近い JavaScript の数値型に変換されます。ただし、メソッドからの戻り値の型として宣言されている場合や、アプレットごとの Packages キーワードを使用した new 式の結果である場合を除きます。これらの場合、ボクシングオブジェクトは Java オブジェクトとして JavaScript エンジンに返されます。
- Java の Boolean は、JavaScript のブール値に変換されます。ただし、メソッドからの戻り値の型として宣言されている場合や、アプレットごとの
Packages キーワードを使用した new 式の結果である場合を除きます。これらの場合、ボクシングオブジェクトは Java オブジェクトとして JavaScript エンジンに返されます。
- Java の String は JavaScript の文字列に変換されます。
ただし、アプレットごとの Packages キーワードを使用した new 式の結果である場合を除きます。この場合、Java の String は Java オブジェクトとして JavaScript エンジンに返されます。
-
netscape.javascript.JSObject クラスのオブジェクトは、元の JavaScript オブジェクトに変換されます。
- Java 配列は、JavaScript の疑似配列オブジェクトに変換されます。このオブジェクトの動作は、JavaScript の Array オブジェクトとほぼ同じです。arrayName[index] という構文で要素の設定と取得を行い、arrayName.length でその長さを判定できます。
- その他のクラスの Java オブジェクトは、JavaScript ラッパーに変換されます。このラッパーを使用して、Java オブジェクトのメソッドとフィールドにアクセスできます。
netscape.javascript パッケージの公式な javadoc は、次の場所にあります。
netscape.javascript パッケージの Javadoc
- オフライン javadoc (.zip アーカイブ)
Java のプラットフォームと仮想マシン実装は、(数百個までいかないとしても) 数十個の Java 以外のプログラミング言語実装をホストします。JRuby、Jython、Groovy、Scala、JavaFX Script などは、JVM をターゲットとする有名な言語のほんの一部です。Wikipedia の記事に、ほかの例が数多く示されています。
任意の言語でグラフィカルな Java プログラムを記述し、それらをアプレットコンテナに埋め込んで Web ページ上で表示できるようにすることが目標として望まれます。これはすでに可能であり、現在ではこの機能のサポートを提供するプログラミング言語実装もあります。
また、Web ブラウザ内の JavaScript を使用して、これらの Java 以外のアプレットの動作に影響を与えることができるようにすることも望まれます。JavaScript の構文を特定の JVM ベースの言語に「マップ」して、メソッドの呼び出し、フィールドへのアクセス、その他の操作を可能にするという考え方もあります。難点は、JVM にホストされる Java 以外の言語のほとんどで、名前の符号化やデータ型の代替表現など、この仕様で提示されているデフォルトのオブジェクトアクセスやデータ型変換規則とうまく連携しない方法が使用されていることです。
Java 以外のアプレットのスクリプト作成を可能にするため、新しい Java Plug-In は言語間 LiveConnect ブリッジを導入しました。JVM にホストされる言語の実行システムは、簡単な JavaScript の構文を使用してその言語のオブジェクトにアクセスできるようにするデリゲートを、このブリッジに登録できます。言語の実装者は、メソッドの呼び出しやフィールドへのアクセスが (ブラウザの JavaScript 構文の規則に従って) その言語内のオブジェクトにどのように作用するかや、JavaScript エンジンとその言語の間で値をどのように相互変換するかについて、すべて意思決定できます。このブリッジは複数言語との同時対話をサポートするように設計されているため、アプリケーションのある部分をある言語で記述し、別の部分を別の言語で記述しても、Web ページ上の JavaScript コードはその両方と対話できます。
言語間 LiveConnect ブリッジの Javadoc へのリンクは、次の節にあります。このブリッジを利用した最初の言語実装は、JavaFX Script です。JavaFX アプレットが新しい Java Plug-In とともに実行されると、Web ページ上の JavaScript はメインスクリプトとの対話、シーングラフへの下降、JavaFX Script 関数の呼び出しなどを行うことができます。JavaScript フィールドへのアクセスやその他の操作は、JavaFX コンパイラのランタイムによって JavaFX Script にマップされます。JavaScript / JavaFX Script ブリッジのソースコードの大部分は、JavaFX Script compiler で参照できます。
言語間 LiveConnect ブリッジの javadoc は、次の場所にあります。
- 言語間 LiveConnect ブリッジの Javadoc
- オフライン javadoc (.zip アーカイブ)
この仕様の適合性テストはまだ完成していません。Sun Microsystems, Inc. は、Java Plug-In テクノロジの実装によって広範囲のプラットフォーム、Web ブラウザ、およびハードウェアデバイスで JavaScript と Java コンテンツ間の相互運用が可能になることを保証するテストケースの作成をコミュニティーに支援していただきたいと考えています。次の一連のテストは、当初 Netscape 社によって開発されたもので、新しい Java Plug-In の LiveConnect 実装のいくつかの基本機能を検証します。これは包括的な適合性テストスイートではありません。このテストスイートには、新しい Java Plug-In が必要です。以前のバージョンの Java Plug-In では動作しません。
この仕様の参考資料として「Core JavaScript 1.5 Guide」の「LiveConnect Overview」と以前の LiveConnect 適合性テストの使用を許可していただき、新しい Java Plug-In を Firefox 3 の全機能とともに動作させるために協力していただいた Mozilla に謝意を表します。
この仕様および次世代 Java Plug-In 全般に関するフィードバックを Java Plug-In forum までお送りください。
この仕様から説明や修正が必要な部分を発見した場合は、Sun Bug Reporter を使って報告してください。最初に、Sun Bug Database の Java Plugin カテゴリ内にあるサブカテゴリ plugin2 で、バグがすでに報告済みかどうかを確認してください。バグを提出するときは、「Product/Category」に「Java Plug-in」を、「Subcategory」に「plugin2」を指定してください。LiveConnect 実装のバグや、動作が仕様と一致しない部分を報告する場合は、次の情報を入力してください。
- オペレーティングシステムの名前とバージョン
- Web ブラウザの名前とバージョン
- 自己完結型のテストケース
- 障害の再現方法に関する明確な指示
- 障害モードの内容 (つまり、期待される動作と現実の動作)
新しい LiveConnect 実装の信頼性と移植性の向上にご協力いただき、ありがとうございます。
|