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から順に、「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()"/>   <input type="button" value="Exceptionを発生(Batch)" onclick="throwExpBat()"/>   <input type="button" value="Errorを発生" onclick="throwErr()"/> </div> </body> </html>
<?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 '」というエラーが大量に吐きだれてしまう。