アプレット LiveConnect の検証

JavaのLiveConnect は、Java アプレットがブラウザ内の JavaScript エンジンと通信したり、 Web ページ上の JavaScript がアプレットを使うことを意味します。
過去に、セキュリティ対策のため幾度の修正がなされて、その度に訂正した苦い経験があります。
以下はそれらを実験する例で、これが可能であることが、今後で変わらないことを強く望むところであります。

ここでは、アプレットのページ移動メソッドと、このアプレットクラスを使った閲覧回数取得メソッドを作り、 これをJavaScriptから呼び出す例です。
また、ページ表示のJavaScript実行で、アプレットが使えまで待つ技法の例も紹介しています。
このクリックで、下のtestgotoapplet1.htmlの動作を確認できます。
以下に、この例で使っているHTMLを示します。(の部分が異なる1と2のページがあります。)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<style type="text/css">body { background-color: #FFEEBB;}</style>
</head>
<script type="text/javascript">
var n = 0;
function sub1(){
	try {
		if(! document.ap01.isActive()){//onloadで実行すると失敗する可能性があり、タイマーで可能になるまでトライさせる。
			setTimeout("sub1()", 100);// 0.1秒後に実行
			document.getElementById("X").innerHTML = ++n + "回目の待ち状態";
			return;//アプレットがまだ使えない
		}
	}
	catch(e){
			setTimeout("sub1()", 100);// 0.1秒後に実行
			document.getElementById("X").innerHTML = ++n + "回目の待ち状態";
			return;//アプレットがまだ使えない
	}
	//alert( document.ap01.isActive() );//アプレットが使えるようになった。
	var count =  document.ap01.getCount();
	document.getElementById("X").innerHTML = "移動回数:" + count;
}
function sub2(page){
	var path = location.href;
	var i=path.lastIndexOf("/");
	page = path.substring(0, i+1) + page;
	alert( page + "へ移動します。");
	document.ap01.setPage(page);//アプレットに作った移動メソッドの実行(ブラウザなどの環境によって実行できないかも?)
}
function start(){
	setTimeout("sub1()", 100);// 0.1秒後に実行
	document.getElementById("X").innerHTML = ++n + "回目の待ち";//onloadで実行してかまわない。
}
</script>
<body onload="start()">
<h1>1のページ</h1>
<applet CODEBASE="./" code="TestGotoApplet.class"  name="ap01" Width="120" Height="90" >
</applet><br>
<br>
<span id="X">XXXX</span>です<br>
<br>
<span style="border-style: outset; border-width: 8px; border-color: yellow; cursor: pointer" onclick="sub2('testgotoapplet2.html')">
ページ移動</span>
</body>
</html>

上記の2つのページで使っているアプレットクラスのソースを以下に示します。

import java.applet.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;

public class TestGotoApplet extends Applet implements Runnable{
	static int count = 1;
	String page;//移動先
	Thread thread;
	byte thread_state;//0:停止状態,1:起動中, 2:停止途中

	public void run(){//スレッド
		try {
			thread_state = 1;
			while(thread_state == 1){
				if(page != null){
					AppletContext context = this.getAppletContext();
					try {
						URL url = new URL(page);
						context.showDocument(url);//ページ移動
					}
					catch(Exception e){e.printStackTrace();}
				}
				Thread.sleep(2000);//2秒
				System.out.println((new Date()).toString());
			}
		}
		catch(Exception e){e.printStackTrace();}
		thread_state = 0;
	}

	public void init(){//アプレットメソッドのオーバーライド
		count++;//カウント
		this.thread = new Thread(this);
		this.thread.start();
	}	

	public void stop(){//アプレットメソッドのオーバーライド
		System.out.println("stop1");
		if(thread == null) return;
		thread_state = 2;
		while(thread_state != 0){//スレッドが停止するまで待つ
			try {
				Thread.sleep(1);
			}
			catch(Exception e){e.printStackTrace();}
		}
		thread = null;
		System.gc();
		System.out.println("stop2");
	}
	public void setPage(String page){//移動ページ設定
		this.page = page;
	}
	public int getCount(){//スタティック変数を返す(セッション管理に使える技法)
		return count;
	}
}

ページの移動の部分をクリックすることでアプレット内のインスタンス変数に、移動ページが記憶されて それが、スレッド内のアプレット移動処理で実行されますが、それによってアプレットが破棄されます。
Javaコンソールを表示さいた例を以下に示します。
破棄の時に、InterruptedException: sleep interruptedが起きて、スレッドが停止しているのが分かります。
しかし、これはなぜ起きるのでしょうか?
上記では、アプレットのstopメソッドがスレッド内ループを止める働きがあるので、この例外は起きなくても良いはずです。
実験を行うと分かるですが、この例外が起きないで移動できる場合もあります。
この頻度は、ソースのThread.sleep(2000);//2秒を減らすと少なくなります。

Mon Mar 05 17:09:30 JST 2012
Mon Mar 05 17:09:32 JST 2012
Mon Mar 05 17:09:34 JST 2012
Mon Mar 05 17:09:36 JST 2012
stop1
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at TestGotoApplet.stop(TestGotoApplet.java:45)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at TestGotoApplet.run(TestGotoApplet.java:25)
	at java.lang.Thread.run(Unknown Source)
stop2
Mon Mar 05 17:09:39 JST 2012
Mon Mar 05 17:09:41 JST 2012
Mon Mar 05 17:09:43 JST 2012

以上の実験より、アプレットのスレッドは、プログラムで止めなくても、アプレッドが破棄される処理で、 自動的に、、InterruptedException: sleep interruptedの例外が送られて停止されるということが分かります。
(stopメソッドが正しく実行されれば、この例外が起きないからです)
つまり、スレッド内の処理で時間がかかるプログラムを書いた場合に、ページ移動で実行できないケースがあり得るということが分かります。
また、そのスレッドを制御するプログラムをのstopメソッドに書いても、働かない危険があるということになります。
この技法を使う場合、以上の危険を考慮してプログラミングしなければならないということです。
この例であれば、移動メソッド実行前で、スレッドを停止し、その確認後に行えばこの不具合は起きないでしょう。