ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Server JSPタグ拡張のプログラミング
11gリリース1 (10.3.6)
B55531-04
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

4 タグ・ハンドラの実装

この章では、拡張タグの機能を実装するJavaクラスを記述する方法について説明します。

シンプル・タグ・ハンドラのライフサイクル(SimpleTagインタフェース)

シンプル・タグ・ハンドラでは、従来のタグ・ハンドラと比べて、大幅に簡素化された呼出しプロトコルが提示されます。タグ・ライブラリ記述子は、物理的な基底の実装に対して、タグ・ライブラリ宣言をマップします。シンプル・タグ・ハンドラは、SimpleTagインタフェースを実装するクラスによってJavaで表現されます。

シンプル・タグ・ハンドラのライフサイクルは簡単明瞭なものであり、キャッシングのセマンティクスによって複雑化されていません。一旦コンテナによってインスタンス化されたシンプル・タグ・ハンドラは、実行され、その後破棄されます。同じインスタンスをキャッシュして再利用することはできません。初期パフォーマンスのメトリックにより、タグ・ハンドラ・インスタンスをキャッシュしても、必ずしもパフォーマンスが向上するわけではないことがわかります。そのようなキャッシングに対応しようとすると、ポータブル・タグ・ハンドラの記述が困難になり、タグ・ハンドラでエラーが生じやすくなります。

扱いが簡単であることに加えて、シンプル・タグの拡張では、いかなるサーブレットAPIにも直接的な依存がないため、将来的な他の技術との統合の可能性にも対応できます。この機能は、Page-Contextによって拡張されたJspContextクラスで促進されています。JspContextは、JspWriterの格納やスコープ指定された属性の追跡など、一般的なサービスを提供します。一方、PageContextはサーブレットのコンテキストにおけるJSPの処理に特化した機能を備えています。TagインタフェースはPageContextに依存していますが、SimpleTagJspContextのみに依存しています。SimpleTagの本体は、もしあれば、JSPフラグメントに変換され、setJspBodyメソッドに渡されます。タグはその後、必要な回数、そのフラグメントを実行できます。「JSPフラグメント」を参照してください。

従来のタグ・ハンドラとは異なり、SimpleTagインタフェースは、Tagを拡張したものではありません。doStartTag()およびdoEndTag()をサポートするのではなく、SimpleTagインタフェースは単純なdoTag()メソッドを提供します。このメソッドは任意のタグ呼出しごとに一度だけ呼び出されます。すべてのタグのロジック、反復、本体評価などが、この単一のメソッド内で実行されます。したがって、シンプル・タグ・ハンドラはBodyTagと同等の能力を持ちますが、そのライフサイクルとインタフェースはずっと単純です。

本体のコンテンツをサポートするために、setJspBody()メソッドが提供されています。コンテナは、JspFragmentオブジェクトがタグの本体をカプセル化している状態でsetJspBody()メソッドを呼び出します。タグ・ハンドラの実装は、そのフラグメントに対してinvoke()を呼び出し、本体を評価できます。SimpleTagSupportコンビニエンス・クラスでは、これをさらに容易に行うための、getJsp-Body()やその他の有用なメソッドが用意されています。

シンプル・タグ・ハンドラのライフサイクルにおけるイベント

