1 2 Previous Next 17 Replies Latest reply on Jan 5, 2010 1:20 AM by Tim Evers

    Ending Nested Natural Conversations

    Pablo Caballero Newbie

      hello, I'm trying to use natural conversations together with nested conversations. For example a parent conversation Order:266 is started and later another child conversation Item:23 is started. This all works fine, but if I try to end the child conversation Item:23 and go back to the natural parent conversation, I get the error Conversation id is already in use : Order:366. I tried using both natural and regular child conversations and I get the same error.


      I tried with seam 2.0.3.CR1 and also with seam 2.1.0.BETA1 with the same result. I've seen a number of JIRA issues(some closed, some pending) related to the error I'm getting and natural conversations. However, I could not find an issue that matched exactly what I'm trying to do (the closest JBSEAM-3183)


      This is the exception I get:



      19:37:55,554 ERROR SeamPhaseListener uncaught exception
      java.lang.IllegalStateException: Conversation id is already in use: Order:366
             at org.jboss.seam.core.Manager.updateCurrentConversationId(Manager.java:92)
             at org.jboss.seam.faces.FacesManager.beforeRedirect(FacesManager.java:76)
             at org.jboss.seam.faces.FacesManager.redirect(FacesManager.java:171)
             at org.jboss.seam.faces.Navigator.redirect(Navigator.java:46)
             at org.jboss.seam.navigation.RedirectNavigationHandler.navigate(RedirectNavigationHandler.java:53)
             at org.jboss.seam.navigation.Rule.execute(Rule.java:100)
             at org.jboss.seam.navigation.Navigation.navigate(Navigation.java:58)
             at org.jboss.seam.navigation.Pages.navigate(Pages.java:136)
             at org.jboss.seam.jsf.SeamNavigationHandler.handleNavigation(SeamNavigationHandler.java:42)
             at org.jboss.seam.navigation.Pages.handleOutcome(Pages.java:603)
             at org.jboss.seam.navigation.Pages.callAction(Pages.java:645)
             at org.jboss.seam.navigation.Pages.preRender(Pages.java:296)
             at org.jboss.seam.jsf.SeamPhaseListener.preRenderPage(SeamPhaseListener.java:560)
             at org.jboss.seam.jsf.SeamPhaseListener.beforeRenderResponse(SeamPhaseListener.java:471)
             at org.jboss.seam.jsf.SeamPhaseListener.beforeServletPhase(SeamPhaseListener.java:144)
             at org.jboss.seam.jsf.SeamPhaseListener.beforePhase(SeamPhaseListener.java:114)
             at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:222)
             at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:144)
             at javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)
             at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
             at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
             at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:147)
             at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:256)
             at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:362)
             at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:488)
             at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
             at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
             at com.ganapan.web.filter.UserFilter.doFilter(UserFilter.java:38)
             at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
             at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
             at com.ganapan.web.filter.NoCacheFilter.doFilter(NoCacheFilter.java:31)
             at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
             at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
             at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64)
             at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
             at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
             at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45)
             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.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(Thread.java:619)

      Any ideas?


      Thanks

        • 1. Re: Ending Nested Natural Conversations
          some one Newbie

          Without code/config files it would be next to impossible to guess the problem. One way to see what's happening however is to show a list of conversations that are currently in use/memory.


          The online guide provided has a nice little chunk of code to do this in chapter 7, The conversation list


          Put that in your facelets code and see where your conversations are being removed.

          • 2. Re: Ending Nested Natural Conversations
            Shane Bryzak Master

            Nested conversations aren't supported with natural conversation ID's.

            • 3. Re: Ending Nested Natural Conversations
              Pablo Caballero Newbie

              Thanks for your responses. Are there any plans to support natural conversation ID's with nested conversations in the near future?


              Also, would it be possible to document that nested conversations aren't supported with natural conversation ID's

              • 4. Re: Ending Nested Natural Conversations
                blabno Master

                Any news on this issue ? Nesting naturals is needed.

                • 5. Re: Ending Nested Natural Conversations
                  Tim Evers Master

                  Every time I've thought nested conversations were the way to go, I ended up realising that at the end of the day nested conversations are not worth it. For starters the complexity of nested conversations is generally just not worth it. Considering things like @PerNestedConversation are not ready for production code it stands to reason that nested conversations come with a lot of issues to think about.


                  Just use the conversation switcher, pass state between conversations as needed and keep a track of your own parent pointer.


                  In my app we have lots of wizard/nested type situations and I do not use nested conversations anywhere. I have my own navigation manager that handles switching between conversations and ending all conversations that are logically nested under another.

                  • 6. Re: Ending Nested Natural Conversations
                    Konstantin Larionov Newbie

                    Hello Tim,


                    I can agree with you. I came to the same conclusion after trying nested conversations for some weeks.


                    Could you, please, explain your solution in details?
                    Looks like I'll have to invent something similar and it would be nice to get some help.

                    • 7. Re: Ending Nested Natural Conversations
                      Tim Evers Master

                      Hey Konstantin,


                      I believe I have posted a fair bit of information about how I do navigation in another thread HERE


                      Unless you are in the early stages of development you may find it difficult to plug in. But, I'm happy to answer any questions you have about my code :)


                      That being said I'm closing in on go live day for my project so I'm not sure how often I'll be able to respond. But, I'll do my best to help out when I can if you need me.

                      • 8. Re: Ending Nested Natural Conversations
                        Artem Shramko Newbie

                        Hello Tim,


                        I work with my colleague Konstantin Larionov and right now we’re investigating your code and trying to use your approach in our system.


                        I’ve encountered with the next problem. When I invoke the method createController the ClassCastException is thrown. This happens because of the next cast:


                        T controller = clazz.cast(Component.getInstance(clazz, getScopeType(clazz), true));


                        The Component.getInstance(clazz, getScopeType(clazz), true) returns object of the class Object_$$_javassist_seam_16. This object has only one property – handler. But this handler contains the bean of the correct class. It seems that the class of the returned object is a proxy class. But I’ve checked it using Proxy.isProxyClass method and the answer was wrong. It’s not a proxy.


                        Maybe you encountered with such problem and can help me to solve it.


                        • 9. Re: Ending Nested Natural Conversations
                          Tim Evers Master

                          Hey Artem,


                          I haven't come across the issue that you are experiencing, but it really would help if you posted a bit more code so that I have some sort of context as to how we end up in this situation. Maybe then I can try to reproduce the error and give you a fix. :)


                          Does this problem always happen, or is it limited to a certain scenario? Is it always a certain Seam Bean that causes this? Or if multiple Seam beans cause this problem do they have the same Scope? Are you calling createController or is it happening inside the code that I wrote?


                          But, for starters: Anything that is (ClassName)_&&_javassist_seam_(number) is most certainly a proxy. So I'm not sure why the isProxyClass is returning false.
                          And more importantly as far as I'm aware any call to Component.getInstance MUST return a proxy or the Seam wont work properly.


                          You aren't accidently passing in a Interface for the clazz param are you?


                          Can you log out the value for the param clazz and the result of getScopeType(clazz) just so I can try to see if there's any reason why this should fail.


                          So yeah, more info would really help.

                          • 10. Re: Ending Nested Natural Conversations
                            Tim Evers Master

                            Just been looking at my code and I've realised that I have some legacy code in there to cater for how we started doing Navigation before NavigationManager really got going.


                            So, there are 2 createController methods in NavMan.


                            This one is the right one to be using. However, looking at my code the only place it should be used is inside NavigationUtils.setupNavigationAndForward method.


                            createController(String conversationId, Class<T> clazz)
                            



                            This one should not be used and is a leftover of old code. I really should go rip it out so no one on my team uses it by accident. (Not that it will actually break anything but it doesn't follow the model that I'm trying to enforce)


                            createController(Class<T> clazz)
                            



                            So, if you are calling createController manually then maybe you just need to do something slighly different. But if my code is dying inside NavUtils.setupNavigationAndForward then we have a problem :)


                            (I'm guessing we are on very different time zones based on the time your post is at so... I'm sorry if my replies are not very timely for you. I'm on the east coast of Australia if that makes it easier to know what my local time is)

                            • 11. Re: Ending Nested Natural Conversations
                              Artem Shramko Newbie

                              Hello Tim,




                              I’m just at the beginning of the implementing of your concept. So this is the first attempt to use your NavigationManager. :)


                              I simplified your code. I left only forward navigation. And I also removed all reference to the Breadcrumb class, perhaps I shouldn’t do it.




                              I have a session component that has only one action method invoked when I click on the link. In this method I call the NavigationUtils.setupNavigationAndForward






                              @Name("middlemanDashboard")
                              @AutoCreate
                              @Scope(ScopeType.SESSION)
                              
                              public class MiddlemanDashboardBean extends AbstractSeamComponent implements MiddlemanDashboard {
                              
                                  public void redirectToMiddleman() {
                              
                                      NavigationUtils.setupNavigationAndForward(new AbstractSimpleForwardBackNavigation<MiddlemenGridBean>(this
                                              .getConversationId())
                                              {
                              
                                          @Override
                                          public void navigateForward(MiddlemenGridBean controller) {
                                              navMan.redirectToModule(
                                                      getConversationId(),
                                                      "/restricted/navigators/middlemen.xhtml");
                                          }
                                      });
                                  }
                              }







                              The MiddlemenGridBean is the bean that should be used in the page to which the clicked link should redirect me. This bean has an interface MiddlemanGrid





                              @Stateful
                              @Name("middlemenGrid")
                              @Scope(ScopeType.CONVERSATION)
                              
                              public class MiddlemenGridBean extends AbstractSeamComponent implements MiddlemanGrid {
                              
                                  @In
                                  private EntityManager entityManager;
                              
                                  private ListDataModel dataModel;
                              
                                  @In(required = false)
                                  @Out(required = false)
                                  protected Middleman selectedMiddleman;
                              
                                  public ListDataModel getMiddlemen() {
                                      return dataModel;
                                  }
                              
                                  @Create
                                  @Observer(value = { "ipat.entity.Organization.deleted" }, create = false)
                                  public void fillMiddlemen() {
                              
                                      @SuppressWarnings("unchecked")
                                      List<Middleman> middlemen = entityManager.createQuery("from Middleman").getResultList();
                              
                                      if (selectedMiddleman != null && !middlemen.contains(selectedMiddleman)) {
                                          selectedMiddleman = null;
                                      }
                              
                                      dataModel = new ListDataModel(middlemen);
                                  }
                               
                                  public void onRowClick(ActionEvent event) {
                                      selectedMiddleman = (Middleman) dataModel.getRowData();
                                  }
                              
                                  @Destroy
                                  @Remove
                                  public void destroy() {
                                  }
                              
                              }
                              
                              
                              @Local
                              public interface MiddlemanGrid {
                              
                                  ListDataModel getMiddlemen();
                              
                                  void fillMiddlemen();
                              
                                  void onRowClick(ActionEvent event);
                              
                                  void destroy();
                              
                              }







                              I use the correct method createController(String conversationId, Class<T> clazz)
                              So the value for the clazz parameter is Class<T> (com.ims.ipat.web.grid.MiddlemenGridBean) and the result of the getScopeType(clazz) is CONVERSATION.




                              So, please look at my code. Perhaps you’ll find the problem.






                              (Yeah, we live in opposite time zone. I’m from Russia. But I guess time difference isn’t important problem for communication. :) )

                              • 12. Re: Ending Nested Natural Conversations
                                Tim Evers Master

                                Alrighty...


                                I'm going to try spend a bit of time trying to find out what would cause this, but so far I can't see anything that really pops out at me yet.


                                I may need you to post your new implementations of the NavUtils and the AbstractSimpleForwardBackNavigation as well.


                                Just one thing you may want to change in how you are using this concept though.


                                You have this on your Session bean at the moment.


                                public void redirectToMiddleman() {
                                
                                        NavigationUtils.setupNavigationAndForward(new AbstractSimpleForwardBackNavigation<MiddlemenGridBean>(this
                                                .getConversationId())
                                                {
                                
                                            @Override
                                            public void navigateForward(MiddlemenGridBean controller) {
                                                navMan.redirectToModule(
                                                        getConversationId(),
                                                        "/restricted/navigators/middlemen.xhtml");
                                            }
                                        });
                                    }
                                



                                Our goal with the NavigationManager was that each module/page/converstation took care of it's own navigation. What I mean by that is this. When I want to navigate to your MiddlemenGridBean I should use a method on that bean which will take care of navigating to the right page. This also allows me to pass params to the new conversation without using a Session scoped bean.


                                So how I would implement this in our system is like this:


                                public void redirectToMiddleman() {
                                
                                        NavigationUtils.setupNavigationAndForward(new AbstractSimpleForwardBackNavigation<MiddlemenGridBean>(this
                                                .getConversationId())
                                                {
                                
                                            @Override
                                            public void navigateForward(MiddlemenGridBean controller) {
                                                controller.gotoMiddlemenGrid();
                                            }
                                        });
                                    }
                                



                                And then in your MiddlemenGridBean you would have the method:


                                    public void gotoMiddlemenGrid() {
                                        navMan.redirectToModule(
                                            this.getConversationId(),
                                            "/restricted/navigators/middlemen.xhtml");
                                    }
                                



                                What this means is that the contract for navigating to the MiddlemenGridBean's page is all found on the MiddlemenGridBean itself. It knows the .xhtml page to go to and this also makes it much easier for new developers to know what data they must have setup before navigating there.


                                Looking at the gotoMiddlemenGrid function I know that I do not need to setup any data to navigate there. However, let's just say when you select a Middleman that you go off to some MiddlemanMaintenance page.


                                Then you would have something like this: (although I have no idea if this works with events... which maybe is what is causing us some issues... hmm, something for me to think about).
                                Anyway, your onRowClick would look something like this:


                                public void onRowClick(ActionEvent event) {
                                    NavigationUtils.setupNavigationAndForward(
                                        new AbstractSimpleForwardBackNavigation<MiddlemanMaintenanceBean>
                                            (this.getConversationId())
                                        {
                                            @Override
                                            public void navigateForward(MiddlemanMaintenanceBean controller) {
                                                controller.gotoMaintainMiddleman((Middleman) dataModel.getRowData());
                                            }
                                
                                        }
                                    }
                                



                                And then in the MiddlemanMaintenanceBean you would have the method:


                                    public void gotoMaintainMiddleman(Middleman middleman) {
                                        this.middleman = middleman; //Probably should requery it out here actually
                                        navMan.redirectToModule(...you know what goes here);
                                    }
                                



                                What this allows now is for every developer to know that in order to navigate to the MiddlemanMaintenancePage they need to pass an instance of Middleman to the gotoMaintainMiddleman method. So the MiddlemanMaintenanceBean is enforcing the navigation contract.


                                Ok, enough ranting for now as it is not actually fixing the problem. :)


                                However, I don't think you answered my question about this problem. Does it happen on 'every' navigation that you do? Or do some of them work?

                                • 13. Re: Ending Nested Natural Conversations
                                  Tim Evers Master

                                  I may have found the problem! (Hoping so anyways :P)


                                  It is directly related to what I just said above.


                                  Your navigation method:


                                  public void redirectToMiddleman() {
                                  
                                          NavigationUtils.setupNavigationAndForward(new AbstractSimpleForwardBackNavigation<MiddlemenGridBean>(this
                                                  .getConversationId())
                                                  {
                                  
                                              @Override
                                              public void navigateForward(MiddlemenGridBean controller) {
                                                  navMan.redirectToModule(
                                                          getConversationId(), //******THIS LINE IS NOT RIGHT*****
                                                          "/restricted/navigators/middlemen.xhtml");
                                              }
                                          });
                                      }
                                  



                                  Ok, as you can see I have put a comment on the line that I think is the problem. Essentially what you have said in this method is this:


                                  Navigate to the url /restricted/navigators/middlemen.xhtml in the conversation that your SESSION scoped MiddlemanDashboardBean is in! I'm pretty sure that's not what you wanted to do. The call to getConversationId is going to return you the conversationId of the MiddlemanDashboardBean not the MiddlemenGridBean that you are actually trying to navigate to.


                                  So yes, the navMan.redirectToModule line of code MUST exist on the bean that you are navigating too. Or I guess you possibly could change it to this (maybe).


                                  public void redirectToMiddleman() {
                                  
                                          NavigationUtils.setupNavigationAndForward(new AbstractSimpleForwardBackNavigation<MiddlemenGridBean>(this
                                                  .getConversationId())
                                                  {
                                  
                                              @Override
                                              public void navigateForward(MiddlemenGridBean controller) {
                                                  navMan.redirectToModule(
                                                          controller.getConversationId(),
                                                          "/restricted/navigators/middlemen.xhtml");
                                              }
                                          });
                                      }
                                  



                                  But ultimately I would just recommend you do what I said above and put a goto method on the MiddlemenGridBean and call that inside the navigateForward method. :)


                                  Let me know how that goes for you!

                                  • 14. Re: Ending Nested Natural Conversations
                                    Artem Shramko Newbie

                                    Hi Tim,


                                    Thank you for explanation. It was very useful. I redesigned my code according to your recommendation. But it didn’t solve the problem. Fortunately I’ve found the cause of cast exception.


                                    This happens because I use EJB beans. So I pass to the clazz argument stateful EJB bean. And when I remade the MiddlemenGridBean to ordinary seam component it worked!


                                    So, Tim, the main question for you: do you use EJB beans in your system? Is it possible to pass stateful beans for controller? I hope so because in our system it is necessary to use EJB beans.

                                    1 2 Previous Next