User Interface Research Group - Masatomo Kobayashi - アプレットのメモ
注意:以下の記事は単なるナンセンス特集であり、決してその内容を他に推奨するものではありません。このドキュメントに従ってJavaアプレットとXHTML文書を構成すると、いくつかの古い環境で致命的な問題を引き起こします。
試行錯誤の記録です。主に、次の二点を意図しています。
大まかには、以下のような制約が付きます。
<applet>を使えない(<object>を使う必要がある)。何故、IE+Microsoft VMなのか――非常に多くのユーザが、この組み合わせでWebを閲覧しているからです。何故、XHTML Basicなのか――趣味です。
Microsoft VM用のクラスファイル形式は、Sun VM 1.1用のものと互換です。最新版のSun純正コンパイラを使う場合、コマンドは次のようになります。
>javac -target 1.1 Karateka.java
-target 1.1オプションは、単にクラスファイルの形式をVM 1.1互換にするだけです。非VM 1.1のクラスやメソッドに対して警告を出す訳ではありません。
クラスファイルやリソース類をアーカイブにまとめるには、
>jar cvf karateka.jar *.class *.txt *.png
マニフェストファイルに以下の行を加えれば、実行可能jarファイルになります。
Main-Class: Karateka
この場合、当然ながらKaratekaクラスにmain(String[])メソッドが必要です。
Microsoft VMはSun VM 1.1と完全に互換ではありません。「マルチプラットフォーム」というJava言語の大義名分を考慮して、ここでは両者の共通部分を利用する事にします。
Microsoft VMでしか使えないもの。
Microsoft VMにおいて、使えそうで使えないもの。
以下は(使いたいのに使えない)Java 2で追加された機能のリスト。一部の機能については別の方法で実現可能です。
Microsoft VMでは、Graphics#getClipBounds()の返値がGraphics#setClip(Shape)の引数と対応しない場合があります。Java 1.4のAPI仕様には、getClipBounds()について:
クリップが設定されていない場合、またはクリップがsetClip(null)を使用してクリアされている場合は、このメソッドはnullを返します。
とありますが、Microsoft VMではこのような場合にRectangle[x=0, y=0, width=0x7FFF, height=0x7FFF]を返します。逆に、setClip(Rectangle[x=0, y=0, width=0, height=0])の直後にgetClipBounds()を呼び出すと、nullが返ります。
JDK 1.1のドキュメントにはこの仕様に関する言及がありませんから、Java 2よりも前のバージョンではこのような実装が許されていたのかもしれません。
XHTML Basicドキュメント型はapplet要素を含んでいません。ドキュメント中にアプレットを埋め込むためには、object要素を使う必要があります。appletタグによる記述:
<applet codebase="./" code="Karateka.class" archive="karateka.jar" alt="カラテカ" width="256" height="224">
<param name="vendor" value="SOFT PRO" />
<param name="year" value="1985" />
</applet>
は、objectタグを用いて以下のように書き換えられます。
<object codetype="application/java" codebase="./" classid="java:Karateka" archive="karateka.jar" width="256" height="224">
<param name="archive" value="karateka.jar" />
<param name="vendor" value="SOFT PRO" />
<param name="year" value="1985" />
カラテカ
</object>
IEはobject要素のarchive属性を無視するため、関連ファイルがアーカイブ化されている場合は上記のように<param name="archive" value="karateka.jar" />を付け加える必要があります。
なお、IEではobject要素によってアプレットを埋め込むと標準のフォントや配色にIEの設定が反映されるようになります。デフォルトのフォントサイズや背景色を仮定しているアプレットは、移行の際に注意が必要です。
前節までの内容に注意すれば、基本的なアプレットを妥当なXHTML Basic文書に埋め込み、IE+Microsoft VM環境で動作させる事ができます。
この方法では、しかし外部リソースからサウンドや画像を読み込む場合に不具合が残されています。具体的には、object要素によって埋め込まれたアプレット内でApplet#getAudioClip(...)やApplet#getImage(...)を呼び出すとNullPointerExceptionが発生してしまいます。この例外はcom.ms.applet.AppletPanel内で生じるため、手に負えません。
上述の例外は、より正確にはclassid="java:Karateka"のようにクラスファイルを指定した場合に発生し、code="Karateka.class"の場合には正常に動作します。
IEでは<object code="Karateka.class">等というデタラメな記述が有効です。そこで、code="Karateka.class"の部分がXHTML Basic的ではないという事実には目を瞑り、
<object codetype="application/java" codebase="./" code="Karateka.class" width="256" height="224">
<param name="archive" value="karateka.jar" />
<param name="vendor" value="SOFT PRO" />
<param name="year" value="1985" />
<object codetype="application/java" codebase="./" classid="java:Karateka" archive="karateka.jar" width="256" height="224">
<param name="vendor" value="SOFT PRO" />
<param name="year" value="1985" />
カラテカ
</object>
</object>
のようにしてみます。代替object要素を入れ子にして、IE向けの記述に対応できないUA上でもアプレットが正常に起動される事を狙った記述です。実際、Netscape 7等は期待通りに動作しました。しかし、当のIEが代替要素までまとめて表示しようとしてしまうため(当然、代替要素の方はエラーを起こします)、この方法は採用できません。
なお、八方美人な記述:
<object codetype="application/java" codebase="./" classid="java:Karateka" code="Karateka.class" archive="karateka.jar" width="256" height="224">
<param name="archive" value="karateka.jar" />
<param name="vendor" value="SOFT PRO" />
<param name="year" value="1985" />
カラテカ
</object>
は更にダメです。
こうなるとJavaScript等を使ってUA毎の出力を切り替えたくなりますが、残念ながらXHTML Basicドキュメント型はscript要素を持っていません。しかし、こんな事のためにCGIやSSIを使うのも気が引けます。
仕方なく、Javaのソースコードの方を書き換える事にします。調べてみると、ToolkitクラスのgetImage(URL)メソッドは正常に使える事が分かったので、Imageオブジェクトの読み込み:
Image hero, heroine, boss;
hero = getImage(getDocumentBase(), getParameter("karateka"));
heroine = getImage(getDocumentBase(), getParameter("mariko_hime"));
boss = getImage(getDocumentBase(), getParameter("akuma_shogun"));
を下記のように置換します。
import java.net.*;
Image hero, heroine, boss;
try {
hero = getToolkit().getImage(new URL(getDocumentBase(), getParameter("karateka")));
heroine = getToolkit().getImage(new URL(getDocumentBase(), getParameter("mariko_hime")));
boss = getToolkit().getImage(new URL(getDocumentBase(), getParameter("akuma_shogun")));
} catch (MalformedURLException e) {
// 深刻なエラーヽ(`Д´)/
}
また、更に画期的な方法も可能です。
Image hero, heroine, boss;
char[] karateka_image_data = { ... };
char[] mariko_hime_image_data = { ... };
char[] akuma_shogun_image_data = { ... };
hero = getToolkit().createImage(karateka_image_data);
heroine = getToolkit().createImage(mariko_hime_image_data);
boss = getToolkit().createImage(akuma_shogun_image_data);
ToolkitクラスにはAudioClipを返すメソッドが存在しないため、このように作成されたアプレットではサウンドを扱えません。ここでは、きっぱりとサウンドを諦める事にします。そもそもAUフォーマットのファイルしか読み込めない状況で、ロクな音を鳴らせるはずもありませんので<負け惜しみ。
sun.audio、sun.awt.imageパッケージを使えばIE+Microsoft VMを含む大概の環境でサウンドと画像を扱えるはずですが、Pure Javaでなくなると気持ち悪いのでこの方法は見送りました(詳細情報)。
本文中、
内容や記述の誤り、必要な情報の欠落等、お気付きの点を下記筆者までお知らせ下さい。
KOBAYASHI Masatomo <kobayash at is.s.u-tokyo.ac.jp>