ICEfacesのTreeコンポーネントで

ICEfacesのTreeコンポーネントを試してみました。
まずはVisual Web ICEfacesでTreeコンポーネントを貼り付けてそのまま実行。
よく分からないですが、何もしないのに沢山のフォルダができています。
com.icesoft.faces.component.jsfcl.data.DefaultTreeのコンストラクタで作られているようです。

public DefaultTree() {
	DefaultMutableTreeNode rootTreeNode = new DefaultMutableTreeNode();
	NodeUserObject rootObject = new NodeUserObject(rootTreeNode, this);
	rootTreeNode.setUserObject(rootObject);
	model = new DefaultTreeModel(rootTreeNode);

	for (int i = 0; i < 2; i++) {
		DefaultMutableTreeNode branchNode = new DefaultMutableTreeNode();
		NodeUserObject branchObject = new NodeUserObject(branchNode, this);
		branchNode.setUserObject(branchObject);
		rootTreeNode.add(branchNode);

		for (int k = 0; k < 1; k++) {
			DefaultMutableTreeNode subbranchNode = new DefaultMutableTreeNode();
			NodeUserObject subBranchObject = new NodeUserObject(
					subbranchNode, this);
			subbranchNode.setUserObject(subBranchObject);
			branchNode.add(subbranchNode);

			for (int l = 0; l < 1; l++) {
				DefaultMutableTreeNode subbranchNodeTwo = new DefaultMutableTreeNode();
				NodeUserObject subBranchObjectTwo = new NodeUserObject(
						subbranchNodeTwo, this);
				subbranchNodeTwo.setUserObject(subBranchObjectTwo);
				subbranchNode.add(subbranchNodeTwo);

				for (int m = 0; m < 3; m++) {
					DefaultMutableTreeNode subbranchNodeThree = new DefaultMutableTreeNode();
					NodeUserObject subBranchObjectThree = new NodeUserObject(
							subbranchNodeThree, this);
					subbranchNodeThree.setUserObject(subBranchObjectThree);
					subbranchNodeTwo.add(subbranchNodeThree);

				}
			}

		}
	}
}

で、com.icesoft.faces.component.jsfcl.data.NodeUserObjectのコンストラクタでTreeNodeの詳細を設定しています。

    public NodeUserObject(DefaultMutableTreeNode wrapper, DefaultTree treeBeanPointer) {
        super(wrapper);

        treeBean = treeBeanPointer;


        label = generateLabel();
        setLeafIcon("/xmlhttp/css/xp/css-images/tree_document.gif");
        setBranchContractedIcon("/xmlhttp/css/xp/css-images/tree_folder_close.gif");
        setBranchExpandedIcon("/xmlhttp/css/xp/css-images/tree_folder_open.gif");
        setText(label);
        setTooltip(nodeToolTip);
        setExpanded(true);
    }

うーん、この画像はcom.icesoft.faces.resource.css.imagesにあるのですが、このフォルダをWEBにデプロイする設定が見つからない・・・。Visual Web ICEfacesにすると設定されているのでしょうけど・・・。とりあえず気にしないことに。
それから、ちょっと気になることを発見。

	public void setSelectedNode(String selectedNode) {
		this.selectedNode = selectedNode;
	}

ノードをクリックしたときに選択したノードのラベルをセットするようになっています。ICEFacesのTreeデモでもそのようになっていて、わざわざ「private DefaultMutableTreeNode findTreeNode(String nodeId)」なんていうメソッドまで用意しています。そんな事をしなくてもこんな感じでもいいのではないかと思いやってみました。

    public void setSelectedNodeObject(NodeUserObject selectedNodeObject) {
        if (this.selectedNodeObject != null) this.selectedNodeObject.setBgcolor("none");
        this.selectedNodeObject = selectedNodeObject;
        this.selectedNodeObject.setBgcolor("red");
    }

テキストではなく、オブジェクトそのものを格納しています。例えば、選択したノードの背景を変えたい場合なんかはこちらの方が簡単にできます。この書き方では何か問題があるのかな?

やっぱり駄目か・・

ここのところ、駄目だろうとは思いつつも以下のコードとICEfacesとの両立はできないものか試していました。

