DATE : 2007/06/11 (Mon)
例外の発生原因をスタックトレースから探りたい場合があります。例えば、データベース関係の例外の発生時などです。データベースへの問い合わせで例外が発生した場合、データベースに渡した SQL がおかしかったのか、それともデータベースそのものに障害が発生しているのか、JDBC などのデータベースにアクセスするライブラリ部分の例外なのかなどを判断する必要があります。ところが、Hibernate や Spring Framework などでは、これらの例外は特定の例外オブジェクト内に格納されています。Hibernate の場合、永続化機構(データベースなど)で例外が発生した場合、全て org.hibernate.HibernateException が投げられ、原因(SQL の異常など)はそれに格納されています。
発生した例外の原因は、java.lang.Throwable#getCause() で取り出せます。しかし、これで取り出せるのは1段階下の原因で、2段階以上下のものを取り出すには繰り返し getCause() を呼び出さなければなりません。このようなコードを書くのは非常に面倒です。
そこで、「Jakarta Commons Lang」の org.apache.commons.lang.exception.ExceptionUtils を使います。これは、例外関係に便利なメソッドを集めたユーティリティクラスです。
ExceptionUtils には、例外の原因を探る static メソッドが用意されています。
- indexOfThrowable メソッド
- indexOfType メソッド
これらのメソッドは特定の例外が何段階下のものかを調べるものです。getCause() を順々に呼び出して調べていくイメージです。対象の例外が存在しない場合は-1となります。それぞれのメソッドには、次のような違いがあります。
- indexOfThrowable メソッド
- 指定された例外のクラスのみを対象とし、そのサブクラスは対象としない。
- indexOfType メソッド
- 指定された例外のクラスとそのサブクラスを対象とする。
要するに、サブクラスを対象とするかしないかという違いです。
参考文献
DATE : 2007/06/08 (Fri)
JSTL Core タグライブラリ の c:url を使用すると、URL を変数に記憶しておくことができます。しかも、「/」から始まる絶対パスを指定した場合、ルートはコンテキストルートとなります。
これは JSF を使用しているページに a タグで直接 URL を埋め込む場合にとても便利です。JSF は post-back で動作するため、JSF 上で次のページに遷移した場合の URL は前のページの URL となります。すると、a タグに相対パスを指定した場合、リンクの内容は遷移元のページの URL からの相対パスとなります。これでは、全ページがコンテキストルート直下に存在しないと、遷移元のページのあったディレクトリによってリンクの内容が変化してしまいます。かと言って、絶対パスを埋め込んだ場合、開発用サーバ上から公開用サーバにページを移す場合に、リンク中の絶対パスを全て公開用サーバのものに置き換えなければなりません。
c:url を使えばこの問題は解決できます。「/」から始まる絶対パスのルートはコンテキストルートなので、「/」から始まるパスを埋め込んでおけば公開用サーバに移す場合でもリンクを修正する必要がありません。
c:url を使うには、page ディレクティブて EL 式を有効にし、 taglib ディレクティブで JSTL Core タグライブラリの使用を宣言します。(「...」は省略を表します)
<%@ page ... isELIgnored="false"> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
value 属性に URL を、var 属性に変数名を指定することで、変数に URL を記憶できます。
<c:url var="top_url" value="/index.jsp" />
この変数は次のような EL 式で参照できます。
${top_url}
つまり、a タグに c:url で変数に記憶させた URL を指定するには、次のようになります。
<a href="${top_url}">...</a>
私は、トップページなどの特殊な URL を別の JSP ファイルで記憶させています。そして実際にその URL を使用するページでその JSP ファイルを取り込んで、EL 式から URL を参照しています。よく使う URL を1ファイルにまとめられるので、管理が楽になります。
参考文献
DATE : 2007/05/26 (Sat)
Spring Frameworkの org.springframework.orm.hibernate3.HibernateTemplate#load()を使用してオブジェクトを取得した後にそのオブジェクトのメソッドを呼び出すと、org.hibernate.LazyInitializationException という例外が発生してしまいました。
HibernateTemplate#load() は、内部で Session#load()を呼び出しています。Hibernate には遅延読み込みという機能があり、Session#load()から返されるオブジェクトはオブジェクトそのものではなく、そのオブジェクトの内容を(データベースなどから)読み込むオブジェクトが返されます。こうすることで、オブジェクトのプロパティが必要になった時になって初めてデータが読み込まれるので、オブジェクトの生成に時間がかからないわけです。
ところが、そのデータを読み込めるのは Hibernate セッションが開いている間だけです。つまり、Hibernate セッションを閉じた後にプロパティを初めて参照した場合は例外が発生します。それが LazyInitializationException です。
通常は、Hibernate セッションの管理は開発者が手動で行わなければなりませんが、HibernateTemplate は Hibernate セッションを自動的に管理してくれるので、セッションの管理を気にする必要はなくなります。
(;^ω^)ところが、ここに落とし穴があって、HibernateTemplate#load() も例に漏れず、内部で Hibernate セッションが閉じられていました。なので、Hibernate セッション外で HibernateTemplate#load() を使って読み込んだオブジェクトのプロパティを参照すると、LazyInitializationException が発生します。
そこで、HibernateTemplate#get() に処理を変えました。Session#load() と違い、Session#get() はオブジェクトそのものを読み込みます。そのため、Hibernate セッションが閉じられた後でもオブジェクトのプロパティを参照できます。
(;^ω^)Session#load() と Session#get() は一見おなじような機能なので、適当に load() を利用していたのが間違いでした。同じような機能が複数ある場合は、違いをよく確認しないといけませんね。
参考文献
DATE : 2007/05/11 (Fri)
(;^ω^)しばらく JSF を使ってきたのですが、今日はじめて Validator を複数重ねられることに気付きました。
例えば、入力形式(数字の間にハイフンが入っているなど)を検証する formatValidator とそれが今回はじめて入力されたものかどうかを検証する uniqueValidator を用意したとします。
すると、data の value プロパティに前述の検証を行ってから入力値を格納するテキストフィールドは次のように書けます。
<h:inputText value="#{data.value}"> <f:validator validatorId="formatValidator" /> <f:validator validatorId="uniqueValidator" /> </h:inputText>
JSF 1.2 の仕様書にも次のように書いてありました。(「JavaServer Faces Specification Version 1.2 Final Draft」p.3-34 から引用)
JSF supports a mechanism for registering zero or more validators on each EditableValueHolder component in the component tree.
javax.faces.component.EditableValueHolder は、編集可能な値を保持するコンポーネントが実装しているインタフェースです。javax.faces.component.html.HTMLInputText などの javax.faces.component.UIInput が実装しています。そのため、仕様上、入力用の UI コンポーネントは0から複数の Validator を使用できることになるわけです。
(;^ω^)私の見た入門書やサンプルコードには Validator を1つしか使用しないものしかなかったため、頭の中に Validator は1つという図式ができあがっていたようです。
参考文献
DATE : 2007/05/09 (Wed)
JSF などの JSP の動作確認をしていると、特定の部分だけコメントアウトしたくなることがあります。例えば、一時的に Managed Bean のプロパティを削除した場合などです。
JSP の標準構文(「<% ... %>」形式の構文)を使用している場合、つい以下のようにコメントアウトしがちです。
(;^ω^)何度もひっかかりました。
<!-- <h:outputText value="#{bean.value}" /> -->
しかしこのようにコメントアウトした場合、コメントアウトした(つもりの)部分でエラーが発生することがあります。特に、初めに挙げたような、bean の value プロパティを削除した場合などは確実にエラーになります。
これは、コメントアウトしたつもりの部分を JSP コンテナがコメントと認識していないためです。
実は、JSP の標準構文には「<%-- ... --%>」という形式の標準構文用コメント文が用意されています。このコメント文を使用すると、JSP コンテナがコメント部分をしっかり無視してくれます。
例えば、上の例は次のようになります。
<%-- <h:outputText value="#{bean.value}" /> --%>
JSP コンテナが処理を無視するため、クライアント側からコメントアウト部分は見えません。
なお、JSP を XML 構文で記述している場合は、「<!-- ... -->」のコメント文を使用します。