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