<f:view beforePhase="#{hoge.hoge}">

で、ICEfacesのコードをこんな感じにしてみました。

package com.icesoft.faces.webapp.parser;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.digester.Rule;
import org.xml.sax.Attributes;

import java.lang.reflect.Method;

import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
import javax.faces.webapp.UIComponentTag;
import javax.faces.webapp.UIComponentELTag;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import java.util.HashMap;
import javax.faces.event.PhaseEvent;


public class ELSetPropertiesRule extends Rule {

    public void begin(Attributes attributes) throws Exception {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        HashMap values = new HashMap();
        Object top = digester.peek();

        for (int i = 0; i < attributes.getLength(); i++) {
            String name = attributes.getLocalName(i);
            if ("".equals(name)) {
                name = attributes.getQName(i);
            }
            String value = attributes.getValue(i);

            //take a guess at the types of the JSF 1.2 tag members
            //we are probably better off doing this reflectively
            if (name != null) {
                values.put(name, value);
                if (("id".equals(name)) ||
                    ("name".equals(name)) ||
                    ("var".equals(name))) {
                    values.put(name, value);
                } else if (top instanceof UIComponentTag) {
                    //must be a JSF 1.1 tag
                    values.put(name, value);
                } else if ("action".equals(name)) {
                    values.put(name,
                               getMethodExpression( facesContext, name, value,
                                                    null ));
                } else if ("validator".equals(name)) {
                    values.put(name,
                            getMethodExpression( facesContext, name, value,
                                    null ));
                } else if ("actionListener".equals(name)) {
                    values.put(name,
                               getMethodExpression(facesContext, name, value,
                                                   ActionEvent.class));
                } else if ("valueChangeListener".equals(name)) {
                    values.put(name,
                               getMethodExpression(facesContext, name, value,
                                                   ValueChangeEvent.class));
                } else {
                    values.put(name,
                               getValueExpression(facesContext, name, value));
                }
                if (top instanceof javax.faces.webapp.UIComponentELTag) {
                    //special case for
                    //com.sun.faces.taglib.jsf_core.ParameterTag
                    //and potentially others
                    if ("name".equals(name))  {
                        values.put(name,
                                   getValueExpression(facesContext, name, value));
                    } else if ("locale".equals(name)) {
                        values.put(name,
                                   getValueExpression(facesContext, name, value));
                    } else if ("beforePhase".equals(name)) {
                        values.put(name,
                                    getMethodExpression(facesContext, name, value,
                                                   PhaseEvent.class));
                    } else if ("afterPhase".equals(name)) {
                        values.put(name,
                                    getMethodExpression(facesContext, name, value,
                                                   PhaseEvent.class));
                    }
                } else {
                    //reflection based code as mentioned above.  More likely
                    //to be correct, but performance may not be as good,
                    //so only applying it in a specific case
                    if ("name".equals(name))  {
                        Method setNameMethod = null;
                        try {
                            setNameMethod = top.getClass().getMethod("setName",
                                                                     new Class[] { ValueExpression.class } );
                        } catch (Exception e)  { }
                        if (null != setNameMethod)  {
                            values.put(name,
                                       getValueExpression(facesContext, name, value));
                        }
                    }

                }

            }
        }


        BeanUtils.populate(top, values);
    }

    private ValueExpression getValueExpression(FacesContext facesContext,
                                               String name, String value)  {

        Class argType = Object.class;
        // For some reason, tab index is typed to be a String, even though
        //  it can only be positive integral values
        if (!name.equals("tabindex")) {
            try {
                if (value.equalsIgnoreCase("true") ||
                    value.equalsIgnoreCase("false") ) {
                    argType = Boolean.class;
                } else if (null != Integer.valueOf(value)) {
                    //attempt to coerce to Integer type for standard JSF components
                    argType = Integer.class;
                }
            } catch (NumberFormatException e) {
            }
        }


        ValueExpression valueExpression =
                facesContext.getApplication().getExpressionFactory()
                        .createValueExpression(
                                facesContext.getELContext(),
                                value, argType );

        return valueExpression;
    }

