Struts1 [カスタムタグを作る]

カスタムタグは名前の通り自作のタグライブラリです。別のファイルにコンパイル済みJava クラスとして準備し、JSP から 自由に参照する事ができます。JSP に直接スクリプトレットを書くと可読性が落ちるので、こういう手法で実装します。
struts [validator]のサンプル(bbs02.war) をベースに、実装してゆきます。bbs06 として使います。今回のサンプル(bbs06.war)
更新日 2016-02-13

カスタムタグの実装

簡単なカスタムタグを作ってみます。<ct:test /> で"Test!" を出力させてみます。多少面倒ですが、 最初だけです。後からの追加等は非常にラクなのでご安心を。

Java クラスの作成

本来スクリプトレットで書く予定だった内容を、別ファイルにJava クラスとして用意します。TagSupport クラスもしくは BodyTagSupport クラスを継承します。タグのボディ部を評価したければ後者を選びます。
// javac -classpath "../lib/struts.jar;$CATALINA_HOME/common/lib/servlet-api.jar;
$CATALINA_HOME/common/lib/jsp-api.jar;" -d "." ct_test.java

package mypackage;

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

public class ct_test extends TagSupport
{
	public int doStartTag() throws JspException
	{
		try
		{
			// ここで処理を行う
			pageContext.getOut().print("Test!");
		}
		catch (Exception e)
		{
			throw new JspException(e.getMessage());
		}
		return SKIP_BODY;
	}

	public int doEndTag()
	{
		return EVAL_PAGE;
	}
}
コンパイルしておきます。最初にdoStartTag() が、次にdoEndTag() が呼ばれます。

ct.tld を作成

これはカスタムタグ(例 <ct:test />)の:以降の文字列(test)とJava クラスを関連付けする対応表です。 /WEB-INF/ct.tld を以下の内容で作成します。
<?xml version="1.0" encoding="Shift_JIS" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
 "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
	<tlibversion>1.0</tlibversion>
	<jspversion>1.1</jspversion>
	<shortname>ct</shortname>
	<info>テスト用カスタムタグ・ライブラリ</info>

	<tag>
		<name>test</name>
		<tagclass>mypackage.ct_test</tagclass>
		<bodycontent>empty</bodycontent>
		<info>Test! と出力します</info>
	</tag>
</taglib>
taglib の中のtag エレメントにおいて、test とmypackage.ct_test が関連づけされています。カスタムタグを追加する時は ここに続けてtag エレメントを記述してゆきます。

web.xml の編集

/WEB-INF/web.xml にtaglib エレメントを追加し、ct.tld に対応する偽uri を定義します。JSP の先頭でtaglib ディレクティブを 用いる際に、この偽uri を参照する形をとります。
<web-app>

	<!-- 以下を追加 -->
	<taglib>
		<taglib-uri>/tags/ct</taglib-uri>
		<taglib-location>/WEB-INF/ct.tld</taglib-location>
	</taglib>
struts は、こういうリンクというかエイリアスというかラッパーを挟むのがムダに多い気がする。まぁ開発途上のフレームワークだし 仕様なんで文句を言わず従いましょう。

JSP の作成

テスト用にtest.jsp を作成します。最初のtaglib ディレクティブでは、先ほどweb.xml で定義した偽uri でカスタムタグの利用を 宣言します。prefix 属性に値が名前空間となります。
<%@ page contentType="text/html; charset=Shift_JIS" %>
<%@ taglib uri="/tags/ct" prefix="ct" %>

<html>
	<head>
		<title>test.jsp</title>
	</head>
	<body>
		<ct:test />
	</body>
</html> 

動作確認

test.jsp をブラウズして確認してください。

カスタムタグを追加する

簡単です。先ほどの手順でJava クラスを用意し、ct.tld にtag エレメントを追加するだけです。

属性を扱うカスタムタグ

カスタムタグに設定された属性を参照するには、Java クラスにメンバ変数・アクセスメソッドを用意し(アクションフォームのように)、.tld にも その旨を記載します。例えばname 属性をそのまま出力するカスタムタグを追加してみます。

Java クラスの作成

