4 Replies Latest reply on Dec 18, 2006 5:33 PM by gavin.king

    Component bindings in Conversation scope

      I think I'm correct in saying that currently you cannot have a conversation scoped component that has UIComponent bindings.

      e.g. of a minimal example

      Facelet view

      <!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"
       <h:outputText value="Text" binding="#{testBean.outputText}"/>
       <h:commandButton value="Submit"/>

      And the bean

      @Name( "testBean" )
      @Scope( ScopeType.CONVERSATION )
      public class TestBean
       private HtmlOutputText outputText;
       public HtmlOutputText getOutputText()
       return outputText;
       public void setOutputText( HtmlOutputText outputText )
       this.outputText = outputText;

      It is fine during the initial rendering of the page but if you click the button to do a postback the setOutputText() is called during the restore view phase which is before the conversation context is created by Seam (done in the afterPhase() of the restore view), so the component is unavailable.

      For completeness here's the exception.

      javax.el.PropertyNotFoundException: /index.xhtml @11,68 binding="#{testBean.outputText}": Target Unreachable, identifier 'testBean' resolved to null
       at com.sun.facelets.el.TagValueExpression.setValue(TagValueExpression.java:95)
       at com.sun.faces.lifecycle.RestoreViewPhase.doPerComponentActions(RestoreViewPhase.java:228)
       at com.sun.faces.lifecycle.RestoreViewPhase.doPerComponentActions(RestoreViewPhase.java:233)
       at com.sun.faces.lifecycle.RestoreViewPhase.doPerComponentActions(RestoreViewPhase.java:233)
       at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:186)
       at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:248)
       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.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:228)
       at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
       at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
       at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
       at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
       at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:212)
       at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:818)
       at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:624)
       at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:445)
       at java.lang.Thread.run(Thread.java:595)

      The short term workaround would be to put component bindings in an event scoped component and inject it into the conversation component but I'd prefer not to do this.

      Is it possible via some configuration/option to allow this particular combination? I looked at the SeamPhaseListener and it would be trivial to move the conversation setup code into the beforePhase() of restore view but I have no idea what side-effects this will have and if it will work.

      Is the conversation setup done after restore view for a particular reason? Would be very useful to me personally as I think I will frequently need to bind UI components to do certain types of dynamic page creation in the code. I'm willing to spend some time looking at/trying this if it's worth investigating a change.



        • 1. Re: Component bindings in Conversation scope

          This is correct, and no I can't fix it. The conversation id is only known after restoring the faces component tree.

          • 2. Re: Component bindings in Conversation scope

            Mike, I'm not sure what kind of dynamic page creation you are doing but you may find you can(should be able to?) do it from a facelets TagHandler rather than a component binding. You can certainly add/remove uicomponents from the tree, alter their children etc.

            • 3. Re: Component bindings in Conversation scope

              Thanks for the replies guys.

              Pete, I'll look into doing my logic inside a tag handler, I hadn't thought of doing it that way. I have a template with a grid panel and a ui:define inside. The define will insert things like input fields and combos. I then use some convention rules based of the name of the bean property each control value is bound to, to auto-populate these fields from database tables. To be fair I have to use a custom invoke handler in the template already to do this logic as it has to be done after the panel piece of the view is fully constructed but before encodeAll() is called.

              Gavin, I looked at the code and for the Sun RI at least it is possible to restore the conversation before the bindings are updated on the controls. By putting the conversation restore code in either the SeamStateManager.restoreView() or SeamViewHandler.restoreView(), the ViewRoot is available and built. I have no idea if you can guarantee this will work on all implementations, I'll have to check the spec and see.



              • 4. Re: Component bindings in Conversation scope

                I think its a bad idea for Seam to have its own ViewHandler, because other things like facelets and Ajax4JSF already customize the ViewHandler (and that already causes integration problems). Introducing yet another Viewhandler is going to make things worse.

                Just do you bindings on an event scoped object and inject that object in. Or bind them onto Map attributes, and then inject them into the component via the EL #{map.attribute}.