以下では、作成から呼出しに至るまでのシンプル・タグ・ハンドラのライフサイクルを説明します。シンプル・タグ・ハンドラが呼び出されると、次のイベントが順番に発生します。

  1. シンプル・タグ・ハンドラが、最初は対応する実装クラスに対して0の引数コンストラクタを使用して作成されます。従来のタグ・ハンドラとは異なり、シンプル・タグ・ハンドラ・インスタンスは、決してコンテナにプールされることがありません。タグ呼出しごとに、新しいインスタンスを作成する必要があります。

  2. setJspContext()およびsetParent()メソッドが、タグ・ハンドラに対して呼び出されます。渡されている値がnullの場合には、setParent()メソッドを呼び出す必要はありません。タグ・ファイルの場合、JspContextラッパーが作成されるので、タグ・ファイルは独自のページ・スコープを備えていると認識されます。getJspContext()は、ラップされたJsp-Contextを返す必要があります。

  3. 次に、以下のルールに従って、XML要素の属性として指定される属性(存在する場合)が宣言された順序で評価されます(これを後述の「XML要素の評価」と称します)。適切なBeanプロパティ・セッターが、各々に対して呼び出されます。指定された属性に対してセッターが定義されておらず、しかしタグでは動的な属性を受け付けている場合、setDynamicAttribute()メソッドがセッターとして呼び出されます。

    属性がスクリプト記述式(たとえば、JSP構文の「<%= 1+1 %>」またはXML構文の「%= 1+1 %」)である場合、式は評価され、その結果が変換されてセッターに渡されます。

    また、属性に何らかの式言語表現(たとえば、「Hello ${name}」)が含まれていると、式は評価され、その結果が変換されてセッターに渡されます。

    それ以外の場合、属性値は逐語的に取得され、変換されてセッターに渡されます。

  4. <jsp:attribute>要素の値が評価され、タグの本体に登場する順序に従って、対応するBeanプロパティ・セッター・メソッドが各々について呼び出されます。指定された属性に対してセッターが定義されておらず、しかしタグでは動的な属性を受け付けている場合、setDynamicAttribute()メソッドがセッターとして呼び出されます。

    また、属性のタイプがJspFragmentではない場合、コンテナは<jsp:attribute>要素の本体を評価します。この評価は、コンテナ固有のものとして行うことができます。コンテナの実装者は、この本体評価プロセスにおいて、他のカスタム・アクションが呼び出される可能性があることに留意してください。

    また、属性のタイプがJspFragmentである場合、Jsp-Fragmentオブジェクトのインスタンスが作成されて渡されます。

  5. タグ本体の値が決定され、本体が存在する場合は、setJsp-Body()メソッドがタグ・ハンドラに対して呼び出されます。

    本体のコンテンツ(body-content)が空である、本体が存在しない、またはこの呼出しに対して空の本体が渡されたとタグで宣言されている場合には、setJspBody()は呼び出されません。

    また、<jsp:body>または<jsp:attribute>要素が存在しない場合は、タグの本体は<jsp:body>要素の本体であるか、カスタム・アクション呼出しの本体であるかのいずれかです。この場合、Jsp-Fragmentオブジェクトのインスタンスが作成され、セッターに渡されます。タグが、tagdependentという本体コンテンツ(body-content)を有すると宣言された場合、JspFragmentは本体のコンテンツを逐語的に返す必要があります。

    また、タグが、scriptlessというタイプの本体コンテンツ(body-content)を有すると宣言された場合、JspFragmentは本体のコンテンツをJSPのスクリプトなし本体として評価する必要があります。

  6. doTag()メソッドが呼び出されます。

  7. doTag()の実装により、潜在的に(タグ・ハンドラがタグ・ファイルとして実装されている場合は)他のタグ・ハンドラを呼び出し、フラグメントを呼び出して、機能が実行されます。

  8. doTag()メソッドが返り、タグ・ハンドラ・インスタンスが破棄されます。Skip-PageExceptionが送出されると、ページの残りの部分は評価されず、要求が完了します。この要求が別のページ(またはサーブレット)から転送されている、または組み込まれている場合、現在のページの評価だけが停止します。

  9. AT_BEGINまたはAT_ENDのスコープで宣言されたタグ・スクリプト変数の各々について、クラシック・タグ・ハンドラの場合と同じく、適切なスクリプト変数とスコープ指定された属性が宣言されます。

JSPフラグメント

JSPフラグメントとは、必要に応じて何度でも呼び出せる、JSPコードのタグ・ハンドラに渡される部分です。フラグメントは、カスタマイズされたコンテンツを生成するためにタグ・ハンドラによって使用されるテンプレートであると考えることができます。したがって、コンテナによって評価される単純な属性とは異なり、フラグメント属性はタグ呼出し中にタグ・ハンドラによって評価されます。

JSPフラグメントは、フラグメントを構成するJSPコード内のJSP式言語(JSP EL)変数を使用して、パラメータ化できます。JSP EL変数はタグ・ハンドラによって設定されるため、ハンドラはフラグメントが呼び出される度に、そのフラグメントをカスタマイズできます。JSP式言語の詳細は、『Oracle WebLogic Server Webアプリケーション、サーブレット、JSPの開発』のWebLogic JSPリファレンスに関する項を参照してください。

