11 Replies Latest reply on Jan 22, 2007 1:01 AM by mnrz

    Request for feedback

    gavin.king

      So I finally got fed up with the limitations of JSF navigation rules and I went ahead and implemented a stateless navigation system for Seam. There were four main reasons why I wanted this:

      (1) to centralize orchestration in one place, instead of having it split between pages.xml and faces-config.xml

      (2) to be able to specify request parameters when redirecting

      (3) to be able to specify conversation begin/end according to an outcome

      (4) to be able to have a value binding which evaluates the outcome, in addition to the action method binding - this lets me get rid of the ugly outcomes returned by action methods, and reduces the coupling of the model to the view.

      (5) to support various "result types", other than a JSF view (eg. a file download, a http error, etc).


      What I have today can do what you can do with JSF navigation rules, for example:

      <page view-id="/getDocument.jsp">
       <navigation>
       <outcome value="success">
       <redirect view-id="/editDocument.jsp"/>
       </outcome>
       <outcome value="not-found">
       <redirect view-id="/404.jsp"/>
       </outcome>
       </navigation>
      </page>


      Is equivalent to:

      <navigation-rule>
       <from-view-id>/getDocument.jsp</from-view-id>
       <navigation-case>
       <from-outcome>success</from-outcome>
       <to-view-id>/editDocument.jsp"</to-view-id>
       <redirect/>
       </navigation-case>
       <navigation-case>
       <from-outcome>not-found</from-outcome>
       <to-view-id>/404.jsp"</to-view-id>
       <redirect/>
       </navigation-case>
      </navigation-rule>


      And:

      <page view-id="*">
       <navigation action="#{documentHome.get}">
       <outcome value="success">
       <render view-id="/editDocument.jsp"/>
       </outcome>
       </navigation>
      </page>


      Is equivalent to:

      <navigation-rule>
       <navigation-case>
       <from-action>#{documentHome.get}</from-action>
       <from-outcome>success</from-outcome>
       <to-view-id>/editDocument.jsp"</to-view-id>
       </navigation-case>
      </navigation-rule>



      But it can also do this:

      <page view-id="/getDocument.jsp">
       <navigation outcome="#{documentHome.document!=null}">
       <outcome value="true">
       <redirect view-id="/editDocument.jsp"/>
       </outcome>
       <outcome value="false">
       <redirect view-id="/404.jsp"/>
       </outcome>
       </navigation>
      </page>


      This lets you write a DocumentHome.get() method with a void return type! This is much more transparent to the model.

      You can also do this:

      <page view-id="/editDocument.jsp">
       <navigation action="#{documentHome.update}">
       <null-outcome>
       <redirect view-id="/document.jsp">
       <param name="documentId" value="#{documentHome.document.id}"/>
       </redirect>
       </null-outcome>
       </navigation>
      </page>


      (Yay!)

      Soon I'll also add <begin-conversation/> and <end-conversation/> to the case handling. Eventually I'll also add support for other things in addition to render and redirect.

      OK, so, what I need feedback on is the actual XML format. What I have there today is a little less verbose than the JSF XML, but has one higher level of indentation (which makes it more complex). The reason for this is that I want to allow different actions to specify different outcome expressions, and different lists of cases, eg:


      <page view-id="/getDocument.jsp">
      
       <navigation action="#{documentHome.get}"
       outcome="#{documentHome.document!=null}">
       <outcome value="true">
       <redirect view-id="/editDocument.jsp">
       <param name="documentId" value="#{documentHome.document.id}"/>
       </redirect>
       </outcome>
       <outcome value="false">
       <redirect view-id="/404.jsp"/>
       </outcome>
       </navigation>
      
       <navigation action="#{logout.logout}">
       <null-outcome>
       <redirect view-id="/login.jsp"/>
       <null-outcome>
       </navigation>
      
      </page>



      So the question is should I go for something more similar to the JSF XML (one less level of nesting):

      <page view-id="/getDocument.jsp">
      
       <navigation action="#{documentHome.get}">
       <outcome expression="#{documentHome.document!=null}"
       value="true"/>
       <redirect view-id="/editDocument.jsp">
       <param name="documentId" value="#{documentHome.document.id}"/>
       </redirect>
       </navigation>
      
       <navigation action="#{documentHome.get}">
       <outcome expression="#{documentHome.document==null}"
       value="true"/>
       <redirect view-id="/404.jsp"/>
       </navigation>
      
       <navigation action="#{logout.logout}">
       <redirect view-id="/login.jsp"/>
       </navigation>
      
      </page>



      Or does it make it easier to learn if we stay even more like standard JSF:


      <page view-id="/getDocument.jsp">
      
       <navigation>
       <from-action>#{documentHome.get}</from-action>
       <outcome-expression>#{documentHome.document!=null}</outcome-expression>
       <from-outcome>true</from-outcome>
       <redirect>
       <to-view-id>/editDocument.jsp</to-view-id>
       <param name="documentId" value="#{documentHome.document.id}"/>
       </redirect>
       </navigation>
      
       <navigation>
       <from-action>#{documentHome.get}</from-action>
       <outcome-expression>#{documentHome.document==null}</outcome-expression>
       <from-outcome>true</from-outcome>
       <redirect>
       <to-view-id>/404.jsp"</to-view-id>
       </redirect>
       </navigation>
      
       <navigation action="#{logout.logout}">
       <redirect>
       <to-view-id>/login.jsp"</to-view-id>
       </navigation>
      
      </page>


        • 1. Re: Request for feedback
          gavin.king

          Oh and to see what I'm trying to get at, take a look at the updated booking example in CVS.

          • 2. Re: Request for feedback
            spambob

            I prefer it the new way - it's less verbose without getting cryptic & gives me more possibilities! Nice one :)

            Regarding learning / ease of use IMHO the best way is to provide *up to date* dtds or xsds so one can use content assist in an IDE. This together with the docs / examples would fulfill my needs.

            Perhaps you people could make a page where all dtds & xsds are listed and try to get them up to date?

            • 3. Re: Request for feedback
              pmuir

              +1 for the first option. It may be more complex but it's also more intuitive IMO.

              I'm not a great fan of the JSF format. If you are coming from JSF you can stick with JSF style navigation to start with (this is as-well-as right?) and if you are not coming from a JSF background then I don't think it will make Seam's learning curve noticeably steeper.

              • 4. Re: Request for feedback
                gavin.king

                Yes, we will always support JSF navigation in addition to this (there is zero cost to doing so).

                • 5. Re: Request for feedback

                  I don't know how other people's taste in XML goes, but the following reads really well to me:


                  <page view-id="/getDocument.jsp">
                   <on action="#{documentHome.get}">
                   <redirect to-view-id="/editDocument.jsp" when="#{documentHome.document!=null}">
                   <param name="documentId" value="#{documentHome.document.id}"/>
                   </redirect>
                  
                   <redirect to-view-id="/404.jsp" when="#{documentHome.document==null}" />
                   </on>
                  
                  
                   <on action="#{logout.logout}">
                   <redirect to-view-id="/login.jsp" />
                   </on>
                  </page>



                  Or, with explicit outcome declaration:

                  <page view-id="/getDocument.jsp">
                   <on action="#{documentHome.get}">
                   <outcome value="#{documentHome.document!=null}" />
                  
                   <redirect to-view-id="/editDocument.jsp" when-outcome="true">
                   <param name="documentId" value="#{documentHome.document.id}"/>
                   </redirect>
                  
                   <redirect to-view-id="/404.jsp" when-outcome="false" />
                   </on>
                  
                   <on action="#{logout.logout}">
                   <redirect view-id="/login.jsp" when-null-outcome="true" />
                   </on>
                  </on>


                  Although, I think it might be even better if outcome were just a variable:

                  <redirect to-view-id="/editDocument.jsp" when="#{outcome==true}">
                  ...
                  <redirect to-view-id="/404.jsp" when="#{outcome==null}">
                  






                  • 6. Re: Request for feedback
                    sherkan777

                    Sorry for little off topic...but I must say that!

                    When I see what U guys try to do, I think U're trying to make some huge universal framework...but this is cool!

                    EJB3.0, Ajax4JSF, IceFaces, Hibernate...Trinidad, Seam Realm?, and today I readed on roadmap U want to integrate Spring with Seam...
                    and now U want 2 change navigation style from JSF.

                    Intresting, how Seam will look like in 2010 year? :P

                    • 7. Re: Request for feedback

                      We believe that by 2010 that the JVM will be running on HAL, thanks in large part due to the open sourcing of Java.

                      • 8. Re: Request for feedback
                        gavin.king

                        OK, so after much to and fro, I settled on the following:


                        <page view-id="/edit.jsp">
                        
                         <action execute="#{home.load}"
                         if="#{not conversation.longRunning}"/>
                         <action execute="#{home.invalid}"
                         if="#{validation.failed}"/>
                        
                         <navigation from-action="#{home.load}">
                         <rule if="#{home.instance==null}">
                         <render view-id="/404.jsp"/>
                         </rule>
                         <rule if="#{home.instance.readOnly}">
                         <render view-id="/view.jsp">
                         <param name="id" value="#{home.instance.id}"/>
                         </render>
                         </rule>
                         <rule if="#{home.instance!=null}">
                         <begin-conversation/>
                         </rule>
                         </navigation>
                        
                         <navigation from-action="#{home.update}">
                         <redirect view-id="#{/view.jsp}">
                         <end-conversation/>
                         <param name="id" value="#{home.instance.id}"/>
                         </redirect>
                         </navigation>
                        
                        </page>


                        Or, alternatively:

                        <page view-id="/edit.jsp">
                        
                         <action execute="#{home.load}"
                         if="#{not conversation.longRunning}"/>
                         <action execute="#{home.invalid}"
                         if="#{validation.failed}"/>
                        
                         <navigation from-action="#{home.load}">
                         <rule if-outcome="not-found">
                         <render view-id="/404.jsp"/>
                         </rule>
                         <rule if-outcome="read-only">
                         <render view-id="/view.jsp">
                         <param name="id" value="#{home.instance.id}"/>
                         </render>
                         </rule>
                         <rule if-outcome="found">
                         <begin-conversation/>
                         </rule>
                         </navigation>
                        
                         <navigation from-action="#{home.update}">
                         <redirect view-id="#{/view.jsp}">
                         <end-conversation/>
                         <param name="id" value="#{home.instance.id}"/>
                         </redirect>
                         </navigation>
                        
                        </page>


                        Or even:

                        <page view-id="/edit.jsp">
                        
                         <action execute="#{home.load}"
                         if="#{not conversation.longRunning}"/>
                         <action execute="#{home.invalid}"
                         if="#{validation.failed}"/>
                        
                         <navigation from-action="#{home.load}"
                         evaluate="#{home.status}">
                         <rule if-outcome="NOT_FOUND">
                         <render view-id="/404.jsp"/>
                         </rule>
                         <rule if-outcome="READ_ONLY">
                         <render view-id="/view.jsp">
                         <param name="id" value="#{home.instance.id}"/>
                         </render>
                         </rule>
                         <rule if-outcome="FOUND">
                         <begin-conversation/>
                         </rule>
                         </navigation>
                        
                         <navigation from-action="#{home.update}">
                         <redirect view-id="#{/view.jsp}">
                         <end-conversation/>
                         <param name="id" value="#{home.instance.id}"/>
                         </redirect>
                         </navigation>
                        
                        </page>



                        • 9. Re: Request for feedback
                          app4you

                          I would have to say that the more similar to the JSF XML + the additional nested tag, the better since people are lazy, like me. Also, while you at it, could you make the 404 default to the one that is set in the web.xml file? And one more thing, a bit off the topic, the long running conversation url and the servlet filter don't work well together in seam versions, lot of hacks for me on this.

                          Thanks the hard work.

                          • 10. Re: Request for feedback

                            Hi,

                            I'm just starting using pages.xml (first to declare page parameters for use with EntityHome, now because it looks simpler to define navigation rules). This "request for feedback post" inspired me new ideas and stimulated me to go deeper into pages.xml.

                            My questions relevant to this topic are:

                            1) For the simplest case the developer wants to define outcome mappings, is it possible to write this less verbose?

                            <page view-id="/getDocument.jsp">
                             <navigation outcome="success" redirect="/editDocument.jsp"/>
                             <navigation outcome="not-found" redirect="/404.jsp"/>
                            </page>
                            



                            2) in the booking example, Hotel selection is made via action listener with hotel as parameter

                            #{booking.selectHotel(hotel)}


                            selectHotel returns an outcome that resolves next view, that we know it is "hotel" that redirects to hotel.xhtml. It would be nice if it could be placed only in pages.xml (no returning outcome in the action listener):

                            <page view-id="/hotels.xhtml">
                             <navigation action="#{booking.selectHotel(hotel)}">
                             <redirect view-id="/hotel.xhtml" />
                             </navigation>
                            </page>
                            


                            in other words, navigation action will be able to resolve action listener with parameters?

                            Thanx,
                            Fábio.

                            • 11. Re: Request for feedback
                              mnrz

                              Hi
                              how about define a specific navigation rule to support wizard pages?
                              (I mean pages with Next and Prevoius links)