    private MethodExpression getMethodExpression(FacesContext facesContext,
                                                 String name, String value,
                                                 Class argType)  {
        Class[] argTypes = new Class[]{};
        if (null != argType) {
            argTypes = new Class[]{argType};
        }

        MethodExpression methodExpression =
                facesContext.getApplication().getExpressionFactory()
                        .createMethodExpression(
                                                facesContext.getELContext(),
                                                value, String.class,
                                                argTypes);
        return methodExpression;
    }
}

beforePhaseとafterPhaseを追加しただけなのですが、この変更でExceptionは吐かなくなりました。
が、動かない・・・。無視されっぱなしですね。ここから先はどこを見ればいいのか見当もつきません。この件はここらであきらめるとします。

とりあえず今後はVisual Web ICEfacesでいじってみることにします。

ICEfacesでbeforePhaseが動作しない?

JSF1.2からのようにPageごとに割り込みメソッドを指定できるらしいです。
Chapter 11 Using JavaServer Faces Technology in JSP Pages
Visual Webを使いたくない(使えない)場合に便利そうな機能だと思いましたが、ICEFacesと合わせると動かないんですよね。同じように悩んでいる人を発見するも、解決策を見つけることはできませんでした。
ICEfacesでbeforePhaseが動作しない?

試したコードはこんな感じです。

    <f:view beforePhase="#{Page1.hoge}">

    public void hoge(PhaseEvent pe){

    }

すると、以下のエラーが吐かれます。

java.lang.IllegalArgumentException: argument type mismatch
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.commons.beanutils.PropertyUtils.setSimpleProperty(PropertyUtils.java:1789)
        at org.apache.commons.beanutils.PropertyUtils.setNestedProperty(PropertyUtils.java:1684)
        at org.apache.commons.beanutils.PropertyUtils.setProperty(PropertyUtils.java:1713)
        at org.apache.commons.beanutils.BeanUtils.setProperty(BeanUtils.java:1019)
        at org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:808)
        at com.icesoft.faces.webapp.parser.ELSetPropertiesRule.begin(ELSetPropertiesRule.java:130)
        at org.apache.commons.digester.Rule.begin(Rule.java:200)
        at org.apache.commons.digester.Digester.startElement(Digester.java:1273)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:501)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:400)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2747)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
        at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
        at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:807)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
        at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:107)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
        at org.apache.commons.digester.Digester.parse(Digester.java:1586)
        at com.icesoft.faces.webapp.parser.Parser.parse(Parser.java:130)
        at com.icesoft.faces.application.D2DViewHandler.renderResponse(D2DViewHandler.java:464)
        at com.icesoft.faces.application.D2DViewHandler.renderView(D2DViewHandler.java:153)
        at org.icefaces.netbeans.rave.web.ui.appbase.faces.ViewHandlerImpl.renderView(ViewHandlerImpl.java:296)
        at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:106)
        at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
        at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:144)
        at com.icesoft.faces.webapp.http.core.JsfLifecycleExecutor.apply(JsfLifecycleExecutor.java:17)
        at com.icesoft.faces.context.View$2$1.respond(View.java:47)
        at com.icesoft.faces.webapp.http.servlet.GlassFishAdaptingServlet$GlassFishRequestResponse.respondWith(GlassFishAdaptingServlet.java:159)
        at com.icesoft.faces.context.View$2.serve(View.java:72)
        at com.icesoft.faces.context.View.servePage(View.java:133)
        at com.icesoft.faces.webapp.http.core.SingleViewServer.service(SingleViewServer.java:52)
        at com.icesoft.faces.webapp.http.common.ServerProxy.service(ServerProxy.java:11)
        at com.icesoft.faces.webapp.http.servlet.MainSessionBoundServlet$4.service(MainSessionBoundServlet.java:114)
        at com.icesoft.faces.webapp.http.common.standard.PathDispatcherServer.service(PathDispatcherServer.java:24)
        at com.icesoft.faces.webapp.http.servlet.MainSessionBoundServlet.service(MainSessionBoundServlet.java:160)
        at com.icesoft.faces.webapp.http.servlet.SessionDispatcher$1.service(SessionDispatcher.java:42)
        at com.icesoft.faces.webapp.http.servlet.GlassFishAdaptingServlet.service(GlassFishAdaptingServlet.java:60)
        at com.icesoft.faces.webapp.http.servlet.EnvironmentAdaptingServlet.service(EnvironmentAdaptingServlet.java:63)
        at com.icesoft.faces.webapp.http.servlet.SessionDispatcher.service(SessionDispatcher.java:62)
        at com.icesoft.faces.webapp.http.servlet.PathDispatcher.service(PathDispatcher.java:23)
        at com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:153)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
        at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:411)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:290)
        at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:271)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:202)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:206)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:571)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1080)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:150)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:571)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1080)
        at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:272)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:637)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:568)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:813)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:341)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:263)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:214)
        at com.sun.enterprise.web.portunif.PortUnificationPipeline$PUTask.doTask(PortUnificationPipeline.java:380)
        at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:265)
        at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:106)