シンプル・タグ・ハンドラのライフサイクルの期間中、SimpleTagの本体は、もしあれば、JSPフラグメントに変換され、setJspBodyメソッドに渡されます。変換フェーズの期間中、ページの様々な部分が、タグ・ハンドラに渡される前にjavax.servlet.jsp.tagext.JspFragment抽象クラスの実装に変換されます。これは、タグ・ライブラリ記述子(TLD)内で、フラグメントとして宣言された、またはJspFragmentタイプである、名前付き属性の本体における任意のJSPコード(<jsp:attribute>で定義される)に対して自動的に行われます。また、シンプル・タグ・ハンドラによって処理されるどのタグの本体に対しても、自動的に行われます。一度渡されると、タグ・ハンドラはフラグメントを必要に応じて何度でも評価および再評価でき、タグ・ファイルの場合にはさらに他のタグ・ハンドラに渡すことさえできます。

JSPフラグメントは、フラグメントと関連付けられたJspContext内のページ・スコープ属性を設定することで、タグ・ハンドラによってパラメータ化できます。これらの属性にはその後、式言語を利用してアクセスできるようになります。

JSPフラグメントに変換されるはずのJSPコードの一部に、スクリプトレットまたはスクリプトレット式が含まれていた場合、変換エラーが発生します。

タグ・ハンドラのライフサイクル(TagおよびBodyTagインタフェース)

