属性のあるタグと本体のあるタグ

さて前のページでは、

<ushi:selfurl />

という、最も簡単なタグしか作れませんでした。 しかしJSP組み込みのjsp:なにがしというタグを見ると分かる通り、 タグ名の後ろに属性を指定できたり、「開くタグ」と「閉じるタグ」が対になっていて、 制御構造みたいなものを記述できたりするのも中にはあります。 このページでは、その属性をもつタグ、下のようなの

<ushi:selflink message="このページを再描画"/>

や、開くタグと閉じるタグが対になっていて、その間に挟まれる本体をもつタグ、 下のようなの

<ushi:selflinkbody>このページを再描画</ushi:selflinkbody>

を作ります。ここで作るselflinkとselflinkbodyは全く同じ機能を持つもので、 つまり、このタグを使っているJSP文書へのリンク (自分自身を再描画するリンク) を表示します。リンクのメッセージが前者はタグの属性「message」の値であって、 後者のそれはタグに囲まれる本体である点が違いです。

タグハンドラクラス
まず、属性を持つタグのタグハンドラ、SelfLinkTag.javaです。

package ushitag;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;

import javax.servlet.http.*;

/**
 * 現在表示しているJSPページへのリンクを出力するタグです。
 * 本体はありません。
 * 属性:message ... リンクのメッセージ。
 *       デフォルトは「検索条件入力に戻る」です。
 *
 * @version 20030115
 */
public class SelfLinkTag extends TagSupport {
  String message = "検索条件入力に戻る";

  public void setMessage(String message){
    this.message = message;
  }

  public String getMessage(){
    return this.message;
  }

  public int doStartTag(){
    try {
      JspWriter out = pageContext.getOut();
      HttpServletRequest request =
        (HttpServletRequest) pageContext.getRequest();
      HttpServletResponse response =
        (HttpServletResponse) pageContext.getResponse();
      out.print("<a href=\"" +
                response.encodeURL(request.getRequestURI()) +
                "\">" + getMessage() +
                "</a>");
    } catch(IOException ex){
      System.out.println("Error in SelfLinkTag: " + ex);
    }
    return SKIP_BODY;
  }
}
/* end. */

一見して分かることは、タグの属性はタグハンドラのプロパティ だということです。 属性messageを作りたければ、タグハンドラにメソッドsetMessage を定義すればよいのです。(getMessage()は必須ではありません) 他の点は前ページのSelfURLTag.javaと同じですね。

そして、今度はチョトチガウ、本体を持つタグのタグハンドラ、 SelfLinkBodyTag.javaです。

package ushitag;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;

import javax.servlet.http.*;

/**
 * 現在表示しているJSPページへのリンクを出力するタグです。
 * 本体はリンクのメッセージになります。
 *
 * @version 20030116
 */
public class SelfLinkBodyTag extends BodyTagSupport {

  public int doStartTag(){
    return EVAL_BODY_BUFFERED;
  }

  public int doEndTag(){
    try {
      JspWriter out = pageContext.getOut();
      HttpServletRequest request =
        (HttpServletRequest) pageContext.getRequest();
      HttpServletResponse response =
        (HttpServletResponse) pageContext.getResponse();
      out.print("<a href=\"" +
                response.encodeURL(request.getRequestURI()) +
                "\">" + getBodyContent().getString() +
                "</a>");
    } catch(IOException ex){
      System.out.println("Error in SelfLinkBodyTag: " + ex);
    }
    return EVAL_PAGE;
  }
}
/* end. */

今度はまず、継承元のクラスが TagSupportではなく BodyTagSupportになっています。 BodyTagSupportには本体を持つタグ特有の処理があらかじめたくさん定義されていますから、 これを継承するのが便利なんです。
で、このクラスでは2つのメソッド、 doStartTagdoEndTag をオーバーライドしています。これらは名前の通り、 それぞれ開くタグに出会ったとき、閉じるタグに出会ったときに実行されます。
doStartTagでは、 戻り値EVAL_BODY_BUFFEREDという、 本体の内容をバッファに記憶する命令を表す記号定数を返しています。 それか、このタグの処理は要らないのでスキップしたい場合には記号定数 SKIP_BODYのどちらかを返します。
doEndTagで、本体の出力をします。本体の内容を文字列で取得するには、 サンプルでやっているように、getBodyContent().getString() を使います。そして、ページ(JSP文書)の残りの処理を継続する場合は記号定数 EVAL_PAGEを、 そうでなくページの評価をそこでやめてしまう場合には記号定数 SKIP_PAGEを返すようにします。