GlassFishだとあまり情報がありませんが、Tomcatだともうちょっと情報があり、MethodのところにValueを突っ込んでるみたいなエラーが書かれています。
何となく、com.icesoft.faces.webapp.parser.ELSetPropertiesRule.beginがイヤな感じなので、中を見てみます。

    public void begin(Attributes attributes) throws Exception {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        HashMap values = new HashMap();
        Object top = digester.peek();

        for (int i = 0; i < attributes.getLength(); i++) {
            String name = attributes.getLocalName(i);
            if ("".equals(name)) {
                name = attributes.getQName(i);
            }
            String value = attributes.getValue(i);

            //take a guess at the types of the JSF 1.2 tag members
            //we are probably better off doing this reflectively
            if (name != null) {
                values.put(name, value);
                if (("id".equals(name)) ||
                    ("name".equals(name)) ||
                    ("var".equals(name))) {
                    values.put(name, value);
                } else if (top instanceof UIComponentTag) {
                    //must be a JSF 1.1 tag
                    values.put(name, value);
                } else if ("action".equals(name)) {
                    values.put(name,
                               getMethodExpression( facesContext, name, value,
                                                    null ));
                } else if ("validator".equals(name)) {
                    values.put(name, 
                            getMethodExpression( facesContext, name, value,
                                    null ));
                } else if ("actionListener".equals(name)) {
                    values.put(name,
                               getMethodExpression(facesContext, name, value,
                                                   ActionEvent.class));
                } else if ("valueChangeListener".equals(name)) {
                    values.put(name,
                               getMethodExpression(facesContext, name, value,
                                                   ValueChangeEvent.class));
                } else {
                    values.put(name,
                               getValueExpression(facesContext, name, value));
                }
                if (top instanceof javax.faces.webapp.UIComponentELTag) {
                    //special case for 
                    //com.sun.faces.taglib.jsf_core.ParameterTag
                    //and potentially others
                    if ("name".equals(name))  {
                        values.put(name,
                                   getValueExpression(facesContext, name, value));
                    } else if ("locale".equals(name)) {
                        values.put(name,
                                   getValueExpression(facesContext, name, value));
                    } 
                } else {
                    //reflection based code as mentioned above.  More likely
                    //to be correct, but performance may not be as good,
                    //so only applying it in a specific case
                    if ("name".equals(name))  {
                        Method setNameMethod = null;
                        try {
                            setNameMethod = top.getClass().getMethod("setName",
                                                                     new Class[] { ValueExpression.class } );
                        } catch (Exception e)  { }
                        if (null != setNameMethod)  {
                            values.put(name,
                                       getValueExpression(facesContext, name, value));
                        }
                    }

                }

            }
        }


        BeanUtils.populate(top, values);
    }

beforePhaseにはメソッドを指定するはずなのですが、このコードからはbeforePhaseの場合にはvalueが渡されているような雰囲気です。これが原因かどうかはもっと突っ込まないとわからないようです。ちょっと手に負えませんね。
beforePhaseを使えばすぐわかるような現象だとは思うのですが、使っている人が少ないのでしょうか?

ICEFacesでS2JDBCを使う その2

こんなご意見をいただきました。
http://d.hatena.ne.jp/shin/20090423/p2
これは僕も気にはなっていたものの、とりあえず目をつぶっていた問題です。最初はSeasarではステートレスのコンポーネントを管理して、ステートフルなJSFコンポーネントにインジェクションするということを考えていたのですが、やり方がよく分からなかったので、仕方なくこうしていました。で、せっかくの機会なので別のやり方を考えてみました。

