9 Replies Latest reply on Feb 23, 2009 5:24 AM by Hoan Nguyen

    Handling Errors when usign JPDL Pageflow.

    Joseph Nusairat Newbie

      Ok maybe i am just missing it here.


      I am trying to create a fairly simple page flow definition. And i am using page flows because it could eventually get more complicated based on a substance.


      But how do i handle error validations?


      <page name="loader-information" view-id="/loadCollection.xhtml">
        <transition name="next" to="substances-load">
          <action expression="#{loadConfigSvc.saveLoadConfig()}"/>
        </transition>
        <transition name="cancel" to="cancel"></transition>
      </page>
      



      So i have that saveLoadConfig can return a boolean false if it fails to validate and saves a message ot the facesmessages.


      Well ... when i run it it naturally goes to the next page.


      I know i can handle global exceptions for it in the pages code, but really i just want it to go back on itself and redisplay the same page. Can i use rules with the actions? Like we do in pages.xml?


        • 1. Re: Handling Errors when usign JPDL Pageflow.
          Pete Muir Master

          Once a transition has started in a pageflow, you're beyond the point returning to an old page IIRC. I guess you could file a feature request, and see if you can work out how to implement it...

          • 2. Re: Handling Errors when usign JPDL Pageflow.
            Alexander Schwartz Newbie

            I use an exception handler in (every) JPDL pageflow. Saves some information about the current (and previous) node.


              <exception-handler exception-class="javax.el.ELException">
                <action class="de.plusfinanzservice.common.bean.ExceptionAction" />
              </exception-handler>
            



            Later, an exception handler in pages.xml picks it up and repositions the JPDL pageflow.



              <exception class="java.lang.Exception">
                <redirect view-id="#{exceptionHandler.getViewId('/iss/error.jspx')}">
                  <message severity="error">
                    #{messages[exceptionHandler.getMessage(org.jboss.seam.handledException)]}
                  </message>
                </redirect>
              </exception>
            



            The code for the exception action.


            In a jpdl pageflow with seam we usually use EL expressions, therefore all exceptions we get here are wrapped in an EL Exception. First we unwrap them.


            We determine the source of the transition. If the source is a decision node (and not a page node), we will need to find the error handling page node. Our convention is to use a failure transition on every decision node to do exactly that. The data is saved on a new seam bean exception handler (see below).



            public class ExceptionAction implements ActionHandler {
              private static final long serialVersionUID = 1L;
              
              @Logger
              private Log log;
              
              /**
               * Handle jpdlException. For transitions we
               * @param executionContext context
               */
              public void execute(ExecutionContext executionContext) {
                Throwable e = executionContext.getException();
                if (e instanceof ELException && e.getCause() != null
                    && e.getCause() instanceof Exception) {
                  e = e.getCause();
                }
                
                /*
                 * if this is a transition, we memorize the source, throw an exception and
                 * reposition the flow from within seam.
                 */
                Node source = executionContext.getTransitionSource();
                if (source != null) {
                  /*
                   * if the source of the transition is a decision, handle the failure of
                   * the transition like the failure of the decision.
                   */
                  while (source instanceof Decision) {
                    Decision d = (Decision) source;
                    source = d.getLeavingTransition("failure").getTo();
                  }
                  ExceptionHandler handler = (ExceptionHandler) Component
                      .getInstance(ExceptionHandler.class);
                  handler.setNode(source.getName());
                  throw new JpdlException("caught in transition", e);
                } else {
                  BasicUtil.exceptionToMessage(log, e);
                }
              }
              
            }
            



            The exception handler in pages.xml; the relevant code is:


                  if (node != null) {
                    Pageflow.instance().reposition(node);
                    retVal = Pageflow.instance().getPage().getViewId();
                  } 
            



            The complete code follows. It does a bit more; it tries to fit all exceptions and tries place the use on the last view in the conversation (when there is no current pageflow).


            @Name("exceptionHandler")
            @Scope(ScopeType.EVENT)
            public class ExceptionHandler implements Serializable {
              private static final long serialVersionUID = 1L;
              
              private String node;
              
              /**
               * Get view id for error handling.
               * @param defaultErrorView default error view
               * @return view id to display error
               */
              @Begin(join = true)
              public String getViewId(String defaultErrorView) {
                String retVal;
                HttpServletRequest request = (HttpServletRequest) FacesContext
                    .getCurrentInstance().getExternalContext().getRequest();
                if (request.getMethod().equals("GET")) {
                  if (Conversation.instance().isNested()) {
                    retVal = FacesManager.instance().getParentConversationViewId();
                    Conversation.instance().endAndRedirect();
                  } else {
                    retVal = defaultErrorView;
                  }
                } else if (Conversation.instance().getViewId() != null) {
                  if (node != null) {
                    Pageflow.instance().reposition(node);
                    retVal = Pageflow.instance().getPage().getViewId();
                  } else {
                    retVal = Conversation.instance().getViewId();
                  }
                  Conversation.instance().setViewId(null);
                } else {
                  Conversation.instance().end();
                  retVal = defaultErrorView;
                }
                /*
                 * workaround for Bug http://jira.jboss.org/jira/browse/JBSEAM-2985 Please
                 * remove when this bug is closed.
                 */
                if (Contexts.getPageContext() == null) {
                  PLUSContextsHelper.setPageContext();
                }
                // this seams to be a bug in seam (2.0.2.GA), not raised yet
                Manager.instance().unlockConversation();
                return retVal;
              }
              
              /**
               * Delegator to BasicUtil.
               * @param e exception
               * @return string to be resolved from message bundle.
               */
              public String getMessage(Throwable e) {
                String msg = BasicUtil.getMessage(e);
                return msg;
              }
              
              public String getNode() {
                return node;
              }
              
              public void setNode(String node) {
                this.node = node;
              }
            }
            
            



            Pete, what do you think? Is that ready for a feature request?

            • 3. Re: Handling Errors when usign JPDL Pageflow.
              Pete Muir Master

              File the feature request, attach the code as a patch.


              It's too late for me to think clearly right now ;-)

              • 5. Re: Handling Errors when usign JPDL Pageflow.
                Alexander Schwartz Newbie

                If you are interested in this feature, please vote for it in JIRA!

                • 6. Re: Handling Errors when usign JPDL Pageflow.
                  Pete Muir Master

                  Please use your real name in JIRA.

                  • 7. Re: Handling Errors when usign JPDL Pageflow.
                    Hoan Nguyen Newbie

                    I use the above work around in my project with SEAM 2.1.0.A1. It works fine.
                    However it stops working in SEAM 2.1.1.GA. I reckon that's because the recent change in org.jboss.seam.bpm.SeamExpressionEvaluator.


                    In SEAM 2.1.0.A1


                    try
                    {
                      initMethodExpression();
                    }
                    catch (javax.el.ELException e)
                    {
                      exceptions.add(e);
                    }
                    if (me != null)
                    {
                      try
                      {
                        return me.invoke(createELContext(resolver, mapper), new Object[0]);
                      }
                      catch (MethodNotFoundException e)
                      {
                        exceptions.add(e);
                      }
                    }
                    


                    And in SEAM 2.1.1.GA



                    try
                    {
                        initMethodExpression(); 
                        if (me != null)
                        {
                            try
                            {
                                return me.invoke(createELContext(resolver, mapper), new Object[0]);
                            }
                            catch (MethodNotFoundException e)
                            {
                                exceptions.add(e);
                            }
                        }
                    }
                    catch (javax.el.ELException e) 
                    {
                        exceptions.add(e);
                    }
                    



                    The method invocation has been brought into a try/catch for ELException. So a custom exception thrown by the method can never be seen outside the method.
                    That prevents my action handler knowing about the root cause of the exception.


                    Can any one give me a hint to overcome this?

                    • 8. Re: Handling Errors when usign JPDL Pageflow.
                      Hoan Nguyen Newbie

                      Sorry for a typo.



                      So a custom exception thrown by the method can never be seen outside the method

                      should be read



                      So a custom exception thrown by the method can never be seen outside Expression.evaluate() method