Announcement Announcement Module
Collapse
No announcement yet.
Confusion with int:exception-type-router (resolution channels) Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Confusion with int:exception-type-router (resolution channels)

    Hello Guys

    I am trying to solve the problem of configure input/output channels when I use a int:service-activator together with a int:exception-type-router.

    Before to follow just to recall in the section 5.1.2. Routing and Error handling appear the follow

    Spring Integration also provides a special type-based router called
    ErrorMessageExceptionTypeRouter for routing Error Messages
    (Messages whose payload is a Throwable instance).

    ErrorMessageExceptionTypeRouter is very similar to the PayloadTypeRouter.
    In fact they are almost identical.


    The only difference is that while PayloadTypeRouter navigates the instance hierarchy of a payload instance (e.g., payload.getClass().getSuperclass()) to find the most specific type/channel mappings,the ErrorMessageExceptionTypeRouter navigates the hierarchy of 'exception causes' (e.g., payload.getCause()) to find the most specific Throwable type/channel mappings.
    I am assuming that any Exception would be generated or arise from a Service Activator and/or Transformer, perhaps from other endpoint, the point is handle the Exception thrown.

    I going to work with an ErrorMessage since it let has the payload explicitly of type Throwable

    The follow code work fine (match colors for a better understanding)

    Code:
    	
    <int:exception-type-router input-channel="myErrorChannel" >
    	<int:mapping exception-type="com.manuel.jordan.exception.MyErrorMessageException" 
                                channel="myErrorMessageException"/>
    	<int:mapping exception-type="java.lang.IllegalArgumentException" 
                                channel="illegalChannel"/>
    	<int:mapping exception-type="java.lang.NullPointerException" 
                                channel="npeChannel"/>			
    </int:exception-type-router>
    
    <int:service-activator id="errorServiceActivator01"
    		       input-channel="inicio"
    		       output-channel="myErrorChannel"
    		       method="serviceActivator03"
    		       ref="errorServiceActivator"						   					   
    							/>
    	
    <int:service-activator id="objectServiceActivator01"
    		       input-channel="myErrorMessageException"
    		       output-channel="final"
    		       method="serviceActivator03"
    		       ref="objectServiceActivator"						   
    							/>
    I did realize the input-channel attribute of the int:exception-type-router element is mandatory, therefore must exists an exclusive message channel only to send and receive ErrorMessage exclusively for this type of Router

    Below the code of the method serviceActivator03 which belong to errorServiceActivator

    Code:
    public Message<?> serviceActivator03(Message<?> message)throws MyGlobalException{
    		
    	logger.info("serviceActivator03 message: {}", message);
    		
    	boolean control = true;
    		
    	if(control){
    		
    		logger.info("66666666");
    			
    		MyErrorMessageException myErrorMessageException = 
                            new  MyErrorMessageException("MyErrorMessageException");
    
    		ErrorMessage errorMessage = new ErrorMessage(myErrorMessageException);
    									
    		return errorMessage;
    	}
    	else
    		return message;
    		
    	}
    }
    The code work fine, I have some observations, doubts and confusion, please give me a hand

    1) the Service Activator has explicitly configured the output channel to myErrorChannel, therefore practically it call the Router (it is little illogic, read (2) ).

    2) if my boolean variable control is changed to false, therefore the Exception never is thrown, the application work fine but the original Message sent it is lost, I think due the payload is not a Throwable type (I sent originally a String like payload).

    3) To solve (1) and (2). How I could do the follow.

    3.1) Has any amount of Service Activators where the output channel be free (I mean, not explicitly configure each output channel to some int:exception-type-router)

    3.2) from 3.1 if only I have detected some error, classic try/catch, I want create the ErrorMessage and it should go explicitly to some int:exception-type-router through some configuration

    Thanks in advanced

  • #2
    Manuel, when in doubt look at the code...

    Code:
    public class ErrorMessageExceptionTypeRouter extends AbstractMessageRouter {
    
    	@Override
    	protected List<Object> getChannelIdentifiers(Message<?> message) {
    		String channelIdentifier = null;
    		Object payload = message.getPayload();
    		if (payload != null && (payload instanceof Throwable) && this.channelIdentifierMap != null) {
    			Throwable mostSpecificCause = (Throwable) payload;
    			while (mostSpecificCause != null) {
    				String mappedChannelIdentifier = this.channelIdentifierMap.get(mostSpecificCause.getClass().getName());
    				if (mappedChannelIdentifier != null) {
    					channelIdentifier = mappedChannelIdentifier;
    				}
    				mostSpecificCause = mostSpecificCause.getCause();
    			}
    		}
    		return Collections.singletonList((Object) channelIdentifier);
    	}
    
    }
    The exception type router is really intended for use on an error flow (downstream of an error channel, e.g. on a gateway).

    As you have observed, and can see in the code, if the payload is not a Throwable, the message is not routed anywhere.

    As you can also see, we drill down to the most specific cause.

    If your inbound endpoint has an error channel, and your service, invoked by a service activator throws, say, a NullPointerException, Spring Integration will create an error message with a payload of MessagingException. The MessagingException will have two attributes - the failedMessage, and the cause (your NullPointerException). This ErrorMessage will then be sent to the error channel and you can route based on the original exception, in this case NPE.

    HTH

    Comment


    • #3
      Hello Gary

      Thanks again for your time and reply, I really appreciate, if we following in this way, soon I owe you a truck of beer

      when in doubt look at the code...
      I forgot that, yes, you are right, but I think more valuable and how complement the English explanation of the developers

      From your code, I could conclude the follow ideas, please correct me, If I am wrong

      Code:
      protected List<Object> 
      return Collections.singletonList((Object) channelIdentifier);
      for this case it should be return a List only with a unique element, right?, it could be a List with more items in case the we work with RecipientListRouter

      Practically the method implementation checks that exists a payload and it must be of type throwable and the the Map/*List* of channels to be evaluated must exists (channelIdentifierMap).

      Within the while cycle, it goes around the hierarchy of exceptions until find one that exists like a key from the channelIdentifierMap to assign the output channel.

      The exception type router is really intended for use on an error flow (downstream of an error channel, e.g. on a gateway).
      Yes, I concluded the same, or in case if we have some endpoint and we dont want handle the exception thrown and caught (try/catch), instead, we forward this Exception within an ErrorMessage to the unique output channel with the prior knowledge that this Exception should arrive to this special kind of router, I am correct?

      Here arise again a similar question I wrote in the first post , if my previous paragraph or idea is correct I have the follow ideas and doubts

      1) We must very sure and accept the fact that we are not going to handle the Exception within the Service Activator, therefore once caught the Exception we must create an ErrorMessage and forward to the unique output channel of this Service Activator

      2) From my first post, if we have the follow situation where if the Service Activator could has the risk the Service called could throw or could not throw an Exception (duplicate PK in the DB) we are in problems about the desicion of the output channel, due the follow two cases

      2.1) If I handle the caught Exception therefore the output channel could be "nextEndPointChannel"

      2.2) If I do not handle the caught Exception therefore the output channel could be "errorChannelToSomeExceptionTypeRouterChannel"

      Practically from 2.1 and 2.2 the output channel is exclusive

      I hope you see now my point, now here arise a new question (just an idea):

      Why not each Service Activator (perhaps other endpoints) should has two output channels, one or the classic to forward to the next endpoint and a secundary dedicated to only send ErrorMessages (only if we want it)

      Recall we have by default the errorChannel, or Spring Integration could create a new MessageChannel implementation but being DataType only for Throwable objects, thus we have a strict control.

      Therefore we can have an ExceptionTypeRouter with its input channel only restricted for Throwable objects (using DataType), without care or worry about from which endpoint was sent it the ErrorMessage.

      Therefore if we have many endpoints with high risk to throw the same exception and we want a central to control and forward this Exception to an unique place to be handle, I mean one place to handle a type of Exception, we can use the ExceptionTypeRouter

      Let me know your thoughts!

      If your inbound endpoint has an error channel, and your service, invoked by a service activator throws, say, a NullPointerException
      You see, you are assuming my endpoint(Service Activator) has an error channel how its output, what happen if none Exception never is thrown?, I just only forward in peace to the next endpoint

      Spring Integration will create an error message with a payload of MessagingException.
      Yes, it is the base class about the hierarchy of Exceptions on Spring Integration, I used to receive its sub classes like MessageHandlingException, but it happen automatically breaking by complete the flow integration process

      The MessagingException will have two attributes - the failedMessage, and the cause (your NullPointerException). This ErrorMessage will then be sent to the error channel and you can route based on the original exception, in this case NPE.
      Totally agree


      My Best Regards
      Last edited by dr_pompeii; Aug 20th, 2011, 01:41 PM.

      Comment


      • #4
        for this case it should be return a List only with a unique element, right?
        Correct because a Map can only have one instance of the exception type as a key. However, you can route to multiple channels by using...

        Code:
        channel="channel1,channel2"
        You could also put a RLR downstream of a single channel.

        1) We must very sure and accept the fact that we are not going to handle the Exception within the Service Activator, therefore once caught the Exception we must create an ErrorMessage and forward to the unique output channel of this Service Activator
        We generally don't recommend using SI APIs directly in user code - tying your application to the framework. There is no reason your service needs to know about ErrorMessages; simply allow the exception to propagate and the framework will take care of creating the ErrorMessage.

        Why not each Service Activator (perhaps other endpoints) should has two output channels,
        There is no need for this; the exception is thrown to the closest element (usually a gateway or inbound adapter) that has an error channel and the ErrorMessage is sent there. Think of the error channel as like a java try{} block further down the stack.

        You see, you are assuming my endpoint(Service Activator) has an error channel ...
        No I am not. In my text (that you quoted), I said inbound endpoint. This is usually a gateway of some kind, or an inbound adapter; we only have error-channel configuration there. If you want to catch an exception mid-flow, you can add a service-activator that invokes a gateway (that has an error-channel).

        I hope that clears everything up.

        Comment


        • #5
          Hello Gary

          Thanks a lot for your time and reply, I really appreciate

          Correct because a Map can only have one instance of the exception type as a key.
          I referred to this code

          Code:
          protected List<Object> 
          return Collections.singletonList
          Therefore Map vs List

          Code:
          channel="channel1,channel2"
          You could also put a RLR downstream of a single channel.
          Interesting the bold part, when is wise use such approach?.

          I just did a search with Firefox with this pattern channel=" and I checked each match throughout SI documentation and none snippet code about using channels with comma appear

          We generally don't recommend using SI APIs directly in user code - tying your application to the framework. There is no reason your service needs to know about ErrorMessages;
          Totally agree, but I am creating the ErrorMessage in my Service Activator not in the Service Object, from my Service Activator I call some method offered by the Service Object, therefore if this last thrown an Exception I caught in my Service Activator

          Therefore I am keeping the "same" approach like does a Controller, in this case the endpoint

          simply allow the exception to propagate and the framework will take care of creating the ErrorMessage.
          Yes, but all the application is aborted, is the same when in a Web Controller I call some Service Object and it thrown an Exception and I didnt catch it, therefore I receive the error stack trace directly in my jsp or view

          I hope that clears everything up
          Not for the two last and most important paragraphs, I will research later these components when I arrive to such elements for my experiments, perhaps I am complicating by myself unnecessarily

          Thanks a lot for your valuable time and help

          My Best regards

          Comment


          • #6
            Therefore Map vs List
            But the List is assembled by traversing the Map, keyed by Type so, in this case, the List will have a max of 1 element. Recall that the general router API supports a list, but not every router supports it.

            when is wise use such approach?.
            Whenever you have a router that only supports returning a single result in getChannelIdentifiers(). If the result is a string, you can use this technique in such a router to route to multiple channels.

            I looked at the code and then tested it - it probably should be documented but we need to be careful to not make the documentation too confusing for people who only have simple use cases. Maybe we need an 'advanced techniques' section.

            I am creating the ErrorMessage in my Service Activator not in the Service Object,
            A <service-activator/> invokes (activates) a Service. A <service-activator/> is part of the framework; you don't write <service-activator/>s, you write services that are activated by SI.

            The SI namespace parser generates the service activator, which is the code that invokes your service.

            Your service should throw Exceptions, you generally should not need to extend or reference framework objects.

            Comment


            • #7
              Hello Gary

              Again thanks a lot for your time

              But the List is assembled by traversing the Map, keyed by Type so, in this case, the List will have a max of 1 element. Recall that the general router API supports a list, but not every router supports it.
              Thanks for the explanation

              Whenever you have a router that only supports returning a single result in getChannelIdentifiers(). If the result is a string, you can use this technique in such a router to route to multiple channels.
              I see a trick

              I looked at the code and then tested it - it probably should be documented but we need to be careful to not make the documentation too confusing for people who only have simple use cases. Maybe we need an 'advanced techniques' section.
              Agree, it could break some basic ideas about the resolution of channels

              A <service-activator/> invokes (activates) a Service. A <service-activator/> is part of the framework;
              Totally agree, but I dont call directly the Service Object from the <service-activator/>, instead, from the <service-activator/> I call a customized Service Activator endpoint (like a @Controller) and from there I call the services

              Perhaps in my previous last reply I did a wrong explanation, sorry for the confussion

              I mean:

              Code:
              <int:service-activator id="itemArticuloServiceActivator01"
              			   input-channel="dvdChannel"
              			   output-channel="final"		
              			   method="serviceActivatorAltern"				   
              			   ref="objectServiceActivator" />
              
              @MessageEndpoint
              public class ObjectServiceActivator {
              
                private SomeServiceBo someServiceBo
              
                @Autowired 
                public void setSomeServiceBo(....)
                ...
                @ServiceActivator
                public ....... methodABC(...){
                   ...
                   someServiceBo.someMethod(...);
                   ...
                }
              ...
              you don't write <service-activator/>s, you write services that are activated by SI.
              I coud be agree, but I prefer caught my possible expected own Exceptions (checked) to create some customized errors if some of them are thrown, something like MyDuplicatePKException, MyEmptyListEmptyException etc

              I hope you see my approach now

              Let me know your suggestions, I think my approach is correct and is practically the same used in MVC with @Controller, and even on Spring Documentation for this section B.5 Annotation Support the follow snippet code is available

              Code:
              @MessageEndpoint
              public class FooService {
              
                  @ServiceActivator
                  public void processMessage(Message message) {
                      ...
                  }
              }
              I dont think this class be a Service really, the context would be confused, I think should be FooEndPoint, because the method argument is tightly acopled related with SI

              Best Regards

              Comment


              • #8
                You have probably detected that I have a bias towards XML configuration; I will leave it to others to comment further on your use of @MessageEndpoint and @ServiceActivator, but they are intended to replace the <service-activator/> element rather then being referenced by it.

                In essence, your ref= in your <service-activator/> is refering to it as a POJO, not as an @MessageEndpoint. The attributes are intended to be discovered by a <context:component-scan/>; @MessageEndpoint is an @Component (stereotype annotation). This eliminates the need for the XML declaration.

                Regardless, I still believe that it is not necessary for your code (service activator or service) to deal with ErrorMessages or worry about routing. Simply throw an exception and the framework will take care of it.

                Comment


                • #9
                  Hello Gary

                  You have probably detected that I have a bias towards XML configuration;
                  Now yes, but I must know how to work with both approaches

                  I will leave it to others to comment further on your use of @MessageEndpoint and @ServiceActivato
                  OK, dont worry

                  but they are intended to replace the <service-activator/> element rather then being referenced by it.
                  Totally Agree, but I am using this approach due if I replace the <service-activator/> I got a confusion when I select the integration-graph tab on STS since this element not appear anymore in the flow integration. I hope you see my point now. Practically I am merging two approaches in one (xml + annotation)

                  In essence, your ref= in your <service-activator/> is refering to it as a POJO, not as an @MessageEndpoint. The attributes are intended to be discovered by a <context:component-scan/>; @MessageEndpoint is an @Component (stereotype annotation). This eliminates the need for the XML declaration.
                  Agree, but is more easier for me use @MessagePoint + @Autowired to declare this bean instead of the classic <bean declartion + manual injection and again due the previous sentence about the STS graphical support


                  And @ServiceActivator is used in case we dont want use the method attribute on the <service-activator/> but is better use this attribute if we have many methods in some POJO and we want set explicitly what method use

                  Regardless, I still believe that it is not necessary for your code (service activator or service) to deal with ErrorMessages or worry about routing. Simply throw an exception and the framework will take care of it
                  I am confused, what do you mean with the bold part?, If I do that, I can see clearly the error stack trace but the application is broke, since it is not handle it

                  Best Regards

                  Comment


                  • #10
                    That is a limitation of using annotations see https://issuetracker.springsource.com/browse/STS-1617.

                    I am confused, what do you mean with the bold part?, If I do that, I can see clearly the error stack trace but the application is broke, since it is not handle it
                    Read my earlier replies - the application does not need to create and send ErrorMessages itself. Simply throw the exception and if the inbound endpoint has an error-channel, an ErrorMessage will be sent there.

                    Not having an error-channel on an inbound endpoint is like not having a try-catch block in a method; if you don't use one, the exception will be thrown to the caller.

                    Comment


                    • #11
                      Hello Gary

                      That is a limitation of using annotations see https://issuetracker.springsource.com/browse/STS-1617.
                      Thank you, unresolved since February 2011

                      Read my earlier replies - the application does not need to create and send ErrorMessages itself. Simply throw the exception and if the inbound endpoint has an error-channel, an ErrorMessage will be sent there.
                      Thanks a lot, I going to read carefully such element. I am covering right now only the Routing chapter or section

                      Not having an error-channel on an inbound endpoint is like not having a try-catch block in a method; if you don't use one, the exception will be thrown to the caller
                      I understood

                      Sorry for my persistence and thanks for your patience

                      Comment

                      Working...
                      X