Seasarは外部のコンポーネントにインジェクションすることもできることに気付いたので、今回はその方法で行きます。
DelegatingVariableResolverを使うところまでは同じですが、基本的にfaces-config.xmlの設定からコンポーネントを取得するようにします。

	public Object resolveVariable(FacesContext facesContext, String name) throws EvaluationException {
		Object value = resolveOriginal(facesContext, name);
		if (value != null) {
            S2Container s2con = SingletonS2ContainerFactory.getContainer();
            if (s2con != null && s2con.hasComponentDef(value.getClass())) {
                s2con.injectDependency(value);
            }
		}
		return value;
	}

取得後、Seasarコンポーネント定義があるかを判別し、あるようならインジェクションさせます。
ここで、外部のコンポーネントにインジェクションする場合はdiconファイルにもそのように設定する必要があります。

    <component name="Page1" class="webapplication1.Page1" instance="outer"/>

instance="outer"という設定がそれにあたります。
これで以上です。前回使ったS2ContainerFilterは今回は使いません。これで一応は動作しました。

しかし、Pageクラスが描画されるまでにresolveVariableが2回呼び出されることに気付きました。呼ばれる都度、インジェクションが走ることになるわけです。うーん、レスポンス的にいかがなものか・・・?おそらく問題になることは少ないとは思うのですが、それはそれで気持ちが悪い。かといって、インジェクション済みのフラグを持たせるのはもっと気持ち悪い。それをするくらいなら、Pageクラスに直接Seasarのコードを埋め込む方がマシですね。
ということで、なかなかすっきりしなかったりします。とりあえず、また別の方法を思いついたらトライします。

ICEFacesでS2JDBCを使う

いずれはICEFacesを業務系システムの開発に使いたいので、データベース周りの連携を試してみました。個人的にSQLを書くスタイルが好みなので、その系では一番使いやすいS2JDBCを試しました。
まずはICEFacesからSeasarコンポーネントを呼べるようにします。連携用のツールはSeasarのプロダクト群を探してみても適当なものがなかったので、仕方がないので自作することにしました。この辺りはSpringの方が情報やツールが充実していますので、それを参考にしました。ICEFaces and Spring 2.5 in Java EE
org.springframework.web.jsf.DelegatingVariableResolverをコピーしてseasar用に書き換えます。

package web;

import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.VariableResolver;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

public class DelegatingVariableResolver extends VariableResolver {

	protected final Log logger = LogFactory.getLog(getClass());

	protected final VariableResolver originalVariableResolver;


	public DelegatingVariableResolver(VariableResolver originalVariableResolver) {
		this.originalVariableResolver = originalVariableResolver;
	}

	protected final VariableResolver getOriginalVariableResolver() {
		return this.originalVariableResolver;
	}


	public Object resolveVariable(FacesContext facesContext, String name) throws EvaluationException {
		Object value = resolveOriginal(facesContext, name);
		if (value != null) {
			return value;
		}
		Object bean = resolveSeasarBean(facesContext, name);
		if (bean != null) {
			return bean;
		}
		return null;
	}

	protected Object resolveOriginal(FacesContext facesContext, String name) {
		Object value = null;
        try{
            value = getOriginalVariableResolver().resolveVariable(facesContext, name);
        }catch(Exception e){
            e.printStackTrace();
        }
		if (value != null && logger.isTraceEnabled()) {
			logger.trace("Successfully resolved variable '" + name + "' via original VariableResolver");
		}
		return value;
	}

	protected Object resolveSeasarBean(FacesContext facesContext, String name) {
        return SingletonS2ContainerFactory.getContainer().getComponent(name);
	}
}

このクラスでやっていることは、ICEFacesコンポーネントを探すときに、まずICEFacesで管理しているコンポーネントを探しに行き、そこで見つからなかった場合にSeasarに探しに行くようにしているだけです。
これをfaces-config.xmlに追記します。

    <application>
        <view-handler>
            org.icefaces.netbeans.rave.web.ui.appbase.faces.ViewHandlerImpl
        </view-handler>
        <variable-resolver>web.DelegatingVariableResolver</variable-resolver>
    </application>