TagインタフェースまたはBodyTagインタフェースから継承され、そのタグ・ハンドラ・クラスによって実装されたメソッドは、JSPページの処理中の特定の時点でJSPエンジンによって呼び出されます。これらのメソッドは、タグのライフサイクルにおける様々なポイントを表し、以下の順序で実行されます。

  1. JSPエンジンがJSPページの中でタグを見つけると、新しいタグ・ハンドラが初期化されます。javax.servlet.jsp.tagext.TagインタフェースのsetPageContext()メソッドとsetParent()メソッドが呼び出されて、そのタグ・ハンドラの環境コンテキストが設定されます。タグ開発者は、ベース・クラスTagSupportまたはBodyTagSupportを拡張する場合、これらのメソッドを実装する必要はありません。

  2. タグ属性ごとにsetXXXX()というJavaBeanのようなメソッドが呼び出されます。詳細は、「タグ本体内の例外処理」を参照してください。

  3. doStartTag()メソッドが呼び出されます。タグ・ハンドラでこのメソッドを定義すると、タグ・ハンドラを初期化したり、データベースなどの必要なあらゆるリソースへの接続を開くことができます。

    doStartTag()メソッドの終わりには、タグ本体を評価すべきかどうかを、以下の値定数のうちの1つをタグ・ハンドラ・クラスから返すことで指定できます。

    SKIP_BODY - タグの本体をスキップするようJSPエンジンに指示します。タグが本体のないタグ(空タグ)である場合、この値を返します。タグのライフサイクルのうち本体に関係のある部分はスキップされ、次に呼び出されるメソッドはdoEndTag()となります。

    EVAL_BODY_INCLUDE - タグ本体のコンテンツを評価して組み込むようJSPエンジンに指示します。タグのライフサイクルのうち本体に関係のある部分はスキップされ、次に呼び出されるメソッドはdoEndTag()となります。

    この値を返すことができるのは、Tagインタフェースを実装するタグの場合だけです。この値を使用すると、本体を組み込むかどうかは決定できるが、そのコンテンツには関知しないタグを記述できます。タグがBodyTagインタフェースを実装する(またはBodyTagSuportクラスを拡張する)場合、この値を返すことはできません。

    EVAL_BODY_TAG - タグ本体を評価してからdoInitBody()メソッドを呼び出すようJSPエンジンに指示します。この値は、タグがBodyTagインタフェースを実装する(またはBodyTagSupportクラスを拡張する)場合にだけ返すことができます。

  4. setBodyContent()メソッドが呼び出されます。この時点では、タグからの出力はBodyContentという特殊なJspWriterにリダイレクトされ、クライアントには送られません。本体を評価して得られるすべてのコンテンツはBodyContentバッファに追加されます。このメソッドにより、タグ・ハンドラはBodyContentバッファへの参照を格納してdoAfterBody()メソッドで評価後処理のために使えるようにすることができます。

    タグが出力をJSPページ(ネスト・タグの場合はその親スコープ)に渡す場合、タグはタグ・ライフサイクルのこの時点からdoEndTag()メソッドが終わるまでに、そのタグの出力を親スコープのJspWriterに明示的に書き出さなければなりません。タグ・ハンドラは、getEnclosingWriter()メソッドを使用して、親スコープの出力にアクセスできます。

    コンビニエンス・クラスのBodyTagSupportを使用している場合、このメソッドを実装する必要はありません。これは、タグがBodyContentへの参照を保持し、BodyContentメソッドを介してその参照を使用できるようにするからです。

  5. doInitBody()メソッドが呼び出されます。このメソッドを使用すると、タグ本体が初めて評価される直前に何らかの処理を実行できます。ここでは、スクリプト変数を設定したり、タグ本体の前のBodyContentに何らかのコンテンツを挿入したりできます。ここで付加したコンテンツは、JSPページのタグ本体のコンテンツとは異なり、JSPとして評価されることはありません

    このメソッドで実行する処理とdoStartTag()メソッドの終わりに実行する処理との大きな違いは(EVAL_BODY_TAGを返そうとしていることがわかった場合)、このメソッドでは、タグの出力のスコープはネストされていて、JSPページ(または親タグ)には直接向けられないということです。すべての出力は、BodyContentという特殊なJspWriterに書き込まれます。

  6. doAfterBody()メソッドが呼び出されます。このメソッドは、タグの本体が評価されBodyContentバッファに追加された後に呼び出されます。タグ・ハンドラは、評価済みタグ本体に基づいて何らかの処理を行うためにこのメソッドを実装する必要があります。ハンドラがコンビニエンス・クラスのBodyTagSupportを拡張する場合、getBodyContent()メソッドを使用して評価済みタグ本体にアクセスできます。単にBodyTagインタフェースを実装するだけであれば、setBodyContent()メソッドを定義して、そこにBodyContentインスタンスへの参照を格納しておく必要があります。

    doAfterBody()メソッドの終わりには、タグのライフサイクルを、前と同じように以下の値定数のうちの1つを返すことで決定できます。

    SKIP_BODY - 処理を続行し、本体を再び評価しないようJSPエンジンに指示します。タグのライフサイクルはdoEndTag()メソッドに進みます。

    EVAL_BODY_TAG - 本体を再び評価するようJSPエンジンに指示します。評価済み本体がBodyContentに追加され、doAfterBody()メソッドが再び呼び出されます。

    この時点で、タグ・ハンドラが親スコープに出力を書き出すよう指定できます。親スコープへのライターを取得するには、BodyTagSupport.getPreviousOut()メソッドまたはBodyContent.getEnclosingWriter()メソッドを使用します。どちらのメソッドでも、同じ親ライターが取得されます。

    タグ・ハンドラは評価済み本体のコンテンツを親スコープに書き出すこともあれば、その評価済み本体をさらに処理して他の何らかの出力を書き出すこともあります。本体の反復処理ごとにBodyContentが既存のBodyContentに追加されるので、SKIP_BODYを返すよう決定した場合、反復処理された本体のコンテンツ全体だけを書き出します。そのようにしないと、後続の各反復処理のコンテンツが何度も出力に現れることになります。

  7. pageContext内のoutライターが親のJspWriterに復元されます。このオブジェクトは実際にはスタックであり、pushBody()メソッドとpopBody()メソッドを使用してpageContext上のJSPエンジンによって処理されます。ただし、タグ・ハンドラの中でこれらのメソッドを使用してこのスタックを処理しようとしてはいけません。

  8. doEndTag()メソッドが呼び出されます。タグ・ハンドラはこのメソッドを実装して、タグ処理後のサーバー側作業を実行し、出力を親スコープJspWriterに書き出し、データベース接続などのリソースをクローズできます。

    タグ・ハンドラは、doEndTag()メソッド内でpageContext.getOut()を実行して得られるJspWriterを使用して、親スコープに出力を直接書き出します。なお、pageContext.outは、前の手順でpopBody()pageContext上で呼び出されたときに親ライターに復元されています。

    doEngTag()メソッドから以下の値のうちの1つを返すことで、JSPページの残りの部分の評価フローを制御できます。

    EVAL_PAGE - JSPページの残りの部分の処理を続行するようJSPエンジンに指示します。

    SKIP_PAGE - JSPページの残りの部分をスキップするようJSPエンジンに指示します。

  9. release()メソッドが呼び出されます。この呼出しは、タグ・ハンドラ・インスタンスが逆参照され、ガベージ・コレクション用に使用できるようになる直前に行われます。

