8 Replies Latest reply on Dec 17, 2013 9:50 PM by kcbabo

    ExchangeInterceptor As Global Exception Handling

    pzl_mz

      I would like to use the ExchangeInterceptor for global exception handling. The after() method checks the content of the Exchange for HandlerException. The problem is that the after() method is called twice even though only one target (PROVIDER or CONSUMER) is set in getTargets(). How can I use the ExchangeInterceptor to handle an Exception only once?

        • 1. Re: ExchangeInterceptor As Global Exception Handling
          kcbabo

          Can your provide a small app+test which reproduces this behavior?  Looking at how this is handled:

          https://github.com/jboss-switchyard/core/blob/master/bus/camel/src/main/java/org/switchyard/bus/camel/CamelExchangeBusRouteBuilder.java#L137

           

          I can see a window where the ExchangeInterceptor can be called twice on the fault path if an exception is thrown after the PROVIDER after() method has been called.  It's not immediately apparent to me how this can happen when an Exception is reported twice though.  In that scenario, the first call to after() would be a normal message and the second call would be a fault/exception.

           

          For now, you can make sure you only process once by adding a context property to the exchange in your ExchangeInterceptor that indicates the exception was handled.  Check for that property in your interceptor code to guard against processing twice.  The fix in the runtime to close the window I mentioned above will look similar.

          • 2. Re: ExchangeInterceptor As Global Exception Handling
            kcbabo
            • 3. Re: ExchangeInterceptor As Global Exception Handling
              pzl_mz

              I posted an example which should illustrate the behavior. But I don't think that my problem is related to the bug. I just wondered why CamelService gets intercepted in after() even though an exception occurred before in TestService and the the Invoker was set to inOnly.

               

              I tried your suggestion but without success:

              public void after(String target, Exchange exchange) throws HandlerException {

                      Object content = exchange.getMessage().getContent();

                      if (content instanceof HandlerException) {

                          if (exchange.getContext().getProperty("error_processed", Scope.EXCHANGE) == null) {

                              exchange.getContext().setProperty("error_processed", true, Scope.EXCHANGE);

                              // should happen only once, but still happens twice, why?

                          }

                      }

              }

              • 4. Re: ExchangeInterceptor As Global Exception Handling
                igarashitm

                I'm sorry for taking longer to respond, I just walked through the scenario your application shows. This is correct behavior since this application has 2 SwitchYard exchanges in its entire route. One is for invoking "CamelService" and the other is for invoking "TestService" from the Camel route. The ExchangeInterceptor is being invoked for each SwitchYard exchange. It also happens in the fault route since the SwitchYard processor chain triggers the interceptors on the catch path.

                 

                hth,

                Tomo

                • 5. Re: ExchangeInterceptor As Global Exception Handling
                  pzl_mz

                  Thanks for your feedback. I understand why the interceptor gets called twice but why does the workaround in reply 3) not work?

                  • 6. Re: ExchangeInterceptor As Global Exception Handling
                    igarashitm

                    That workaround doesn't work for multiple exchanges - since the context property is exchange scoped, it doesn't survive over the exchange.

                     

                    2 workarounds come to my mind:

                     

                    1. Cache the root cause of HandlerException in the ThreadLocal field of the ExchangeInterceptor and handle only if the incoming exception is different from cached one. Note that you would need to peel the HandlerException and extract the root cause so you can make sure those are the same Exception.
                    2. If the application always have a single front service and it invokes other services, you may be able to limit the situation of ExchangeInterceptor being fired to that front service with checking provider/consumer name. Those name can be retrieved by exchange.getProvider().getName().

                     

                    hth

                    Tomo

                    • 7. Re: ExchangeInterceptor As Global Exception Handling
                      pzl_mz

                      Thanks for your answer!

                       

                      Workaround 2) in my case does not work since there's not enough information available in the exchange properties of the front service. So the workaround 1) and 2) combined might work.

                      Workaround 1) is used to set the HandlerException in ThreadLocal

                      Workaround 2) is used to remove HandlerException from ThreadLocal

                       

                      There's still the question why neither exchange.setProperty Scope.EXCHANGE nor exchange.setProperty Scope.MESSAGE work for exchange pattern inOnly in case of an exception.

                      • 8. Re: ExchangeInterceptor As Global Exception Handling
                        kcbabo

                        A property scoped at EXCHANGE is available for the life of that exchange, but it does not propagate across multiple exchanges.  Going off Tomo's evaluation earlier, it sounds like there are actually two exchanges created (e1, e2) when the app is invoked.  If your interceptor sets the property in one exchange (e2) at EXCHANGE scope when the exception is encountered, then it will be present on that exchange for it's lifetime.  It will not, however, be present on the other exchange (e1) when your interceptor fires on that.