ct_echo_name.java を以下の内容で作成します。
// javac -classpath "../lib/struts.jar;$CATALINA_HOME/common/lib/servlet-api.jar;
$CATALINA_HOME/common/lib/jsp-api.jar;" -d "." ct_echo_name.java

package mypackage;

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

public class ct_echo_name extends TagSupport
{
	private	String m_strName = "";

	public void setName(String str)
	{
		m_strName = str;
	}

	public String getName()
	{
		return m_strName;
	}


	public int doStartTag() throws JspException
	{
		try
		{
			// ここで処理を行う
			pageContext.getOut().print( m_strName );
		}
		catch (Exception e)
		{
			throw new JspException(e.getMessage());
		}
		return SKIP_BODY;
	}

	public int doEndTag()
	{
		return EVAL_PAGE;
	}
}
アクションフォームの時のように、属性をプロパティとして見なしてアクセサメソッドを実装しています。コンパイルしておきます。

ct.tld に追加

echo_name タグが有効になるようにtag エレメントで追加します。
<tag>
	<name>echo_name</name>
	<tagclass>mypackage.ct_echo_name</tagclass>
	<bodycontent>empty</bodycontent>
	<info>name 属性の値を出力します</info>
	<attribute>
		<name>name</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
</tag>
name 属性が有効になるようにattribute エレメントによる宣言も忘れないように。rtexprvalue をtrue にすると jsp:attribute にて属性を設定できるようになります。

test.jsp を修正

body タグの中に今回作成したカスタムタグを追加します。
<body>
	<ct:test />
	<br />
	<ct:echo_name name="テスト文字列" /> 
</body>

動作確認

test.jsp をブラウザで確認してください。

ボディを評価する

カスタムタグで囲んだボディ部を処理するにはBodyTagSupport クラスを継承します。試しにボディ部をそのまま出力するカスタムタグecho_body を作成してみます。

Java クラスの作成

ct_echo_body.java を以下の内容で作成します。
// javac -classpath "../lib/struts.jar;$CATALINA_HOME/common/lib/servlet-api.jar;
$CATALINA_HOME/common/lib/jsp-api.jar;" -d "." ct_echo_body.java

package mypackage;

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

public class ct_echo_body extends BodyTagSupport
{
	public int doStartTag()
	{
		return EVAL_BODY_TAG;
	}

	public int doEndTag() throws JspException
	{
		try
		{
			// ここで処理を行う
			String str = (String)bodyContent.getString();
			pageContext.getOut().print( str );
		}
		catch (Exception e)
		{
			throw new JspException(e.getMessage());
		}
		return EVAL_PAGE;
	}
}
BODY を評価するにはdoStartTag() でEVAL_BODY_TAG を返し、doEndTag() に処理を記述します。

ネストされたカスタムタグの呼ばれる順

以下のようにネストされたカスタムタグの場合、doStartTag()、doEndTag() はそれぞれコンストラクタ・デストラクタのように呼ばれます。
<ct:flashvars>
	<ct:split_color>
AAA
	</ct:split_color>
</ct:flashvars>
ct:flashvars.doStartTag(), ct:split_color.doStartTag(), ct:split_color.doEndTag(), ct:flashvars.doEndTag() の順で呼ばれる。doEndTag() の 時は常にネストされたカスタムタグは既に評価が完了している事になり、bodyContent.getString() は文字列を返す。

ct.tld に追加

echo_name タグが有効になるようにtag エレメントで追加します。
<tag>
	<name>echo_body</name>
	<tagclass>mypackage.ct_echo_body</tagclass>
	<bodycontent>tagdependent</bodycontent>
	<info>body の値を出力します</info>
</tag>
bodycontent タグのボディの取り扱いを指定します。ボディを評価するならtagdependent を用います。そうでなければempty。またループであったりJSP、HTML として の処理を期待するならjsp を指定します。

test.jsp を修正

body タグの中に今回作成したカスタムタグを追加します。
<body>
	<ct:test />
	<br />
	<ct:echo_name name="テスト文字列" /> 
	<br />
	<ct:echo_body>ボディ文字列</ct:echo_body> 
</body>

動作確認

test.jsp をブラウザで確認してください。