1 Reply Latest reply on Mar 6, 2008 12:41 PM by gonzalad

    pages.xml virtual pages usage

    gonzalad Apprentice


      Here's quite a long mail (sorry) !


      Request for enhancement.


      In our application, we have links calling some 'virtual' pages. Those virtual pages execute some actions before rendering the real pages.

      Those 'virtual' pages enables us to do some pre-processing before rendering the real page (i.e. do some conversation cleanup stuff or things related).

      Virtual and real pages are all declared in the pages.xml.
      Our problem is that 'action', 'restrict', .. elements declared for the real pages are not
      executed when coming from the 'real' page.

      I suppose the 'virtual' pages mecanism could be interesting in other situations.

      Is it viable to ask for an enhancement on this subject (executing 'action', 'restrict', 'login-required', 'conversation-required', 'page', ...on pages being rendered) ?

      Please note, the notion of virtual pages has already been discussed before (lookat : http://www.jboss.com/index.html?module=bb&op=viewtopic&t=88388)

      I define a virtual page in pages.xml, e.g. a page that doesn't actually exist, but where the action listener would decide the page to be rendered, e.g:

      <page view-id="/virtual.xhtml" action="#{switchboard.chooseAPage}" />

      virtual.xhtml being non-existant, and switchboard.chooseAPage being a stateless session bean method that returns the real view-id to display.

      The user would possibly enter the URL ending in /myapp/virtual.elvis, and #{switchboard.chooseAPage} might return a value such as eminem.jsp or britney.xhtml


      Our menu actions are linked to the 'virtual pages' using the prefix '/global'.

      For instance for our 'Charge / Search' link (Charge is an entity), we have the following jsf page fragment :

      <rich:menuItem rendered="#{s:hasRole('EDUWSPH CHAINT')}">
        <s:link id="rechercheChargeH" view="/global/charge/recherche_charge.jspx" value="Search Charge..."/>

      In our page.xml, we declare :

      <page view-id="/global/*">
        <action execute="#{conversationHelper.endAllConversationsAndBeginLongRunning}"/>
      <page view-id="/views/*" login-required="true"/>
      <page view-id="/global/views/charge/*">
        <action execute="#{menuAction.setMenuCourant('charge')}"/>
      <page view-id="/global/views/charge/recherche_charge.jspx">
          <render view-id="/views/charge/recherche_charge.jspx"/>
      <page view-id="/views/charge/recherche_charge.jspx">
        <restrict>#{s:hasRole('EDUWSPH CHAINT')}</restrict>  
        <navigation from-action="#{rechercherChargeClienteleAction.reset}">
          <render view-id="/views/charge/recherche_charge.jspx"/>
        <navigation from-action="#{rechercherChargeClienteleAction.view}">
          <begin-conversation nested="true"/>
          <render view-id="/views/charge/detail_charge.jspx"/>

      '/global/views/charge/recherche_charge.jspx' is our virtual page.

      '/views/charge/recherche_charge.jspx' is our real page.

      So, looking at pages.xml, I would suppose when we call /global/charge/recherche_charge.jspx, that :

      1. action #{menuAction.setMenuCourant('charge')} is called (/global/views/charge/*).

      2. action #{conversationHelper.endAllConversationsAndBeginLongRunning} is called (/global/*)

      3. restrict #{s:hasRole('EDUWSPH CHAINT')} is called (/views/charge/recherche_charge.jspx)

      4. login-required=true is executed (views/*)

      5. we render page /views/charge/recherche_charge.jspx

      But : 3 and 4 is not called.
      Also, if we add  action  elements on  page view-id=/views/charge/recherche_charge.jspx , those  action  are not called.

      Why are we doing this ?
      Because when we come from a menu link (/global/views/charge/recherche_ charge.jspx),
      we want to clear all conversations whereas when we postback the page /views/charge/recherche_charge.jspx we don't want conversations to be cleared !
      Putting all the cleanning stuff in one place only ('page view-id=/global/*') appears also to be quite clean.

      Alternate solutions

      if not, has something a better idea for handling conversation cleanup on menu links ?

      1. just adding an action element on menu s:link ?

      <s:link id="rechercheChargeH" view="/global/charge/recherche\_charge.jspx" value="Search Charge..." action="#{conversationHelper.endAllConversationsAndBeginLongRunning}"/> 

      cons : this scatters action code across all the menus, and we are limited to calling only one action.
      2. use 'redirect' on /global virtual pages and not 'render' (smells quite bad IMO).

      Thanks for your help.

        • 1. Re: pages.xml virtual pages usage
          gonzalad Apprentice

          Sorry, this point was already adressed in Seam reference documentation http://docs.jboss.com/seam/2.0.1.GA/reference/en/html/events.html#d0e3859

          The page action method can return a JSF outcome. If the outcome is non-null, Seam will use the defined navigation rules to navigate to a view.

          Instead of using render in my /global links, I just call an action returning the next viewId :

          <page view-id="/global/charge/recherche_charge.jspx"> 
              <action execute="#{navigationHelper.go('/views/charge/recherche_charge.jspx')"/>

          This just navigates to /views/charge/recherche_charge.jspx page executing all actions, restrict elements, and so in declared in Seam pages.xml.

          With navigationHelper being a noop class :

          public class NavigationHelper extends HashMap<String, String>{
              private static final Log logger = LogFactory.getLog(NavigationHelper.class);
              public String go(String aViewId)  {
                  if (logger.isDebugEnabled()) {
                      logger.debug("go :- viewId="+aViewId);
                  String lViewId = getViewId();
                  if (StringUtils.hasLength(lViewId)) {
          en cours (sinon boucle infinie)
                      if (lViewId.equals(aViewId)) {
                          String lMessage = "Le paramètre viewId doit être différent de la page courante (risque de boucle infinie) : "+aViewId;
                          throw new IllegalStateException(lMessage);
                  return aViewId;
              private String getViewId () {
                  UIViewRoot lViewRoot = getViewRoot();
                  return (lViewRoot == null) ? null : lViewRoot.getViewId();
              private UIViewRoot getViewRoot () {
                  return FacesContext.getCurrentInstance().getViewRoot();        

          We could also have done without the navigationHelper :

          <page view-id="/global/charge/recherche_charge.jspx"> 
              <action execute="/views/charge/recherche_charge"/>