1 Reply Latest reply on Apr 24, 2009 7:39 PM by mtorres

    Exception Filter and Page Parameters

    mtorres

      Im trying to upgrade to Seam 2.1.x from 2.0.x version and I encountered a problem regarding Exception Filter. Page parameters that has expressions are not being propagated on redirect. The code sample below works on Seam 2.0.3.CR1. When the page by is accessed using


      http://localhost:8080/test/errorTest.seam?param=1


      and the Test button is clicked, the browser is redirected to


      http://localhost:8080/test/errorTest.seam?param=1&cid=<conversationId>


      I think it could be caused by Lifecycle.endRequest() that was recently added to ExceptionFilter before Exceptions.instance().handle(e) is called.


      errorTest.xhtml



      <h:form>          
      <a4j:commandButton value="Test" action="#{exceptionHandlerTestAction.onClick}"/>
      </h:form>




      pages.xhtml



      <page view-id="/errorTest.xhtml">          
           <description>Error Test</description>                    
           <param name="param" value="#{exceptionHandlerTestAction.param}" />          
      </page>




      <exception class="test.TestException">
           <redirect view-id="/errorTest.xhtml">               
           </redirect>
      </exception>



      TestException


      public class TestException extends RuntimeException{
      }



      ExceptionHandlerTestAction


      @Name("exceptionHandlerTestAction")
      public class ExceptionHandlerTestAction {
           private String param;
      
           /**
            * @return the param
            */
           public String getParam() {
                return param;
           }
      
           /**
            * @param param the param to set
            */
           public void setParam(String param) {
                this.param = param;
           }     
           
           public void onClick(){
                throw new TestException();
           }
      }



        • 1. Re: Exception Filter and Page Parameters
          mtorres

          The main reason we have the above use case is we're trying to have a centralized exception handling where our application level Exceptions will be caught, logged, and have the message recorded into JSF. The request is then redirected to the same page(we're not hardcoding errorTest.xhtml) for redisplay. Most of our pages works well with the exception handling pattern described above.


          Some pages though, maintains their state in page parameters, instead of session or conversation and thus we need the page parameters during redirect. Rendering within the same request cycle, would have conceptually worked for us but I did not know of a way to do that with the ExceptionsFilter.


          As a workaround for this problem, we tried to do the exception handling via a Java Interceptor approach. JSF facing components will mostly be annotated with ExceptionsAware, that triggers the ExceptionsAwareInterceptor. This interceptor encloses the invocation inside a try, catch block and delegates exception handling to the ExceptionsExt component, which is basically the built in Exceptions component that we overrode to remove some of the events publishing. The invocation is only wrapped inside a try/catch during the outermost interception within a call stack, thus the marker on the event context. This approach allowed us to not redirect during exceptions. In addition, I think, we can reuse the navigation rules in pages.xml to redirect/render to other views during exceptions, although we dont have such requirement yet so I still need to test it.


          I'd appreciate your insight into this, do you think this is a workeable solution?


          @Interceptor(stateless = true,
                    around = TransactionInterceptor.class)     
          public class ExceptionsAwareInterceptor extends AbstractInterceptor{
               private static final String EVENT_MARKER_NAME = ExceptionsAwareInterceptor.class.getName() + "_EventMarker"; 
               
               public Object aroundInvoke(InvocationContext ic) throws Exception {
                    Method method = ic.getMethod();
                    ExceptionsAware tag=null;
                    
                    if ( method.isAnnotationPresent(ExceptionsAware.class) ) 
                    {
                         tag = method.getAnnotation(ExceptionsAware.class);
                    }else if (ic.getTarget()!=null && ic.getTarget().getClass().isAnnotationPresent(ExceptionsAware.class)){
                         tag = ic.getTarget().getClass().getAnnotation(ExceptionsAware.class);
                    }          
                    if (tag.enabled()){
                         Boolean marker = (Boolean)Contexts.getEventContext().get(EVENT_MARKER_NAME);               
                         if (ObjectUtilsExt.eval(marker)){
                              //marked, let it pass through
                              return ic.proceed();
                         }else{
                              //not marked, first for the call stack, do handling here.
                              try{
                                   //mark
                                   Contexts.getEventContext().set(EVENT_MARKER_NAME,Boolean.TRUE);
                                   return ic.proceed();
                              }catch (Exception e) {
                                   ExceptionsExt.instance().handle(e);
                                   return null;
                              }finally{
                                   //remove the marker here.
                                   Contexts.getEventContext().remove(EVENT_MARKER_NAME);
                              }
                         }               
                    }else{
                         return ic.proceed();
                    }
               }
          
               public boolean isInterceptorEnabled() {
                    return getComponent().beanClassHasAnnotation(ExceptionsAware.class);
               }
          }