最強の柔軟性Java内JavaScript
これまでのサンプルは古いJREでもほとんどサポートされている機能を使っていましたが、JRE 1.6では、アプレットと組み合わせることでアプレットの可能性を飛躍的に高める新しい機能Java Script APIが導入されました。Java Script APIは、Javaプラットフォーム上でスクリプト言語を実行する仕組みです。この仕組みは、Microsoft .NETにおけるIron Pythonと似ています。
Java Script APIの実装を用いることによって、コンパイルすることなく、テキストをスクリプト言語として実行することができます。もちろん、すべてのJavaオブジェクトにアクセス可能であり、Java標準クラスライブラリを利用することができます。Java Script APIは、スクリプト制御のためのインターフェイスですが、実装としてJavaScriptが用意されています。Javaの中でJavaScriptを動かすことができるJava Script APIです。なんだかややこしい話です。
先ほどはHTMLに組み込まれた外部のJavaScriptからアプレットのメソッドを呼び出すというものでしたが、Java Script APIで実行するJavaScriptは、Javaプラットフォームの内部で動作します。つまり、Java言語のように、JavaScriptをJavaアプリケーション開発用言語として使うことができるということになります。ただし、Java Script APIで実行するスクリプトは実行時に処理されるため、コンパイルの必要はありません。
これまで解説してきたJavaアプレットにパラメータを渡したり、アプレットの内部でサーバーからデータを取得する方法と組み合わせることによって、動的にアプレットに渡したスクリプトを内部で実行させるといったことが可能になります。Javaアプレット開発の大きな欠点にコンパイルという作業がありましたが、Java Script APIを利用することで外部のスクリプトを読み込ませて実行させることができるのです。
スクリプトをJavaで実行させる方法はそれほど難しくありません。まず、Java Script APIを使うにはjavax.scriptパッケージをインポートします。
import javax.script.*;
Java Script APIは、JRE 1.6以降で追加されたものなので、古いJavaで実行することはできません。注意してください。
次に、javax.script.ScriptEngineManager
クラスをインスタンス化します。このクラスは、スクリプトエンジンの検出や生成を行ってくれるファクトリです。Java Script APIにはRhinoをベースとしたJavaScriptエンジンがバンドルされていますが、別のスクリプトエンジンを開発して組み込むことも可能になっています。
java.lang.Object +---javax.script.ScriptEngineManager
public class ScriptEngineManager extends Object
このクラスはパラメータを受けとらないコンストラクタを公開しています。コンストラクタから、インスタンスを生成してください。
public ScriptEngineManager()
ScriptEngineManagerのインスタンスが生成できれば、次にスクリプトエンジンを取得します。実装されているスクリプトエンジンの詳細を知る必要はありません。どのようなスクリプト言語も、javax.script.ScriptEngineインターフェイスから操作することができます。
public interface ScriptEngine
ScriptEngineを取得するにはScriptEngineManager
オブジェクトのgetEngineByName()
メソッドを使います。このメソッドは、与えられた名前からスクリプトエンジンを自動的に検出してくれます。
public ScriptEngine getEngineByName(String shortName)
shortNameには、ScriptEngineの短い名前を指定します。JavaScriptのScriptEngineを取得するには"JavaScript"を指定します。
ScriptEngineインターフェイスを実装するオブジェクトを取得することができれば、実行するのは簡単です。ScriptEngineインターフェイスが公開しているeval()
メソッドに、スクリプト言語のソースを文字列として渡します。
Object eval(String script) throws ScriptException Object eval(Reader reader) throws ScriptException
scriptパラメータに、ソースとなる文字列を渡してください。このメソッドはオーバーライドされているため、Reader
オブジェクトからも入力することができます。ファイルなどをソースとする場合、この方が効率的なこともあるでしょう。
実行されるJavaScriptから、Javaの標準クラスライブラリを利用することができるため、スクリプトからでも、自由にオブジェクトを生成することができます。ただし、スクリプトを起動したコードは見えないため、スクリプトと、スクリプトを起動したJavaのコードを通信させる必要があります。例えば、アプレット内でJavaScriptを実行させる場合、必然と実行中のアプレットを表すApplet
オブジェクトがスクリプト内でも必要になります。このような場合はput()
メソッドを使ってスクリプトに名前付きの値を送ることができます。
void put(String key, Object value)
keyには名前を、valueには名前に関連付ける値を指定します。このメソッドからスクリプト内で利用したいオブジェクトをvalueとして渡せば、keyに指定した名前を変数としてスクリプトからアクセスすることができるようになります。
import javax.swing.*; import java.net.*; import java.io.*; import javax.script.*; public class Test extends JApplet { public void start() { String paramURL = getParameter("url"); if (paramURL == null) { getContentPane().add( new JLabel("URL が指定されていません")); } else try { URL url = new URL(getDocumentBase(), paramURL); InputStream stream = url.openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); String text = ""; while(true) { String temp = reader.readLine(); if (temp == null) break; else text += temp; } reader.close(); stream.close(); ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); engine.put("applet", this); engine.eval(text); } catch(Exception err) { getContentPane().add(new JLabel(err.toString())); } } }
<html> <head> <title>sample</title> </head> <body> <applet code="Test.class" width="400" height="200"> <param name="url" value="test.txt" /> </applet> </body> </html>
importPackage(java.awt); importPackage(javax.swing); var label = new JLabel("ぱんぽんぱぱーん"); var button = new JButton("ぴぴるぴるぴる"); applet.getContentPane().removeAll(); applet.getContentPane().add(label); applet.getContentPane().add(button, BorderLayout.NORTH);
sample04は、HTMLソース内で指定されたurlパラメータの値に基づいて、アプレット実行時にサーバーからソースファイルを取得してスクリプトを実行するというサンプルです。実行するスクリプトのソースファイルのアドレスはHTMLから変更することができ、スクリプトを変更すればアプレットに表示される結果も自由に変更することができるという高い柔軟性を持っていることが確認できます。サーバー側スクリプトで動的にJavaScriptを生成してアプレットに実行させる、というような処理も可能になってきます。
まとめ
では、Ajaxの利点として挙げたリストをもう一度振り返ってみましょう。
- HTMLに組み込むことができる
これは、サンプルの実行結果を見ていただいたとおりです。JavaアプレットはもともとHTMLに組み込むための技術なので、文書との相性がよく、ブラウザとは別のソフトウェアを使っているという違和感をユーザーに与えません。
- URIからWebサーバーに問い合わせ、結果を取得できる
Javaアプレットは、自分自身を配信したサーバーに対してリクエストすることができます。この条件はAjaxと同じですが、アプレットはソケットレベルの通信を行うことができるため、入出力のフォーマットにも制限はありません。例えば、クライアント側で生成した画像データをサーバーにアップロードするといったことができます。この仕組みは、以前からいわゆるお絵かき掲示板などで使われてきました。
- Document Object Modelによる文書の操作
本稿では省略していますが、Javaは優れたクラスライブラリを保有しています。javax.xml以下のパッケージと、org.w3c.domパッケージを使うことでW3Cが勧告するDocument Object Modelに従ったXML処理を行うことができます。そのため、アプレットからWebサービスに接続してXML形式のデータを取得し、その情報を基にスクリプトからプレゼンテーションを構築するといった柔軟な処理が可能です。
- クロスブラウザ・マルチプラットフォーム
ご存じのように、JavaはOSに依存しないプラットフォームです。Java Plug-inさえインストールされていれば、Webブラウザ上で実行することができます。HTMLフォームはブラウザごとに見た目や振る舞いが異なりますが、JavaのSwingは統合されたプラグイン可能なルック・アンド・フィールを備えています。設計さえ間違えなければ、環境が異なってもフォームの外観を損なう危険性は低いでしょう。こうした性質はFlashに似ています。
- 初心者でも、比較的簡単な言語構造
これは、難しい問題です。Javaアプレットが支持を失った大きな理由かもしれません。Flashは開発ツールさえあれば、初心者でも比較的簡単に入門することができましたが、Javaアプレットを開発するにはJava言語やオブジェクト指向の理解が不可欠です。この問題は完全には解決できませんが、Java Script APIを利用することで軽減できます。
例えば、経験のある技術者がアプレットの基本的な設計とプログラムを行い、セキュリティの調整やサーバーとの通信を行うビジネスロジックを開発します。得られた情報の結果を表示するプレゼンテーションをJava Script APIに移譲することで、レイアウトの変更などのUIの調整に強いプログラムを作れます。JavaScriptであれば、多くのWebデザイナも使える言語なので、プレゼンテーションはデザインの専門家に委ねるということも可能になります。
- インストール作業が不要
こればかりは、Ajaxの方が圧倒的に優位です。Ajaxは、ブラウザ標準のJavaScriptを使うため、JavaScriptさえ有効になっていればインストール作業は不要です。これに対してJavaアプレットは、JREがインストールされていなければ実行することができません。
しかし、これはFlashや、間もなく登場するであろうSilverlightも同じことです。HTMLに依存するAjaxでは表現できない、高度な演出にはFlashが使われる傾向がありますが、Javaアプレットでもある程度のことは実現可能です。
残念なことに、Flashと比較した場合、マルチメディアとアニメーションに関して、Javaは大きく不利になります。そこで、Javaアプレットは開発者向け、Flashはデザイナ向けと考えて、要求によって使い分けるという方法はいかがでしょうか。エンターテイメントであればFlashが有利だと思われますが、ビジネスやフォームを中心としたWebアプリケーションでは、Javaアプレットの方が有利になることも多いでしょう。
最後に
思い返せば、筆者が開発者として仕事をいただくきっかけとなった作品が、Javaアプレットで動作する小さなインタプリタでした。パラメータとして渡された文字列を解析してコマンドを認識し、図形を描画するというアプレットです。このプログラムがやりたかったことはSVGによく似ていました。
素人が作った貧相なソフトウェアでしたが、アイデアは面白いものだったと思います。Javaは非常に豊富なグラフィックス機能をそろえているため、これをスクリプト的に自由に操ることができれば、他のアドオンを必要とせずにブラウザ上でさまざまなプログラムを表現することができる、という発想に基づいていました。Java Script APIは、それを実現してくれています。
加えて、現在のWebは孤島ではありません。サービスの概念が発達したことで、Web同士がソフトウェア的に接続しあい、情報を交換することができるようになりました。Javaプラットフォームが持つSwing APIを使ってリッチなGUIを提供すれば、新しいユーザー体験を提供できるかもしれません。Ajaxの場合、結果の表示方法はHTMLという制約がありますが、Javaアプレットには制約のない自由でプログラム可能なグラフィックス出力があります。試してみては、いかがでしょうか。