タグ本体の反復処理(IterationTagインタフェース)

javax.servlet.jsp.tagext.IterationTagインタフェースを実装するタグでは、タグの本体を条件付きで再評価できるdoAfterBody()というメソッドを使用できます。doAFterBody()IterationTag.EVAL_BODY_AGAINを返す場合は、本体が再評価されます。doAFterBody()Tag.SKIP_BODYを返す場合は、本体はスキップされてdoEndTag()メソッドが呼び出されます。詳細は、このインタフェースのJava EE Javadocを参照してください(Javadocはhttp://www.oracle.com/technetwork/java/javaee/jsp/index.htmlからダウンロードできます。)

タグ本体内の例外処理

javax.servlet.jsp.tagext.TryCatchFinallyインタフェースのdoCatch()およびdoFinally()メソッドを実装すると、タグ内から送出される例外を捕捉できます。詳細は、このインタフェースのJava EE Javadocを参照してください(Javadocはhttp://www.oracle.com/technetwork/java/javaee/jsp/index.htmlからダウンロードできます。)

タグ属性の使い方

カスタム・タグでは、JSPページから指定できる属性を何個でも定義できます。これらの属性は、タグ・ハンドラに情報を渡してその動作をカスタマイズするために使用できます。

各属性名は、TLDの中で<attribute>要素を使用して宣言します。この要素は、属性の名前とその他の属性プロパティを宣言します。

JavaBean規約と同じように、タグ・ハンドラは属性名に基づいてセッター・メソッドとゲッター・メソッドを実装する必要があります。たとえば、fooという属性名を宣言する場合、タグ・ハンドラは以下のパブリック・メソッドを定義しなければなりません。

public void setFoo(String f);
public String getFoo();

属性名の先頭の文字は、接頭辞のsetまたはgetの後では大文字になることに注意してください。

JSPエンジンは、タグ・ハンドラが初期化されてからdoStartTag()メソッドが呼び出されるまでの間に、各属性のセッター・メソッドを呼び出します。一般に、タグ・ハンドラの他のメソッドからアクセスできるメンバー変数に属性値を格納するには、セッター・メソッドを実装しなければなりません。

新しいスクリプト変数の定義

タグ・ハンドラは、様々なスコープでJSPページから参照できる新しいスクリプト変数を使用できます。スクリプト変数は、それらの定義済みスコープの内部で暗黙的なオブジェクトのように使用できます。

javax.servlet.jsp.tagext.TagExtraInfoを拡張するJavaクラスを識別するための新しいスクリプト変数は、<tei-class>要素を使用して定義します。例:

<tei-class>weblogic.taglib.session.ListTagExtraInfo</tei-class>

次に、TagExtraInfoクラスを作成します。例:

package weblogic.taglib.session;
import javax.servlet.jsp.tagext.*;
public class ListTagExtraInfo extends TagExtraInfo {

public VariableInfo[] getVariableInfo(TagData data) {
 return new VariableInfo[] {
  new VariableInfo("username","String",true,VariableInfo.NESTED)
  new VariableInfo("dob","java.util.Date",true,VariableInfo.NESTED)
   };
  }
 }

上の例では、VariableInfo要素の配列を返すgetVariableInfo()という1つのメソッドが定義されています。各要素は、新しいスクリプト変数を定義します。上記のサンプルでは、java.lang.String型のusernamejava.util.Date型のdobという2つのスクリプト変数が定義されています。

VariableInfo()のコンストラクタは、以下の4つの引数を取ります。

タグ・ハンドラは、ページのコンテキストを介してスクリプト変数の値を初期化しなければなりません。たとえば、上で定義したスクリプト変数の値を初期化するには、doStartTag()メソッドの中で以下のJavaソースを使用します。

pageContext.setAttribute("name", nameStr); 
pageContext.setAttribute("dob", bday);

ここで、最初のパラメータはスクリプト変数の名前を指定し、2番目のパラメータは代入される値を示します。なお、ここではJava変数のnameStrString型で、bdayjava.util.Date型です。

また、TagExtraInfoクラスを使用して作成された変数にアクセスするには、useBeanを使用して作成されたJavaBeanにアクセスするときと同じようにそれを参照します。

動的名前付きスクリプト変数

タグの属性から、新しいスクリプト変数の名前を定義できます。このように定義することで、1つのスクリプト変数を定義するタグの複数のインスタンスを同じスコープで使用しつつ、タグのスクリプト変数名の衝突を避けることができます。TagExtraInfoを拡張するクラスからこれを実行するには、getVariableInfo()メソッドに渡されるTagDataからスクリプト変数の名前を取得しなければなりません。

TagDataからは、getAttributeString()メソッドを使用して、スクリプト変数の名前を指定する属性の値を検索できます。さらに、id属性の値を返すgetId()メソッドも存在します。これは、JSPタグからもたらされる新しい暗黙的オブジェクトに名前を付けるのによく使用されます。

タグ・ライブラリ記述子の変数の定義

タグ・ライブラリ記述子の変数を定義できます。詳細は、「スクリプト変数を定義します(オプション)」を参照してください。

協調的ネスト・タグの記述

ネストされているタグが、その親タグに定義されているプロパティを暗黙的に使用するよう設計できます。たとえば、サンプル・コードの「SQL Query」(WebLogic Serverのsamples/examples/jsp/tagext/sqlディレクトリを参照)では、<sql:query>タグが<sql:connection>タグの内部にネストされています。queryタグは親スコープのconnectionタグを検索して、そのタグによって確立されたJDBC接続を使用します。

親スコープのタグを見つけるために、ネスト・タグはTagSupportクラスの静的メソッドであるfindAncestorWithClass()を使用します。次に、QueryTagのサンプルから抜粋したコードを示します。

try {
  ConnectionTag connTag = (ConnectionTag)
    findAncestorWithClass(this, 
      Class.forName("weblogic.taglib.sql.ConnectionTag"));
  } catch(ClassNotFoundException cnfe) {
    throw new JspException("Query tag connection "+
                           "attribute not nested "+
                           "within connection tag");
}

この例では、与えられたクラスに一致するタグ・ハンドラ・クラスを持つ最も近い親タグ・クラスが返されます。直系の親タグがこのタイプでなければ、さらにその親が調べられます。一致するタグが見つかるまでこの処理が繰り返され、それでも見つからない場合はClassNotFoundExceptionが送出されます。

カスタム・タグでこの機能を使用すれば、JSPページでのタグの構文と使い方を簡素化できます。

タグ・ライブラリ・バリデータの使用

タグ・ライブラリ・バリデータはユーザーが記述するJavaクラスです。これを使用するとJSPページのXML表示の検証を実行できます。バリデータ・クラス上のvalidate(String, String, PageData)は、変換時(JSPがサーブレットに変換される時)にJSPコンパイラによって呼び出され、ページが検証された場合にはnull文字列を、検証が失敗した場合にはエラー情報を含む文字列を返します。

タグ・ライブラリ・バリデータを実装するには、次の手順に従います。

  1. バリデータ・クラスを記述します。バリデータ・クラスはjavax.servlet.jsp.tagext.TagLibraryValidatorクラスを拡張します。

  2. タグ・ライブラリ記述子内のバリデータを参照します。例:

    <validator>
      <validator-class>
         myapp.tools.MyValidator
      </validator-class>
    </validator>
    
  3. (オプション)初期化パラメータを定義します。バリデータ・クラスで初期化パラメータを取得および使用できます。例:

    <validator>
      <validator-class>
         myapp.tools.MyValidator
      </validator-class>
      <init-param>
        <param-name>myInitParam</param-name>
        <param-value>foo</param-value>
      </init-param>
    </validator>
    
  4. WebアプリケーションのWEB-INF/Classesディレクトリに、バリデータ・クラスをパッケージ化します。タグ・ライブラリjarファイルにクラスをパッケージ化することもできます。詳細は、「JSPタグ・ライブラリのJARファイルとしてのパッケージ化」を参照してください。