<?xml version="1.0" encoding="Shift_JIS"?>
<document>

  <properties>
    <author email="jon@latchkey.com">Jon S. Stevens</author>
    <title>You make the decision - Error Handling</title>
    <translator>熊坂祐二</translator>
    <translator>高橋達男</translator>
    <original>ymtd/ymtd-error-handling</original>
  </properties>

<body>

<section name="エラーの扱い">

<p>
これは、JSP の典型的かつ根本的な設計の問題です。
JSP を使うとき、いったい何種類のエラーに遭遇するでしょうか?
例えば、JSP Servlet が、.jsp テキストファイルから自動生成されて、
コンパイラでコンパイルされ、生成/解析エラーやコンパイルエラーが
起こるとどうなるでしょうか? JSP が不必要に複雑なために、
エラーのパターンが実際に非常に多くなっているのです！
</p>

<p>
この問題の中で最も美しくないのは、エラーが 2つの異なる仕組みを通して報告される点です。
パーサは独自の種類のエラーを投げますし、javac コンパイラもまた別の種類のエラーを
投げます。そして、生成処理の結果として起こる、コンパイラからのエラーは多くの場合、
まったく意味不明です。例えば、このエラーがどこで起こったか、説明できますか？
</p>

<source><![CDATA[
org.apache.jasper.JasperException: Unable to compile class: Invalid type
expression.
                out.println("JSP is great!")
                           ^
: Invalid declaration.
                out.write("\r\n\r\n\r\n");
                         ^
2 errors
]]></source>

<p>
エラーの原因が最初の out.println() の後に<code>;</code>が抜けていたせいだとわかった
あなたは正解です! さて、Java のコードを書いたことも見たこともない人の立場に
立ってみましょう。そういう人がこのエラーを迅速かつ容易に理解できると思いますか? 
それに加えて、このエラーの発生した場所が、その原因となった場所とは違う場所だった場合、
元の .jsp ファイルから抽象化したレベルがあり、
さらに生成された中間的な .java ファイルがあるため、
エラーの原因となった場所を見つけるのは困難です。
</p>

<p>
繰り返しますが、Velocity には、中間ステップも抽象化レイヤもないので、
これと同じ問題で苦しむことはありません、
</p>

<source><![CDATA[
<%@ page errorPage="/error.jsp" %>
]]></source>

<p>
また、JSP では Throwable な例外がページの処理中に投げられたときに
使用するエラーページを定義できます。これもまた、
MVC モデルを破壊することにならないでしょうか? 
言い換えれば、アプリケーションフレームワークが
エラーメッセージの扱いに責任を持てないのです。
</p>

<source><![CDATA[
<% throw new Exception("oops"); %>
]]></source>

<p>
JSP ページのどこかで Exception を投げるためには、
ステートメントの中に最初にそれを埋め込む必要があります。
この特殊なケースでは、コンパイラで最適化が有効になっている場合、
例外が全てコンパイルされない可能性があることに注意してください。
したがって、[下記の例の]「true」の代わりに、
具体的なオブジェクトを使わなければなりません。
厳密な MVC モデルを使うなら、この方法は現実的には難しいかもしれません。
なぜなら、オブジェクトの生成処理が View を破壊するからです。

</p>

<source><![CDATA[
<%
  if (true) {
    throw new Exception("oops");
  }
%>]]></source>

<p>
その理由は、JSP が Exception の後に追加で <code>out.println("\r\n");</code>
を生成するからです。javac がページをコンパイルしようとするときに、
デバッグしづらい別のエラーが起こります。
</p>

<source><![CDATA[
org.apache.jasper.JasperException: Unable to compile class for
  JSPC:\engines\jakarta-tomcat\work\localhost_8080%2Fjsp\
  _0002ferrorMaker_0002ejsperrorMaker_jsp_3.java:75:
Statement not reached.
                out.write("\r\n");
                ^]]></source>

<p>
(自分ではうまく説明できないので) 直接 Jason の書籍から引用すると
</p>

<source><![CDATA[
実際、JSP スクリプトを使う時には、こんな風に「なるほど…そういうことか!」
と思う機会が多いのです。たまたま式を書き忘れて ("=" 記号を忘れて)
スクリプトレットを書いてしまったり、(staticは許されない)
スクリプトレット内で static 変数の定義をしたり、
(式には不要だが、スクリプトレットには必要な)セミコロンを忘れたり、
たまたま全然ダメな Java コードを書いてしまったりしてしまうと、
意味不明なエラーメッセージが出る可能性があります。なぜなら、
コンパイル処理は JSP ファイルではなく生成された Java コードに対して
行われるからです。この問題を具体的に示すために、
errorTaker.jsp ファイルで <%= name %> とすべきところを
<% name %> としてみましょう。Tomcat は以下のエラーを生成します。

org.apache.jasper.JasperException: Unable to compile class for
  JSPC:\engines\jakarta-tomcat\work\localhost_8080%2Fjsp\
  _0002ferrorTaker_0002ejsperrorTaker_jsp_6.java:91:
Class name not found.
                 name 
                 ^

このようなエラーをデバッグする際、エラーの発生を再現するために、
プログラマが生成されたコードを
見なければならないことも少なくありません。]]></source>

<p>
Velocity では、テンプレート内部にどんな Java コードも埋め込むことを
許可していないため、同じ問題は発生しようがありません。
テンプレートで唯一許されるのは Velocity テンプレート言語（VTL）と
メソッド呼び出しだけです。
</p>

<p>
それ以外は全てパーサが「テキスト」と見なしてそのまま出力されます。
Velocity でトラブルが起こるのは、実行中に例外を投げるメソッドを呼び出す時だけです。
例えば、この VTL では <code>$foo</code> という String が定義されており、
その <code>substring()</code> メソッドを呼びだそうとすると、
<code>IndexOutOfBoundsException</code> が投げられるかもしれません。
</p>

<source><![CDATA[
#set ($foo = "bar")

#set ($bar = $foo.substring(0,10))
]]></source>

<p>
例外が投げられると、パーサは処理を停止し、
パーサが実行したメソッドで例外が捕捉された時点のスタックツリーに
その例外を吐き出します。その時点で、例外は無理なく扱うことができます。
そうです、この例外は、Java の知識のないデザイナにはデバッグが難しいかも知れませんが、
少なくとも限定的な Java の知識があるテンプレートエンジニアならデバッグは簡単なのです。
</p>

<p>
これは Velocity との組み合わせで Turbine を使う利点の1つです。
というのは Turbine の設計は、一貫性のある方法で例外を取り扱うからです。
また、Velocity に含まれている VelocityServlet を使ってもこれと同じ機能が実現できます。
この Exception には、エラーが発生した .vmファイル内の行番号とカラム番号が含まれています。
JSP のように抽象化を行わないので、エラーの行番号とカラム番号が一致します。
また、例外を投げるツールはパーサだけであり、中間の .java ファイルではなく、実際のテンプレートに関連した場所情報やエラー情報がこの例外には含まれています。
生成された .java コードの結果である、不可解な javac メッセージを
デバッグする必要はありません。ただし、商用のアプリケーションサーバの中には、
エラー時に JSP ファイルの行番号をうまく扱うものもあります。
</p>

<p>
[どちらを選ぶかは] あなたが判断してください。
</p>
<p>
<strong>[ <a href="ymtd-generation.html">生成?</a> &lt;- 前 | 
    次 -&gt; <a href="./ymtd-javabeans.html">JavaBeans</a> ]
</strong></p>

</section>

</body>
</document>