web.xmlにもSeasarの設定を追記します。

    <listener>
        <listener-class>org.seasar.framework.container.servlet.S2ContainerListener</listener-class>
    </listener>

    <filter>
        <filter-name>s2filter</filter-name>
        <filter-class>org.seasar.framework.container.filter.S2ContainerFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>s2filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

app.diconはこんな感じです。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
        "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="convention.dicon"/>
    <include path="aop.dicon"/>
    <include path="s2jdbc.dicon"/>
    <component name="personDao" class="dao.PersonDao"/>
    <component name="Page1" class="webapplication1.Page1" instance="request"/>
</components>

Page1コンポーネントはinstance="request"としています。(faces-config.xmlの設定と合わせた)
先ほどのweb.xmlでs2filterの設定を追加したのは、Seasarでrequestスコープのコンポーネント管理をするためです。

実は僕はここで大はまりしました。最初は、Page1の記述をfaces-config.xmlに、personDaoの記述をapp.diconにしていたのですが、それだと例外が発生します。どうやら、faces-config.xml内のコンポーネントはfaces-config.xml内のコンポーネントしかインジェクションできないようなのです。つまり、Seasarコンポーネントを使いたい場合は、PageクラスからSeasarで管理しなければいけないのです。ここが分からなくて、夜中の3時まではまってしまいました。
あと、NetBeansプラグインの問題?で、勝手にfaces-config.xmlにPage1のエントリを作ってしまうのです。一度消したはずなのに、知らずに復活していました。インジェクションされないので??としばらく悩みました。
Page1クラスはこんな感じです。

package webapplication1;

import com.icesoft.faces.component.jsfcl.data.DefaultSelectedData;
import com.sun.rave.faces.data.DefaultSelectItemsArray;
import com.sun.rave.web.ui.appbase.AbstractPageBean;
import dao.PersonDao;
import entity.NewClass;
import javax.faces.FacesException;

public class Page1 extends AbstractPageBean {

 〜中略〜

    public Page1() {

    }

    public void init() {
        super.init();
        try {
            _init();
        } catch (Exception e) {
            log("Page1 Initialization Failure", e);
            throw e instanceof FacesException ? (FacesException) e: new FacesException(e);
        }
    }

    public void preprocess() {
        name = "preprocess";
    }

    public void prerender() {
        name = "prerender";
        name = personDao.getPerson().name;
    }

    public void destroy() {
    }

    protected PersonDao personDao;

    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    protected String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

jdbc.diconはこんな感じです。データベースはNetBeansに付属しているサービスのTravelを使いました。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
	"http://www.seasar.org/dtd/components24.dtd">
<components namespace="jdbc">
	<include path="jta.dicon"/>
	<include path="jdbc-extension.dicon"
		condition="@org.seasar.framework.util.ResourceUtil@isExist('convention.dicon')"/>

	<component class="org.seasar.extension.jdbc.impl.BasicResultSetFactory"/>
	<component class="org.seasar.extension.jdbc.impl.ConfigurableStatementFactory">
		<arg>
			<component class="org.seasar.extension.jdbc.impl.BasicStatementFactory"/>
		</arg>
		<property name="fetchSize">100</property>
	</component>

	<!-- for Derby -->
	<component name="xaDataSource"
		class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
		<property name="driverClassName">
			"org.apache.derby.jdbc.EmbeddedDriver"
		</property>
		<property name="URL">
			"jdbc:derby://localhost:1527/travel"
		</property>
		<property name="user">"travel"</property>
		<property name="password">"travel"</property>
	</component>

	<component name="connectionPool"
		class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl">
		<property name="timeout">600</property>
		<property name="maxPoolSize">10</property>
		<property name="allowLocalTx">true</property>
		<destroyMethod name="close"/>
	</component>

