DWR: 例外(Exception)のハンドリング

先のログ「DWR: Javaオブジェクトを画面の要素にマップする」で、「(DWRでマップする)Objectの型に合わない入力をすると例外(org.directwebremoting.extend.MarshallException)が発生する」と書いた。

この場合、「Error」と書いたAlertボックスがポップアップする。
これは、つまり、DWRがなんらかの(例外が発生したという)情報を送出していることを意味している。

(この延長線上として)DWRには、例外(正確に言うとjava.lang.Throwable)をハンドルする仕掛けが存在するので、そのサンプリング結果をログする。

サンプルは、DWRの本家ページ(「Error and Exception Handling」;リンクはこちら)にしたがった。

初期画面は以下のような簡単なもの。


ここで、「Exceptionを発生」ボタンを押すと、サーバー側でメッセージを仕込んだExceptionを発生させ、AlertボックスにメッセージとExceptionのクラスを表示する。


また、「Errorを発生」ボタンを押すと、サーバー側でErrorを発生させ、以下のようなAlertボックスを表示する。


ここでは、以下の3種類の「例外ハンドラー(Exception Handler)」をサンプリングした。

  1. Javaのメソッド呼び出し時のハンドラー
  2. DWRのBatch処理時のハンドラー
  3. Pageスコープでグローバルなハンドラー


1から順に、「Exceptionを発生」ボタン、「Exceptionを発生(Batch)」ボタン、「Errorを発生」ボタンに対応している。


まず、Javaのコード(ExceptionDemo.java)と、HTML(exception_demo.html)を示す。

ExceptionDemo.javaは、単純に例外やエラーを発生するだけのオブジェクト。これを、クライアント側からDWR経由で呼び出す。

package test;

/**
 * DWRのデモ
 * 
 * 例外を発生させる。
 * 
 * 
 */
public class ExceptionDemo {

    /**
     * クラス <code>ExceptionDemo</code> のオブジェクトを構築します。
     * 
     */
    
    public ExceptionDemo() {
    }
    
    public void throwException() throws Exception {
        throw new Exception("Exceptionが発生しました。");
    }
    
    public void throwError() {
        throw new Error("Errorが発生しました。");
    }
}


exception_demo.htmlでは、上記3種の例外ハンドラの定義を行う。
コードを見た方が速い。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>DWRのサンプリング</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">

<style type="text/css" id="defaultstyle">
body {
	margin:0;
	padding:0;
}

#container {
	margin: 2px;
	padding: 3px;
	line-height:1.5em;
	width: 500px;
	height: auto;
	border:1px dashed #999999;
}
</style>
	
<!-- ExceptionDemo.jsはDWRによって自動生成される。 
     dwr/はドキュメントルートとの相対参照。
-->

<script src='dwr/interface/ExceptionDemo.js'></script>
<!-- engine.jsは必須 -->
<script src='dwr/engine.js'></script>
<!-- util.jsは必須 -->
<script src='dwr/util.js'></script>

<script type="text/javascript">

// page scopeにグローバルなjava.lang.Throwableのハンドラ。
dwr.engine.setErrorHandler(errh);
function errh() {
	  alert("致命的なエラーが発生しました。");
}

function throwExp() {

	// メソッド呼び出し時のハンドラ定義。
	// 例外を発生させる。
	ExceptionDemo.throwException(
		// オブジェクトでCallBackとErrorHandlerを定義
		{
		  callback:function() {
			// CallBackがあれば、ここに書く。
		  },
		  errorHandler:function(msg,ex) {
				alert(ex.message + "クラス名; " + ex.javaClassName);
			}
		}
	);
}

function throwExpBat() {

	// Batch Start.
	dwr.engine.beginBatch();

	// Batch処理でのメソッド呼び出し時のハンドラ定義。
	// 例外を発生させる。
	ExceptionDemo.throwException();
	dwr.engine.endBatch({
		// ErrorHandlerを記述
		errorHandler:function(msg,ex) {
						alert(ex.message + "クラス名; " + ex.javaClassName);
					}
	});
	// Batch End.
}

function throwErr() {

	// エラーを発生させる。
	ExceptionDemo.throwError();

}
</script>
</head>
    
<body>
<div id="container">

<p>
DWRのデモです。<br>
例外のハンドリングをします。<br>

</p>

<input type="button" value="Exceptionを発生" 
   				onclick="throwExp()"/>

&nbsp
<input type="button" value="Exceptionを発生(Batch)" 
   				onclick="throwExpBat()"/>

&nbsp
<input type="button" value="Errorを発生" 
   				onclick="throwErr()"/>

</div>
</body>
</html>


最後にdwr.xmlを以下のように定義する。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://directwebremoting.org/schema/dwr20.dtd">

<dwr>
  <allow>
    
    <create creator="new" javascript="ExceptionDemo">
      <param name="class" value="test.ExceptionDemo"/>
    </create>
    
    <!--// 以下を定義しないと、エラーハンドラにThrowableが戻ってしまう。 -->
    <convert match="java.lang.Exception" converter="exception"/>

    <!--// 以下を定義しないと、
    	No converter found for ' java.lang.StackTraceElement ' 
    	がたくさんコンソールに出る。
    -->
    <convert match="java.lang.StackTraceElement" converter="bean"/>
         
	
  </allow>
</dwr>

この中の

    
    <!--// 以下を定義しないと、エラーハンドラにThrowableが戻ってしまう。 -->
    <convert match="java.lang.Exception" converter="exception"/>

    <!--// 以下を定義しないと、
    	No converter found for ' java.lang.StackTraceElement ' 
    	がたくさんコンソールに出る。
    -->
    <convert match="java.lang.StackTraceElement" converter="bean"/>

が例外を捕捉するための定義である。
最初の定義でException(とそのサブクラス)について、exceptionコンバータを利用(してクライアントとやり取り)する宣言をする。
この定義は、(例外の)クラス名やメッセージをクライアント側で捕捉するために必要である。これを宣言しない場合、デフォルトのコンバート処理となって、Throwableが発生したことだけがクライアントに通知される。
2つ目の定義は、java.lang.StackTraceElementをクライアントとやり取りするための定義。
DWRのHPには書かれていないのだが)これを定義しないと、最初の定義に基づいて例外をコンバートする際に「No converter found for ' java.lang.StackTraceElement '」というエラーが大量に吐きだれてしまう。