4 Replies Latest reply on Jun 10, 2008 5:50 AM by rgustav

    Using indirect 'action' on a <h:commandButton>

    rgustav

      Hopefully someone's got some hints on this...


      My goal is to be able to setup a Facelet tag which will output buttons with all the necessary standard decorations for 'alt', 'image', etc.


      So the standard repeat yourself ad nauseum command button approach works without error:


      <h:commandButton value="Reset" action="#{Demo.reset}" image="/image/Reset.png" alt="Reset" />
      



      The reset() method on my backing EJB3 component works, and Seam PageFlow routes to the proper View, I still need to figure out how to get immediate=true (omitted in this example) to render the View properly yet, but that's a different issue.


      Moving on to changing to use an indirect action like so:


      <jstl-core:set var="action" value="#{Demo.reset}" />
      <h:commandButton value="Reset" action="#{action}" image="/image/Reset.png" alt="Reset" />
      



      Fails with this error (same error when I use Facelets instead of the trivial JSTL used in this example):


      12:04:31,642 FATAL [application] javax.el.PropertyNotFoundException: /demo/DemoSearch.xhtml @35,102 action="#{action}": /demo/DemoSearch.xhtml @34,58 value="#{Demo.reset}": Property 'reset' not found on type org.javassist.tmp.java.lang.Object_$$_javassist_1
      javax.faces.el.EvaluationException: javax.el.PropertyNotFoundException: /demo/DemoSearch.xhtml @35,102 action="#{action}": /demo/DemoSearch.xhtml @34,58 value="#{Demo.reset}": Property 'reset' not found on type org.javassist.tmp.java.lang.Object_$$_javassist_1
           at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:84)
           at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:91)
           at javax.faces.component.UICommand.broadcast(UICommand.java:383)
           at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:447)
           at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:752)
           at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:97)
           at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
           at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117)
           at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
           at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
           at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
           at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._invokeDoFilter(TrinidadFilterImpl.java:238)
           at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:195)
           at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:138)
           at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92)
           at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
           at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
           at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83)
           at org.jboss.seam.debug.hot.HotDeployFilter.doFilter(HotDeployFilter.java:68)
           at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
           at org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:58)
           at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
           at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:85)
           at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
           at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
           at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
           at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
           at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69)
           at org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158)
           at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
           at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
           at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
           at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
           at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
           at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
           at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
           at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
           at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:432)
           at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
           at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
           at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
           at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
           at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
           at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262)
           at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
           at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
           at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446)
           at java.lang.Thread.run(Unknown Source)
      Caused by: javax.el.PropertyNotFoundException: /demo/DemoSearch.xhtml @35,102 action="#{action}": /demo/DemoSearch.xhtml @34,58 value="#{Demo.reset}": Property 'reset' not found on type org.javassist.tmp.java.lang.Object_$$_javassist_1
           at com.sun.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:70)
           at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:77)
           ... 47 more
      



      Thoughts?


      Thanks!
      --Ryan

        • 1. Re: Using indirect 'action' on a <h:commandButton>
          rgustav

          Researching into this problem further by digging into EL.  Would appear the problem is that the value of 'action' is being returned as a 'ValueExpression' when in actuality I need 'Demo.reset' to be a 'MethodExpression'.


          From what I read, this distinction is inferred from context of usage, but since usage is being delayed, this assumption is wrong this scenario.


          Now to see if there's a way to cast it to be a ValueExpression, instead of a MethodExpression.  I realize EL has no such concept, but if I'm lucky perhaps I can build my own with an ELResolver trick?  Let's find out...

          • 2. Re: Using indirect 'action' on a <h:commandButton>
            rgustav

            Ahh, further googling turned up the following, which discusses exactly my problem as described above (ValueExpression needing to be used as MethodExpressions):


            http://andrewfacelets.blogspot.com/2006/06/creating-composite-controls-with-jsf.html


            Several views as to how to address the problem.  One of the later commenters even claims jboss seam solves this and much more problem, which I've not yet seen to the case. ;)


            Even though this appears to be a JSF/Facelets issue, hopefully anyone else using Seam and having this problem will find it by searching this forum.

            • 3. Re: Using indirect 'action' on a <h:commandButton>
              dan.j.allen

              Avoid using <c:set> unless you have a very good reason to use it and you understand that it uses a fake context to store the result.


              A much, much, much better way to do this is to use <ui:decorate> (or <s:decorate>) combined with <ui:param>. Here is what you would do:


              button.xhtml


              <ui:composition>
              <h:commandButton action="#{fillMeIn.save}" value="Save"/>
              </ui:composition>



              page-in-app.xhtml


              <s:decorate template="button.xhtml">
              <ui:param name="fillMeIn" value="#{realComponent}"/>
              </s:decorate>



              Rinse and repeat!

              • 4. Re: Using indirect 'action' on a <h:commandButton>
                rgustav

                The <c:set> was for example purposes only, but I take your point.  I'll explore the technique you outline, see if there's something extra I can learn.


                I did succeed in getting a facelet tag to work using the formula described in the article I referenced in a prior reply.  It's ugly, and shouldn't be necessary in an ideal world.  But the developers which end up using the tag library I create won't really know or care.  I think it's worth it to preserve the simplicity and layering that facelet tags provide.


                A bit of diversion here... but I find myself just blown away by how JSF/Facelets are so amazingly powerful together.  I can abstract out all the hard details of using various JSF components, provide consistency, easy maintenance, and go the extra mile to enhance the tags with metadata awareness (e.g. auto-I18N labeling, automatic layout, automatic presentation of appropriate input component based on underlying enhanced datatype (date, money, phone, zip), automatic sorting of table results, etc).  Sky's the limit really.


                As a View technology JSF/Facelets has let me prototype the following minimal declaration with full functionality:


                <ui:composition xmlns:x="http://myexample" template="/layout/template.xhtml">
                        <ui:define name="body">
                                <x:panel entity="#{Demo.criteria}" header="${messages['Example.criteria']}">
                                        <x:input field="ssn"/>
                                        <x:input field="lastNa"/>
                                        <x:input field="firstNa"/>
                                        <x:input field="createDt" />
                                        <x:input field="phoneNumber"/>
                                        <x:input field="zipCode"/>
                                </x:panel>
                                <x:buttonsSearch/>
                                <x:resultTable results="#{Example.results}">
                                        <x:resultColumn field="firstNa"/>
                                        <x:resultColumn field="lastNa"/>
                                        <x:resultColumn field="birthDate"/>
                                        <x:resultColumn field="city"/>
                                        <x:resultColumn field="accountDate"/>
                                </x:resultTable>
                        </ui:define>
                </ui:composition>
                



                Add in Seam to handle state management, flow, etc. and application design starts to boil down to essentials of what's unique to each use case, with little repetition.


                Exciting times.  I've already forgotten what JSP even stands for. ;)


                Of course, standing on the shoulders of giants, and all that...