	<component name="dataSource"
		class="org.seasar.extension.dbcp.impl.DataSourceImpl"
	/>
</components>

とりあえず、これで動きました。ICEFacesSeasarを連携させるもっといい方法を知っている方がいれば、教えてください。
今回の件ではSpringに感謝感謝です。

InputRichTextのToolbarのカスタマイズ

フレームワークの選択を「IceFaces」にして別のプロジェクトを作って、InputRichTextのカスタマイズをしました。やり方は先日と同じ方法です。
すると、ちゃんとカスタマイズできました。「Visual Web ICEFaces」に問題があるのでしょうか?

PanelTabSetとInputRichText

ようやく動かせるようになってきたので、前から興味のあったコンポーネントを試してみます。
まずはPanelTabSetからで、そのままパレットからドロップします。デフォルトでいろいろなコンポーネントがついてきました。

<?xml version="1.0" encoding="UTF-8"?>
<jsp:root version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ice="http://www.icesoft.com/icefaces/component" xmlns:jsp="http://java.sun.com/JSP/Page">
    <jsp:directive.page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"/>
    <f:view>
        <html id="outputHtml1">
            <head id="outputHead1">
                <ice:outputStyle href="./resources/stylesheet.css" id="outputStyle1"/>
                <ice:outputStyle href="./xmlhttp/css/xp/xp.css" id="outputStyle2"/>
            </head>
            <body id="outputBody1" style="-rave-layout: grid">
                <ice:form id="form1">
                    <ice:panelTabSet height="264" id="panelTabSet1" selectedIndex="2" style="position: absolute; left: 48px; top: 24px" tabPlacement="Top" width="504">
                        <ice:panelTab id="panelTab1" label="panelTab1">
                            <ice:panelLayout id="panelLayout1" style="position: inherit; width: 100%; height: 100%;">
                                <ice:selectOneRadio id="selectOneRadio1" partialSubmit="true">
                                    <f:selectItems id="selectOneRadio1selectItems" value="#{Page2.selectOneRadio1DefaultItems}"/>
                                </ice:selectOneRadio>
                            </ice:panelLayout>
                        </ice:panelTab>
                        <ice:panelTab id="panelTab2" label="panelTab2">
                            <ice:panelLayout id="panelLayout2" style="position: inherit; width: 100%; height: 100%;">
                                <ice:inputText id="inputText1" value="inputText value"/>
                            </ice:panelLayout>
                        </ice:panelTab>
                        <ice:panelTab id="panelTab3" label="panelTab3">
                            <ice:panelLayout id="panelLayout3" style="position: inherit; width: 100%; height: 100%;">
                                <ice:selectOneMenu id="selectOneMenu1" partialSubmit="true">
                                    <f:selectItems id="selectOneMenu1selectItems" value="#{Page2.selectOneMenu1DefaultItems}"/>
                                </ice:selectOneMenu>
                            </ice:panelLayout>
                        </ice:panelTab>
                    </ice:panelTabSet>
                </ice:form>
            </body>
        </html>
    </f:view>
</jsp:root>

この状態のまま実行します。すると画面いっぱいにタブ画面が表示されます。PanelTabSet自体にはWidth設定はないようですね。
そこでPanelLayoutをドロップし、その中にPanelTabSetをドロップするとWidth設定ができました。なるほど。
でも、SelectOneRadioの縦位置が真ん中になっています。うーん、それぞれの位置関係がよく分からない。それぞれの領域の左上が原点ではないということか?要調査です。

InputRichTextはツールバーがごちゃごちゃしているのでカスタマイズしたいと思い、ICEFaces.orgのInputRichTextの解説を見ながら試してみました。

 The following steps are involved in creating the custom toolbar:

   1. Create a config file under the webapp, defining the custom toolbar(e.g.) js/config.js
      FCKConfig.ToolbarSets["MyToolbar"] = [['Bold','Italic', 'Underline'], ['Save']] ;
   2. Set the "toolbar" attribute on the inputRichText component to define the custom toolbar name and set the "customConfigPath" attribute to reference the custom config.js.(e.g.)
      <ice:inputRichText toolbar="MyToolbar" customConfigPath="js/config.js"/>

結果はちゃんと動きませんでした。MyToolbarがありませんとエラーがでています。
デザイナ画面でもToolbarは選択制になっていて、他に選ぶ余地がなさそうです。・・・・訳が分からない。

NetBeansとの統合環境はICEFaces.orgから最新をダウンロードしてきています。NetBeansのデフォルトのデザイナでも同じ現象なので、何かうまくいかない理由があるのでしょうね・・・とほほな感じです。