Teedaカスタムコンポーネント作成手順
今回はTeedaの拡張部品を作成したいと思います。
TeedaはSeasarプロジェクト発のJSFを拡張したWebアプリケーションフレームワークで、
慣れるとなかなか便利で使いやすいフレームワークなんですが、拡張するとなると
色々調べないといけないのでちょっと手間がかかります。
ただカスタムコンポーネントの拡張は、基本的にはJSFのカスタムコンポーネント作成と
流れは一緒なので、そんなに難しくないです。
Teedaに用意されている金額入力コンポーネントがありますが、
この部品を拡張してみます。
pageNameという属性の追加のみで、レンダラ実装はとりあえず空とします。
○やるべきこと
・tldファイルの作成
・カスタムUIコンポーネントクラスの作成
・カスタムファクトリークラスの作成
・カスタムタグクラスの作成
・カスタムレンダラーの作成
・faces-config.xmlにUIコンポーネントを登録
・diconにfactoryとrendererのコンポーネント登録
・xhtmlでのカスタムタグ使用
○tldファイルの作成
作成したTLDファイルはアプリケーション内のフォルダならどこにでも配置できるが、通常はWEB-INF/tldなどのようにWEB-INFディレクトリの配下のフォルダに配置して隠ぺい。JAR形式にした場合にはMETA-INFディレクトリ以下に配置。
何故かtldファイルをWEB-INF直下に配置しただけだと有効にならなかったので、tldファイルはjar化してクラスパスに追加。
○customext.taglib.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>dekatotoro</short-name>
<uri>http://www.dekatotoro.co.jp/dekatotoro/teeda/extension</uri>
<description>Teeda Extension Tag Library</description>
<tag>
<name>inputCommaText</name>
<tag-class>
jp.co.dekatotoro.ui.tag.ExtTInputCommaTextTag
</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>id</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>rendered</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>pageName</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>binding</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
</attribute>
省略・・・
</tag>
<taglib>
○カスタムUIコンポーネントクラスの作成
THtmlInputCommaTextクラスを拡張してカスタムUIコンポーネントクラスを作成。
package jp.co.dekatotoro.ui.component;
import javax.faces.context.FacesContext;
import javax.faces.el.VariableResolver;
import org.seasar.framework.util.AssertionUtil;
import org.seasar.teeda.extension.component.html.THtmlInputCommaText;
public class ExtTHtmlInputCommaText extends THtmlInputCommaText {
/** コンポーネントタイプ */
public static final String COMPONENT_TYPE = "jp.co.dekatotoro.ui.component.ExtTHtmlInputCommaText";
/** レンダラータイプ */
public static final String DEFAULT_RENDERER_TYPE = "jp.co.dekatotoro.ui.render.ExtTHtmlInputCommaTextRenderer";
/** 追加したPageName属性 */
private String pageName;
public ExtTHtmlInputCommaText() {
setRendererType(DEFAULT_RENDERER_TYPE);
}
// pageName属性からPageクラスを取得
public Object getPage(final FacesContext context) {
AssertionUtil.assertNotNull("FacesContext", context);
final VariableResolver variableResolver = context.getApplication().getVariableResolver();
return variableResolver.resolveVariable(context, getPageName());
}
@Override
public Object saveState(final FacesContext context) {
final Object[] values = new Object[2];
values[0] = super.saveState(context);
values[1] = pageName;
return values;
}
@Override
public void restoreState(final FacesContext context, final Object state) {
final Object[] values = (Object[])state;
super.restoreState(context, values[0]);
pageName = (String)values[1];
}
public String getPageName() {
return pageName;
}
public void setPageName(String pageName) {
this.pageName = pageName;
}
}
○カスタムファクトリークラスの作成
InputCommaTextFactoryクラスを拡張してカスタムUIコンポーネントクラスを作成。
package jp.co.dekatotoro.ui.factory;
import java.util.Map;
import org.seasar.teeda.extension.ExtensionConstants;
import org.seasar.teeda.extension.html.ActionDesc;
import org.seasar.teeda.extension.html.ElementNode;
import org.seasar.teeda.extension.html.PageDesc;
import org.seasar.teeda.extension.html.factory.InputCommaTextFactory;
public class ExtInputCommaTextFactory extends InputCommaTextFactory {
public ExtInputCommaTextFactory() {}
@Override
protected void customizeProperties(Map properties, ElementNode elementNode, PageDesc pageDesc, ActionDesc actionDesc) {
super.customizeProperties(properties, elementNode, pageDesc, actionDesc);
if (!properties.containsKey(ExtensionConstants.PAGE_NAME_ATTR)) {
properties.put(ExtensionConstants.PAGE_NAME_ATTR, pageDesc.getPageName());
}
}
// tldファイルの取得先
@Override
protected String getUri() {
return "http://www.dekatotoro.co.jp/dekatotoro/teeda/extension";
}
// 金額入力コンポーネントはclass属性にT_currencyを付加するが、
// 変更したい場合は本メソッドを拡張する
@Override
public boolean isMatch(ElementNode elementNode, PageDesc pageDesc, ActionDesc actionDesc) {
super.isMatch(elementNode, pageDesc, actionDesc);
}
}
○カスタムタグクラスの作成
TInputCommaTextTagクラスを拡張します。
package jp.co.dekatotoro.ui.tag;
import javax.faces.component.UIComponent;
import jp.co.dekatotoro.ui.component.ExtTHtmlInputCommaText;
import org.seasar.teeda.extension.ExtensionConstants;
import org.seasar.teeda.extension.taglib.TInputCommaTextTag;
public class ExtTInputCommaTextTag extends TInputCommaTextTag {
/** Page名 */
private String pageName;
public ExtTInputCommaTextTag() {}
/**
* PageNameをプロパティへセット
*/
@Override
protected void setProperties(UIComponent component) {
super.setProperties(component);
setComponentProperty(component, "pageName", getPageName());
}
/**
* コンポーネントタイプを取得
*/
@Override
public String getComponentType() {
return ExtTHtmlInputCommaText.COMPONENT_TYPE;
}
public String getPageName() {
return pageName;
}
public void setPageName(String pageName) {
this.pageName = pageName;
}
}
○カスタムレンダラーの作成
THtmlInputCommaTextRendererを拡張
拡張したいメソッドをオーバライドする。
package jp.co.dekatotoro.ui.render;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.seasar.teeda.extension.render.html.THtmlInputCommaTextRenderer;
public class ExtTHtmlInputCommaTextRenderer extends THtmlInputCommaTextRenderer {
public ExtTHtmlInputCommaTextRenderer() {}
/**
* javascript読み込みキーを取得
*/
@Override
protected String getScriptKey() {
return "view/js/io/ExtTHtmlInputCommaText.js";
}
@Override
protected void encodeHtmlHtmlInputTextPrepare(FacesContext context, THtmlInputText htmlInputText)
throws IOException {
//拡張処理
}
@Override
protected void doEncodeEndStart(FacesContext context, THtmlInputText htmlInputText) throws IOException {
//拡張処理
}
@Override
protected void renderOnfocus(THtmlInputCommaText htmlInputCommaText, ResponseWriter writer, String groupingSeparator)
throws IOException {
//拡張処理
}
@Override
protected String getValue(FacesContext context, UIComponent component) {
//拡張処理
}
}
○faces-config.xmlにUIコンポーネンの登録
<component>
<component-type>
jp.co.dekatotoro.ui.component.ExtTHtmlInputCommaText
</component-type>
<component-class>
jp.co.dekatotoro.ui.component.ExtTHtmlInputCommaText
</component-class>
</component>
○diconにコンポーネント登録
teeda-extension-1.0.13-spx.jarのteedaExtension.diconに
factoryとrendererの登録がされているので、同じようにfactoryと
rendererを登録します。
とりあえず、jarからteedaExtension.diconコピーしてきて
以下を追加。
※factoryはoriginalより前に定義する必要あり!?
<!-- factoryはシングルトン -->
<component name="extInputCommaTextFactory" class="jp.co.dekatotoro.ui.factory.ExtInputCommaTextFactory" />
<!--
レンダラはAutoRegisterで登録 一応prototype(毎回生成のスコープ)
レンダラの実装にもよるけど、Teedaもシングルトンなんでパフォーマンス的にもシングルトンの方がよいのかな。。
-->
<component
class="org.seasar.teeda.core.render.autoregister.TeedaRendererComponentAutoRegister">
<property name="instanceDef">
@org.seasar.framework.container.deployer.InstanceDefFactory@PROTOTYPE
</property>
<initMethod name="addReferenceClass">
<arg>@jp.co.dekatotoro.ui.render.ExtTHtmlInputCommaTextRenderer@class</arg>
</initMethod>
<initMethod name="addClassPattern">
<arg>"jp.co.dekatotoro.ui.render"</arg>
<arg>".*Renderer"</arg>
</initMethod>
</component>
○xhtmlでのカスタムタグ使用
ファクトリのisMatchメソッドを拡張していないので、指定方法はTHtmlInputCommaTextと同様で
class属性に"T_currency"を指定するのみ
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:te="http://www.seasar.org/teeda/extension" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title id="title">タイトル</title>
</head>
<body>
<form id="form" autocomplete="off">
<input type="text" id="aaa" class="T_currency" />
</form>
定義を纏めると
・ファクトリ → diconファイル
・コンポーネント → faces-config.xml
・タグ → tldファイル
・レンダラ → ファイル
動き的には
ファクトリがタグファイル特定
→tldのtag定義からtagクラス特定
→tagクラスからコンポーネントクラス特定
→コンポーネントクラスからレンダラ特定
みたいな感じかな(適当)