今回は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クラスからコンポーネントクラス特定
→コンポーネントクラスからレンダラ特定
みたいな感じかな(適当)
今日は。
返信削除拡張はよく使うので重要ですね。