7 Replies Latest reply on Feb 17, 2007 7:08 AM by quilleashm

    Conversation versus Session Context

    jrosskopf

      Hello everybody,

      I´m a little bit confused about using the conversation context in a backing bean.

      I have the following facelet code:

      <ui:define name="body">
      
       <h:messages globalOnly="true" styleClass="message" />
       <h:form id="panel" >
       <h:panelGrid columns="1">
      
       <h:panelGroup>
       <h:selectOneMenu valueChangeListener="#{selectItemFacade.handleItemTypeChange}" onchange="submit()">
       <f:selectItems value="#{selectItemFacade.availableItemTypes}" />
       </h:selectOneMenu>
       </h:panelGroup>
      
       <h:panelGroup>
       <h:panelGrid binding="#{selectItemFacade.selectedItemPreview}" />
       </h:panelGroup>
      
       <h:panelGroup>
       <h:commandButton id="edit_button" action="#{selectItemFacade.edit}" />
       </h:panelGroup>
      
       </h:panelGrid>
       </h:form>
       </ui:define>
      </ui:composition>
      


      As you can see, some components are generated based on the selection made in the selectOneMenu. That works fine.

      @Stateful
      @Scope(ScopeType.CONVERSATION)
      @Name("selectItemFacade")
      public class SelectItemFacadeBean implements SelectItemFacade {
      
       @Logger
       Log log;
      
       // Bijection
       @Out()
       ItemDescription selectedItemType;
       @Out(required=false)
       Item item;
      
       // Instance
       HtmlPanelGrid selectedItemPreview;
      
       @Create
       public void init() {
       this.selectedItemPreview = (HtmlPanelGrid)FacesContext.getCurrentInstance().getApplication().createComponent(HtmlPanelGrid.COMPONENT_TYPE);
       this.selectedItemType = ItemRegistry.INSTANCE.getDefaultItemDescription();
       updateSelectedItemComponents();
       }
      
       private void updateSelectedItemComponents() {
       if (this.selectedItemType != null && this.selectedItemPreview != null) {
       selectedItemType.getPreviewComponent(FacesContext.getCurrentInstance(), this.selectedItemPreview, this.item);
       }
       }
      
       public Item getItem() {
       return item;
       }
      
       public void setItem(Item item) {
       this.item = item;
       }
      
       public SelectItem[] getAvailableItemTypes() {
       return ItemRegistry.INSTANCE.getAvailableTypesAsSelectItem();
       }
      
       public ItemDescription getSelectedItemType() {
       return selectedItemType;
       }
      
       public void setSelectedItemType(ItemDescription selectedItemType) {
       this.selectedItemType = selectedItemType;
       }
      
       public HtmlPanelGrid getSelectedItemPreview() {
       return selectedItemPreview;
       }
      
       public void setSelectedItemPreview(HtmlPanelGrid selectedItemPreview) {
       this.selectedItemPreview = selectedItemPreview;
       }
      
       // Actions
       public void handleItemTypeChange(ValueChangeEvent evt) {
       ItemDescription desc = ItemRegistry.INSTANCE.getDescription((String)evt.getNewValue());
       if (desc != null) {
       this.selectedItemType = desc;
       updateSelectedItemComponents();
       log.debug("Item type changed to " + this.selectedItemType.getLabel());
       }
       }
      
       public String edit() {
       log.debug("Action: edit called;");
      
       if (this.selectedItemType != null && this.item == null) {
       log.debug("Creating " + selectedItemType.getLabel() + " and getting edit Component;");
       this.item = selectedItemType.createItemInstance();
       //selectedItemType.getEditComponent(FacesContext.getCurrentInstance(), this.selectedItemEditor, this.item, "createItemFacade.item.tags");
       }
      
       return NavCodes.ACTION_EDIT;
       }
      
       @Destroy @Remove
       public void destroy() { }
      
      }
      


      In the selectItemFacade component I want to outject the selectedItemType and the item to the session context, as the instances are needed for editing in another component.

      Futher I thougth outjecting selectedItemPreview isn´t needed as the instance is private to the stateful session bean.

      If I enter select.xhtml everything works fine for the moment. But if I click on the edit_button the page is redirected to the seam debug page and in the logfile there is the following error:

      08:26:51,406 ERROR [SeamExceptionFilter] uncaught exception
      javax.servlet.ServletException: /item/select.xhtml @23,71 binding="#{selectItemFacade.selectedItemPreview}": Target Unreachable, identifier 'selectItemFacade' resolved to null
      


      Why is the instance in the stateful session bean now null.
      Can somebody explain me, what I´m doing wrong? I already placed a @Begin annotation on the edit() method. No effect.

      Thank you in advance.
      Regards
      ---
      Joachim

        • 1. Re: Conversation versus Session Context

          I think this happens because you don't explicitly start the conversation which results in you having short running conversations (basically makes your component request scoped from what I understand). If you want your conversation last longer you need to start the long-running conversation by using @Begin annotation.

          • 2. Re: Conversation versus Session Context

            This is because you have a component binding into a conversation scoped component. The conversation context is only available AFTER the restore view phase, but control bindings like this are executed in the restore view phase.

            To work around this have a event scoped component that hold your ui component bindings and inject it into your conversation component.

            @Name( "componentBindings" )
            @Scope( ScopeType.EVENT )
            public class ComponentBindings
            {
             private HtmlPanelGrid selectedItemPreview;
            
             // getters and setters
            }
            


            and then

             @In
             private ComponentBindings componentBindings;
            


            in your conversation component.

            HTH.

            Mike.

            • 3. Re: Conversation versus Session Context
              jrosskopf

              Hello Mike,

              thank you for reply. Unfortunatly that is not documented in http://docs.jboss.com/seam/1.1.6.GA/reference/en/html/concepts.html#d0e2401

              But anyhow, is the componentBindings component a stateful / stateless session bean or just a POJO? When and where should the
              instance be created and of course the bound UIComponent set?

              More Confusion!
              Regards.
              ---
              Joachim

              • 4. Re: Conversation versus Session Context

                No it's not documented, I've added an item to JIRA for this to be added somewhere.

                ComponentBindings is just a POJO/Java Bean with Event scope. The component will automatically be created during the Restore View for a postback request or otherwise Render Response when the first binding is encountered in the view.

                • 5. Re: Conversation versus Session Context
                  saeediqbal1

                  reference document is ancient :) i am trying to stop following it but its hard not to look at it.

                  • 6. Re: Conversation versus Session Context
                    jrosskopf

                    Hello,

                    sorry bothering you again.
                    I don´t fully understand this pattern.

                    What I did so far is creating a ComponentBindings-POJO

                    @Name("componentBindings")
                    @Scope(ScopeType.EVENT)
                    public class ComponentBindings {
                     @Logger
                     Log log;
                    
                     HtmlPanelGrid selectedItemPreview;
                     HtmlPanelGrid selectedItemEditor;
                    
                     @Create
                     public void init() {
                     log.debug(LogFormat.INSTANCE.format(LogFormat.L_INSTANCE, ComponentBindings.class.getSimpleName()));
                     }
                    
                     public HtmlPanelGrid getSelectedItemEditor() {
                     return selectedItemEditor;
                     }
                     public void setSelectedItemEditor(HtmlPanelGrid selectedItemEditor) {
                     this.selectedItemEditor = selectedItemEditor;
                     }
                    
                     public HtmlPanelGrid getSelectedItemPreview() {
                     return selectedItemPreview;
                     }
                     public void setSelectedItemPreview(HtmlPanelGrid selectedItemPreview) {
                     this.selectedItemPreview = selectedItemPreview;
                     }
                    }
                    


                    My backing bean looks now like this:

                    @Stateful
                    @Name("selectItemFacade")
                    @Scope(ScopeType.CONVERSATION)
                    public class SelectItemFacadeBean implements SelectItemFacade {
                    
                     @Logger
                     Log log;
                    
                     // Bijection
                     @Out
                     ItemDescription selectedItemType;
                     @Out(required=false)
                     Item item;
                    
                     // Instance
                     @In(create=true)
                     ComponentBindings componentBindings;
                    
                     @Create @Begin()
                     public void init() {
                     this.componentBindings.setSelectedItemPreview((HtmlPanelGrid)FacesContext.getCurrentInstance().getApplication().createComponent(HtmlPanelGrid.COMPONENT_TYPE));
                     this.selectedItemType = ItemRegistry.INSTANCE.getDefaultItemDescription();
                     updateSelectedItemComponents();
                     }
                    
                     private void updateSelectedItemComponents() {
                     if (this.selectedItemType != null && this.componentBindings.getSelectedItemPreview() != null) {
                     selectedItemType.getPreviewComponent(FacesContext.getCurrentInstance(), this.componentBindings.getSelectedItemPreview(), this.item);
                     }
                     }
                    
                     public Item getItem() {
                     return item;
                     }
                    
                     public void setItem(Item item) {
                     this.item = item;
                     }
                    
                     public SelectItem[] getAvailableItemTypes() {
                     return ItemRegistry.INSTANCE.getAvailableTypesAsSelectItem();
                     }
                    
                     public ItemDescription getSelectedItemType() {
                     return selectedItemType;
                     }
                    
                     public void setSelectedItemType(ItemDescription selectedItemType) {
                     this.selectedItemType = selectedItemType;
                     }
                    
                     public ComponentBindings getComponentBindings() {
                     return componentBindings;
                     }
                    
                     public void setComponentBindings(ComponentBindings bindings) {
                     this.componentBindings = bindings;
                     }
                    
                     // Actions
                     @Begin(join=true)
                     public void handleItemTypeChange(ValueChangeEvent evt) {
                     ItemDescription desc = ItemRegistry.INSTANCE.getDescription((String)evt.getNewValue());
                     if (desc != null) {
                     this.selectedItemType = desc;
                     updateSelectedItemComponents();
                     log.debug("Item type changed to " + this.selectedItemType.getLabel());
                     }
                     }
                    
                     @Begin(join=true)
                     public String edit() {
                     log.debug("Action: edit called;");
                    
                     if (this.selectedItemType != null && this.item == null) {
                     log.debug("Creating " + selectedItemType.getLabel() + " and getting edit Component;");
                     this.item = selectedItemType.createItemInstance();
                     }
                    
                     return NavCodes.ACTION_EDIT;
                     }
                    
                     @Destroy @Remove
                     public void destroy() { }
                    
                    }
                    


                    My Page looks now like this:

                    <ui:define name="body">
                    
                     <h:messages globalOnly="true" styleClass="message" />
                     <h:form id="panel" >
                     <h:panelGrid columns="1">
                    
                     <h:panelGroup>
                     <h:selectOneMenu valueChangeListener="#{selectItemFacade.handleItemTypeChange}" onchange="submit()">
                     <f:selectItems value="#{selectItemFacade.availableItemTypes}" />
                     </h:selectOneMenu>
                     </h:panelGroup>
                    
                     <h:panelGroup>
                     <h:panelGrid binding="#{selectItemFacade.componentBindings.selectedItemPreview}" />
                     </h:panelGroup>
                    
                     <h:panelGroup>
                     <h:commandButton id="edit_button" action="#{selectItemFacade.edit}" />
                     </h:panelGroup>
                    
                     </h:panelGrid>
                     </h:form>
                    
                    </ui:define>
                    


                    But after pushing the edit-button I still get the following exception:
                    javax.faces.el.PropertyNotFoundException: /item/select.xhtml @23,89 binding="#{selectItemFacade.componentBindings.selectedItemPreview}": Target Unreachable, identifier 'selectItemFacade' resolved to null
                    


                    I got redirected to the seam debug page. The conversation context contains then:
                    - Conversation Context (1)
                    org.jboss.seam.core.conversation
                    org.jboss.seam.core.facesMessages
                    org.jboss.seam.core.persistenceContexts
                    org.jboss.seam.core.redirect
                    org.jboss.seam.debug.lastException
                    selectItemFacade
                    selectedItemType
                    


                    Can anybody help?
                    Thank you.
                    ---
                    Joachim

                    • 7. Re: Conversation versus Session Context

                      You still have the same problem.

                      You want to access the componentBindings component directly NOT through your selectItemFacade. The selectItemFacade isn't accessible (conversation scope not active yet, see above) when the bindings are done.

                      You should refer to the componentBindings directly in the view.

                      binding="#{componentBindings.selectedItemPreview}"

                      Then the component bindings are injected into the selectedItemFacade so you can access the component via that dependency.