BodyTagSupportでオーバーライドできるメソッドはまだ他にもあります。 そうした隠れキャラの3つが、 doInitTagdoAfterBodyreleaseです。 doInitTagは開くタグの直後、本体の直前で実行されます。 doAfterBodyは本体の直後、閉じるタグの直前で実行されます。 これらはループなどの制御構造をタグで書き下すときに使うようです。 また、releaseは閉じるタグの直後に実行されるメソッドで、 名前からすると、タグの評価に使った一時リリースをクリアーするのに使うことを想定されたもののように思います。

タグライブラリデスクリプタ

<?xml version="1.0" encoding="Shift_JIS" ?>
<!DOCTYPE taglib
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
	"http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>ushitag</short-name>
  <uri></uri>
  <description>
  シンプルなタグライブラリの例
  </description>

  <tag>
    <name>selfurl</name>
    <tag-class>ushitag.SelfURLTag</tag-class>
    <body-content>EMPTY</body-content>
    <description>
    タグが含まれる文書のURLを出力する
    </description>
  </tag>

  <tag>
    <name>selflink</name>
    <tag-class>ushitag.SelfLinkTag</tag-class>
    <body-content>EMPTY</body-content>
    <description>
    タグが含まれる文書へのハイパーリンクを出力する
    </description>
    <attribute>
      <name>message</name>
      <required>false</required>
    </attribute>
  </tag>

  <tag>
    <name>selflinkbody</name>
    <tag-class>ushitag.SelfLinkBodyTag</tag-class>
    <body-content>JSP</body-content>
    <description>
    タグが含まれる文書へのハイパーリンクを出力する
    </description>
  </tag>
</taglib>

そして、タグライブラリデスクリプタです。 今度は冒頭でShift_JISとして、日本語のコメントを書いています。 (単にそれ以上の意味はありませんが(-_-)) あとコレを書きながら悩んだのですが、 Tomcat 4台では、DOCTYPE宣言を省略できません。 TomcatのXMLパーサがタグライブラリデスクリプタの文法をかなり厳密に評価するので、 DOCTYPEがなかったり、 サポートされていないタグを使ったりするとこのタグライブラリを使おうとする JSPのロード時にエラーが発生します。
で、話を戻すと属性のあるタグSelfLinkTagでは、 attributeでサポートする属性を指定します。 nameが属性の名前、requiredはその属性が必須(省略不可)かどうかを指定します。 必須の場合はtrue(またはyes)、そうでなければfalse(またはno)です。
一方、本体のあるタグSelfLinkBodyTagでは、 body-contentの値をJSPにしています。
body-contentは、JSP/empty/scriptless/tagdependentから選びます。 (大文字・小文字はどちらでもいいようです。省略可能でデフォルトはJSP)
JSPを設定すると、タグの内部(本体)に別のJSPタグ、EL式、HTML、scriptletなどが書かれている場合に、 それらをJSPエンジンで解釈します。 つまり、JSPタグの入れ子ができます。
scriptlessもほぼ同様ですが、別のJSPタグ、EL式、HTMLは解釈されますが、 scriptletは解釈されません。
tagdependentは、タグ本体内部の解釈を一切行いません。 本体がJSPではない文字列(例えばSQL文)の場合に使います。
emptyは、本体がないタグの場合に使います。本体のある形式で使うとエラーになります。
本体の中に別のタグを書くことを許可するカスタムタグを作る場合には、
・doStartTag()の戻り値はEVAL_BODY_INCLUDEではなくEVAL_BODY_BUFFEREDにする
・TLDのbody-contentはtagdependentではなくJSPにする
の2点は特に注意が必要です。でないと、 「タグが閉じていない」だののわけが分からないJasperException(JSPエラー) に延々悩むことになります。

web.xml
前のページと同じなので、省略です。

JSP文書

<%@ page language="java" contentType="text/html;charset=Shift_JIS" %>
<%@ taglib prefix="ushi" uri="ushitag" %>
<html><body>
<font color="#700040">●タグライブラリのテスト</font><br>

<a href="<ushi:selfurl />">このページを再描画</a><br>

<ushi:selflink/><br>

<ushi:selflink message="リトライ!" /><br>

<ushi:selflinkbody>
このページを再描画するのだ
</ushi:selflinkbody>

</body></html>

そして、なにやら怪しいJSP文書。 同じページを再描画するリンクばかりで、何が楽しいねん? 私には分かりません。(ちょっと待て)

Server Side Java Index Top

(first uploaded 2003/01/16 last updated 2009/01/18, URANO398)

Gポイントポイ活 Amazon Yahoo 楽天

無料ホームページ 楽天モバイル[UNLIMITが今なら1円] 海外格安航空券 海外旅行保険が無料!