webtier@glassfish.java.net

Re: [webtier] JSF 2.0 composite components with method-valued attributes

From: Jim Driscoll <Jim.Driscoll_at_Sun.COM>
Date: Wed, 11 Feb 2009 20:47:22 -0800

"look into Lincoln's bug" has been on my todo list all week.

Sounds like I should make time to actually give it a closer look tomorrow.

Can either of you give me a *short* composite component example that
should work, but doesn't? That'd help a lot. Just something with all
the parts that aren't necessary to reproduce the bug stripped away.

Jim

On 2/11/09 8:10 PM, Lincoln Baxter, III wrote:
> I've experienced a similar issue when attemting to pass a validator=""
> through a composite component.
>
> (possibly got the wrong exception names here, I don't have a
> working-broken example in front of me. har har)
>
> JSF will throw a ClassCastException and complain that it was expecting
> an ElFieldExpression but got an ElMethodExpression , even if I specify
> the method-signature="" attribute in my composite:interface. I want to
> pass-through a validator method of the form #{myBean.validatorMethod},
> but haven't been able to find a way to do this. Been having lots of
> qwerky issues like this actually. ActionListeners you know I'm already
> having some issues with, still haven't figured that one out, wish I
> could provide more details.
>
> --Lincoln
>
> On Wed, 2009-02-11 at 15:21 -0800, Jim Driscoll wrote:
>> Well, when I did the following blog,
>> http://weblogs.java.net/blog/driscoll/archive/2008/12/jsf_20_wiring_u_1.html
>>
>> I had two attribute methods that weren't named"action" - they were
>> named move1to2 and such.
>>
>> I haven't pored over the rest of your email, yet, but will when I have a
>> minute.
>>
>> Jim
>>
>> On 2/11/09 3:04 PM,webtier_at_javadesktop.org <mailto:webtier_at_javadesktop.org> wrote:
>> > Hello,
>> >
>> > I wonder if method-expression passing through a composite component attribute is supposed to work. I just want be able to specify an"action" method which would be passed to a command button. Unfortunately, the code works only if the attribute has the name"action". And I wanted to get this method from a controller. Here's a code sample:
>> >
>> >
>> > <?xml version='1.0' encoding='UTF-8' ?>
>> > <!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>> > <html xmlns="http://www.w3.org/1999/xhtml"
>> > xmlns:ui="http://java.sun.com/jsf/facelets"
>> > xmlns:c="http://java.sun.com/jsp/jstl/core"
>> > xmlns:f="http://java.sun.com/jsf/core"
>> > xmlns:h="http://java.sun.com/jsf/html"
>> > xmlns:composite="http://java.sun.com/jsf/composite">
>> >
>> > <composite:interface name="adjustableColumnHeader">
>> > <composite:attribute name="header" required="true" type="String" shortDescription="header to use"/>
>> > <composite:attribute name="action" method-signature="java.lang.String act()" targets="hidebutton"/>
>> > </composite:interface>
>> > <composite:implementation>
>> > <th>
>> > <h:panelGrid columns="2">
>> > <h:outputText styleClass="tableHeader" value="#{compositeComponent.attrs.header}"/>
>> > <h:commandButton id="hidebutton" value="hide"
>> > action="#{compositeComponent.attrs.action}"/>
>> > </h:panelGrid>
>> > </th>
>> > </composite:implementation>
>> > </html>
>> >
>> > it is used later like:
>> > <composite:interface name="adjustableColumnHeader">
>> > <composite:attribute name="header" required="true" type="String" shortDescription="header to use"/>
>> > <composite:attribute name="headerClass" required="false" type="String" shortDescription="header class to use"/>
>> > <composite:attribute name="alwaysShown" required="false" type="Boolean" default="false" shortDescription="if true the column cannot be hidden"/>
>> > <composite:attribute name="firstOne" required="false" type="Boolean" default="false" shortDescription="if true this is the first column, hide button is rendered at its he
>> > <composite:attribute name="action" method-signature="java.lang.String act()" targets="hidebutton"/>
>> >
>> > <composite:attribute name="tablebean" required="true" shortDescription="a controller">
>> > <composite:attribute name="shownItems" required="true" type="java.util.Map" shortDescription="map which lists shown items"/>
>> > <!--<composite:attribute name="hideByCheckboxes" targets="hidebutton" required="true" method-signature="java.lang.String hideByCheckboxes()" shortDescription="the actio
>> > <composite:attribute name="checkboxes" required="true" type="java.util.Map" shortDescription="map which given header name tells if a header checkbox is set"/>
>> > </composite:attribute>
>> > </composite:interface>
>> > <composite:implementation>
>> > <h:panelGroup rendered="#{(not empty compositeComponent.attrs.tablebean.shownItems[compositeComponent.attrs.header]) or compositeComponent.attrs.alwaysShown}">
>> > <th class="TblColHdr_sun4 #{compositeComponent.attrs.headerClass}" scope="col">
>> > <h:panelGrid columns="2" border="0" styleClass="TblHdrTbl_sun4" cellpadding="0" cellspacing="0">
>> > <h:outputText styleClass="tableHeader" value="#{compositeComponent.attrs.header}"/>
>> > <!-- the first column always stays -->
>> >
>> > <!--<h:commandButton id="hidebutton" value="hide" action="#{compositeComponent.attrs.tablebean.hideByCheckboxes}" rendered="#{compositeComponent.attrs.firstOne}"
>> > <h:commandButton id="hidebutton" value="hide" action="#{compositeComponent.attrs.action}" rendered="#{compositeComponent.attrs.firstOne}"/>
>> > <h:selectBooleanCheckbox rendered="#{not compositeComponent.attrs.alwaysShown}"
>> > value="#{compositeComponent.attrs.tablebean.checkboxes[compositeComponent.attrs.header]}"/>
>> > </h:panelGrid>
>> > </th>
>> > </h:panelGroup>
>> > </composite:implementation>
>> >
>> > This text below will also not be displayed.
>> >
>> > </body>
>> > </html>
>> >
>> > it is used later like
>> >
>> >
>> > <gc:adjustableColumnHeader tablebean="#{JobsPageBean.tableBean}" header="Status" action="#{JobsPageBean.tableBean.hideByCheckboxes}"/>
>> >
>> > The problem is that if I rename the attribute to say 'act' - it stops working, I get a
>> >
>> > StandardWrapperValve[FacesServlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw
>> > exception java.lang.IllegalStateException: java.lang.IllegalAccessException:
>> > Class javax.faces.component.StateHolderSaver can not access a member of
>> > class com.sun.faces.facelets.tag.TagAttributeImpl$AttributeLookupMethodExpression
>> > with modifiers"public"
>> >
>> > at javax.faces.component.StateHolderSaver.restore(StateHolderSaver.java:112)
>> > at javax.faces.component.UIComponentBase.restoreAttachedState(UIComponentBase.java:1488)
>> > at javax.faces.component.UICommand.restoreState(UICommand.java:340)
>> > at javax.faces.component.html.HtmlCommandButton.restoreState(HtmlCommandButton.java:861)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1200)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1215)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1215)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1215)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1234)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1215)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1234)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1215)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1215)
>> > at javax.faces.component.UIComponentBase.processRestoreState(UIComponentBase.java:1215)
>> > at javax.faces.component.UIViewRoot.processRestoreState(UIViewRoot.java:844)
>> > at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:174)
>> > at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:130)
>> > at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:271)
>> > at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:168)
>> > at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:174)
>> > at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:102)
>> > at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:107)
>> > at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
>> > at javax.faces.webapp.FacesServlet.service(FacesServlet.java:310)
>> > at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:427)
>> > at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:315)
>> > at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:287)
>> > at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:218)
>> > at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
>> > at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
>> > at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
>> > at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:98)
>> > at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:222)
>> > at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
>> > at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
>> > at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
>> > at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
>> > at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:166)
>> > at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)
>> > at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)
>> > at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)
>> > at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)
>> > at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:288)
>> > at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:647)
>> > at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:579)
>> > at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:831)
>> > 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.connector.grizzly.TaskBase.run(TaskBase.java:265)
>> > at com.sun.enterprise.web.connector.grizzly.ssl.SSLWorkerThread.run(SSLWorkerThread.java:106)
>> > Caused by: java.lang.IllegalAccessException: Class javax.faces.component.StateHolderSaver can not access a member of class com.sun.faces.facelets.tag.TagAttributeImpl$AttributeLo
>> > at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
>> > at java.lang.Class.newInstance0(Class.java:349)
>> > at java.lang.Class.newInstance(Class.java:308)
>> > at javax.faces.component.StateHolderSaver.restore(StateHolderSaver.java:106)
>> > ... 50 more
>> > |#]
>> >
>> >
>> > (I am running glassfish 2.1)
>> >
>> >
>> > My first guess was I have wrong method signature (BTW when it is just String a() I get
>> > a class not found exception - author page error, may be one cannot use a String as a shortcut here?). But looking at the code in jsf-ri/src/com/sun/faces/application/view/MultiViewHandler.java...
>> > well it seems there's a lot of things done if attribute name is 'special', but if not - well even targets are ignored (although it looks like if targets is empty nothing happens at all). May be it's just a missing line or two? BTW: Also I am not sure the whole thing works for"String a( )",
>> > may be method arguments are not set to empty Class array then. But I may be wrong here.
>> >
>> > Actually, I wanted to use a 'controller' and do something like:
>> >
>> >
>> > <composite:interface name="adjustableColumnHeader">
>> > <composite:attribute name="header" required="true" type="String" shortDescription="header to use"/>
>> > <composite:attribute name="tablebean" required="true" shortDescription="a controller">
>> > <composite:attribute name="shownItems" required="true" type="java.util.Map" shortDescription="map which lists shown items"/>
>> > <composite:attribute name="hideByCheckboxes" targets="hidebutton" required="true" method-signature="java.lang.String hideByCheckboxes()"/>
>> > <composite:attribute name="checkboxes" required="true" type="java.util.Map" shortDescription="map which given header name tells if a header checkbox is set"/>
>> > </composite:attribute>
>> > </composite:interface>
>> > <composite:implementation>
>> > <th
>> > <h:panelGrid columns="2" border="0" cellpadding="0" cellspacing="0">
>> > <h:outputText styleClass="tableHeader" value="#{compositeComponent.attrs.header}"/>
>> > <h:commandButton id="hidebutton" value="hide" action="#{compositeComponent.attrs.tablebean.hideByCheckboxes}"/>
>> >
>> > <!--rendered="#{compositeComponent.attrs.firstOne}"-->
>> > <h:selectBooleanCheckbox rendered="#{not compositeComponent.attrs.alwaysShown}"
>> > value="#{compositeComponent.attrs.tablebean.checkboxes[compositeComponent.attrs.header]}"/>
>> > </h:panelGrid>
>> > </th>
>> > </composite:implementation>
>> >
>> > and of course then I cannot choose the name of the attribute.
>> >
>> > Also it's a bit strange Jim's controller example uses only attribute-properties and
>> > no attribute-methods. I guess turning switchlist example into a controller based one,
>> > as Ed originally suggested, would be a lot of clue here.
>> >
>> > Thanks a lot in advance!
>> > [Message sent by forum member 'nzinoviev' (nzinoviev)]
>> >
>> > http://forums.java.net/jive/thread.jspa?messageID=331385
>> >
>> > ---------------------------------------------------------------------
>> > To unsubscribe, e-mail:webtier-unsubscribe_at_glassfish.dev.java.net <mailto:webtier-unsubscribe_at_glassfish.dev.java.net>
>> > For additional commands, e-mail:webtier-help_at_glassfish.dev.java.net <mailto:webtier-help_at_glassfish.dev.java.net>
>> >
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail:webtier-unsubscribe_at_glassfish.dev.java.net <mailto:webtier-unsubscribe_at_glassfish.dev.java.net>
>> For additional commands, e-mail:webtier-help_at_glassfish.dev.java.net <mailto:webtier-help_at_glassfish.dev.java.net>
>>