ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Server JSP Tag Extensions プログラマーズ ガイド
11g リリース 1 (10.3.1)
B55531-01
 

目次
目次

戻る
戻る
 
次へ
次へ
 

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 Fusion Middleware 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 を参照してください (Sun Microsystems の Javadoc は、http://java.sun.com/products/jsp/index.jsp でダウンロードできます)。

タグ本体内の例外処理

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

タグ属性の使い方

カスタム タグでは、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 ファイルでのパッケージ化」を参照してください。