JavaアプレットにTclコードを埋め込むには

 Jaclをアプレットで使うには、 つまりJavaアプレットでTcl言語のコードを実行できるようにするには、 というお話です。 ここでは、Javaプラグイン 1.3用のアプレットで試します。 J2SE 1.3にはJavaプラグインが付属しているので、 この環境の場合には他に(開発に)必要なものは特にないでしょう。 JavaプラグインをNetscapeブラウザで使用できるようにする方法は、 おまけのページにまとめました。

●簡単なJaclアプレット

import tcl.lang.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ExprApplet extends JApplet implements ActionListener {
    private JLabel lresult;
    private JTextField txfval1, txfval2;

    public void actionPerformed(ActionEvent ev){
        Interp interp = new Interp();
        try {           
            interp.eval("expr " + txfval1.getText() +
                        " + " + txfval2.getText());
            lresult.setText(interp.getResult().toString());
        } catch(TclException ex){
            System.err.println("actionPerformed/TclException : " + ex);
            ex.printStackTrace();
        } finally {
            interp.dispose();
        }
    }

    public void init(){
        getContentPane().setLayout(new GridLayout(2, 1));
        setSize(300, 80);
        JLabel la = new JLabel("Input decimal values:");
        this.getContentPane().add(la);

        JPanel pa  = new JPanel();
        txfval1    = new JTextField(6);
        JLabel lp  = new JLabel("+");
        txfval2    = new JTextField(6);
        JButton ba = new JButton("=");
        lresult    = new JLabel("");
        pa.setLayout(new FlowLayout());
        pa.add(txfval1); pa.add(lp);      pa.add(txfval2);
        pa.add(ba);      pa.add(lresult);
        ba.addActionListener(this);
        this.getContentPane().add(pa);
    }

    public static void main(String[] args){
        JFrame f = new JFrame("ExprApplet sample");
        f.setSize(310, 90);
        ExprApplet my = new ExprApplet();
        my.init();
        f.getContentPane().add(my);
        f.show();
    }
}
/* end. */

 これをExprApplet.javaと名づけてコンパイルします。 次に、このアプレットを載せるHTML文書は次のようになります。 exprapplet.htmlとでも名づけましょう。

<html>
<body bgcolor="white">
<hr>
<embed type="application/x-java-applet"
  width="310" height="90"
  code="ExprApplet.class"
  pluginspage = "http://java.sun.com/products/plugin/1.3/plugin-install.html"
  cache_option="Plugin"
  cache_archive="jacl.jar, tcljava.jar">
</embed>
<hr>
</body>
</html>

 Webサーバのドキュメントツリーには、次のものを配置します。
  • ExprApplet.class
  • exprapplet.html
  • jacl.jar
  • tcljava.jar
注:jacl.jarとtcljava.jarは、$JRE_HOME/lib/extの下にコピーしておいておけば、 アプレットの実行時にユーザがダウンロードする必要はなくなりますが、 こうすると何らかのタイミングで、
cannot read resource "/tcl/lang/library/init.tcl"
というエラーが出ることがあります。 また、Tcl Blendがエラーを出して実行できなくなるなど、 幾つかの不具合が発生するようです。 このエラーの関連は今後も調査をしていきたいと思います。

 これで、Webブラウザでexprapplet.htmlにアクセスしてみて、 足し算を行う簡単なアプレットが現れれば大成功です。 jacl.jarとtclblend.jarは足してサイズが約700KBと大きいのですが、 上のようにHTML文書のEMBEDタグで、 CACHE_ARCHIVE属性にこれらのJARファイルを入れておけば、 Javaプラグインがキャッシュしてくれるので、 次回からはクラスをロードする時間が大幅に短縮できます(*1)。

(*1) UNIX上のJavaプラグインを組みこんだNetscapeの場合は、 実行すると$HOME/java_plugin_AppletStore というディレクトリが作られ、この中にキャッシュされたJARファイルが格納されます。

スクリーンショット


JaclとJavaのセキュリティ機構

 Jaclアプレットでは、Tclコマンドをアプレットの中で実行させることが可能です。 と、いうことは、と思いをはせますと、 Tclのopenやpwd、execのような一部のコマンドは、 アプレットの中で実行させるとJavaのセキュリティ機構に引っかかるのではないでしょうか。 ご名答!その通りなんです。
 こんな例を考えてみます。システム課のT主任(35歳、独身)は、 毎日ジョブスケジューラから起動するアプリケーションの、 バッチジョブの出力ファイルの内容を、 アプレットで表示させようと次のようなプログラムを作りましたとさ。

import tcl.lang.*;
import java.awt.*;
import java.io.*;
import javax.swing.*;

public class FileReadApplet extends JApplet {
    public static final int WIDTH  = 300;
    public static final int HEIGHT = 200;
    private static final String FILENAME = "/var/app/log/daijinalog.dat";

    public void init(){
        Container c = getContentPane();
        c.setSize(WIDTH, HEIGHT);
        c.setBackground(Color.black);
        JTextArea txaa = new JTextArea(6, 60);
        JScrollPane pa = new JScrollPane(txaa);
        c.add(pa);
        readFile(txaa);
    }

    private void readFile(JTextArea txaa){
        Interp interp = new Interp();
        String content = null;
        try {
            interp.eval("set fin [open \"" + FILENAME + "\"];" +
                        "set buffer [read $fin];" + "close $fin");
            content = interp.getVar("buffer", 0).toString();
        } catch(TclException ex){
            System.err.println("readFile/TclException: " + ex);
            content = null;
        } finally {
            interp.dispose();
        }
        if(content != null){ txaa.setText(content); }
    }

    public static void main(String[] args){
        JFrame f = new JFrame("Example");
        FileReadApplet my = new FileReadApplet();
        my.init();
        f.setSize(my.WIDTH+5, my.HEIGHT+5);
        f.getContentPane().add(my);
        f.show();
        my.start();
    }
}
/* end. */

 これは確かに、 ファイルを開いて中の内容を読むTclスクリプトが埋めこまれています。 Javaアプリケーションとして、

% java FileReadApplet
とするとちゃんとファイルの内容がテキストエリアウィジェットに表示されました。 ところがいざアプレットに載せてブラウザ経由で見ると、
evalResource Ignoring SecurityException,
  it is likely we are running in an applet: ...
java.security.AccessControlException: access denied
  (java.io.FilePermission /var/app/log/daijinalog.dat read)
       at java.security.AccessControlContext.checkPermission(...)
       ...
と見事、セキュリティ制限に引っかかって跳ねられてしまいました。
 これを解決するには、一般のアプレットがやるのと同様に、 アプレットに「署名」をつける必要があります。 この署名のつけかたについては おまけのページにまとめました。
 とりあえず、このクラスをjaclapplets.jarというJARファイルに入れて、 署名をつけたとすると、 クライアントPC側は、
  • KeyStoreファイルを $JRE_HOME/lib/security に置く。
  • $JRE_HOME/lib/security/java.policyファイルを編集する。
という作業を行う必要があります。java.policyはこの場合、
grant codeBase "http://intra.nsnhnkmmkk.co.jp:8080/jacl-applet/*" {
        permission java.io.FilePermission "*", "read,write";
};
このようにします。

拡張レビュー分室 top
(first uploaded 2001/10/17 last updated 2002/08/19, KOUKEN HEIJIMA - MISUMI URANO)