Announcement Announcement Module
Collapse
No announcement yet.
What is the best candidate to apply IF-ELSE logic inside a chain ? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • What is the best candidate to apply IF-ELSE logic inside a chain ?

    I would like to know what is the best component inside a <chain> to apply an IF-ELSE logic without decomposing the chain ?

    In other words, say I've something like:

    <int:chain input-channel="myInputChannel">
    <!-- component 1 -->
    <!-- component 2 -->
    <!-- component 3 -->
    <!-- component 4 -->
    <!-- component 5 -->
    </int:chain>

    What I want is to have an out-of-box component as part of this chain that will give me the functionality to process components 1,2 & 3 based on some logic whose output resolves to a "boolean" true and if that logic resolves to a "boolean" false, components 4 & 5 should be processed.



    Thanks
    LB
    Last edited by lbvirgo; Jul 9th, 2013, 03:48 AM.

  • #2
    Hello!

    Sorry, not enough info.
    Explain, please, your use case and show, at least, a prototype, what you want to achieve.

    Take care,
    Artem

    Comment


    • #3
      Consider that I've below chain:

      <int:chain input-channel="ersSvc2execSvcQMRChannel" output-channel="executionSvcSubmitAdhocReqChannel">
      <int:transformer ref="json2ObjTransformer" method="transformToERSOrchestrationSvcReq" />
      <int:service-activator ref="executionSvcReqMsgBuilder" method="getRptExecutionSvcReqForDataEngine" />
      <int:transformer ref="obj2JsonTransformer" method="transformFromRptExecutionSvcReqForDataEngi ne" />
      <int:service-activator ref="msgReqAuditDAOIntegrator" method="persist" />
      <int:service-activator ref="msgReqAuditDetailDAOIntegrator" method="persist" />
      </int:chain>

      What I want is that the first three components of the chain should get invoked(sequentially) IF my "payload" contains a certain value ELSE the last two components of chain should get invoked (sequentially).

      PS : I do not want to decompose the above chain into two separate chains and have a router logic by the calling layer. I want an IF-ELSE logic inside the chain itself.

      I hope I've made myself clear enough this time.

      Best Regards
      LB

      Comment


      • #4
        No, it's not OK for <chain>. First class citizen of the Spring Integration is a Channel. And the action on Messages relatively to channels is route, which, in turn, comes from endpoints.
        The <chain> is a composite endpoint, so it can receive Messages from input-channel and route them to the output-channel. Handlers within <chain> are invoked sequentially.
        If you want " IF-ELSE logic" in terms of Spring Integration, you should to decouple your <chain> to separate endpoints with their own channels and use <filter> and <router>.

        And a general advice: try do not to use <chain> at all, at least, till then you won't get used to Spring Integration concepts: Message, Channel, Endpoint.

        Is it clear?
        Artem

        Comment


        • #5
          Yes its clear and makes sense.

          On similar lines, I'm facing an issue in developing a logic through Spring Integration components. Pls find below the details:

          I've a gateway defined as below:
          Code:
          	<!--  Gateway -->
          	<int:gateway id="ersOrchestrationSvcGateway"
          				 service-interface="com.bnym.ecs.report.service.orchestration.gateway.ERSOrchestrationSvcGateway"
          				 error-channel="globalExceptionHandlerChannel" >
          
          		<int:method name="processSynchronousRequest" request-channel="ersServiceReqRcvPostValidationChannel" />
          		<int:method name="processAsynchronousRequest" request-channel="ersServiceReqRcvPostValidationChannel" />
          		<int:method name="processResponse" request-channel="ersServiceResRcvPostValidationChannel" />
          		<int:method name="processCallback" request-channel="ersServiceCallbackRcvChannel" />
          
          	</int:gateway>
          The gateway method "processSynchronousRequest" has a non-void return type whereas the remaining three have void returns.

          Now consider that the client has invoked "processSynchronousRequest" method and the downstream components that are processing any message on this channel throws a RuntimeException.
          Since my gateway has a "error-channel" defined, the exception will be wrapped as a MessaingException and will land on this error-channel.
          I've a chain that will read this error-channel and will make some meaningful failed return message for the client to understand.

          Now consider that client has invoked "processAsynchronousRequest" method and the downstream components that are processing any message on this channel throws a RuntimeException.
          Since my gateway has a "error-channel" defined, the exception will be wrapped as a MessaingException and will land on this error-channel.
          The same chain will read this error-channel and will make some meaningful failed return message. However since the return type of this gateway method is void, I do not need this meaningful failed return message to be return to client. I just need to log it and end the execution flow.

          How can I handle both the above scenarios through one "error-channel" ?

          OR the only way to handle is define different gateways for void and non-void methods ?

          Thanks in advance for all the suggestions.

          Best Regards
          LB

          Comment


          • #6
            OR the only way to handle is define different gateways for void and non-void methods ?
            Almost. If you have a return type on the Geteway's method, you can make some recovery logic on the error flow with error-channel in the beginning, and, of course, return to the caller some appropriate result.
            But if the method is void, of course, there should be a bit different logic.
            If you have this <int:method>, I suggest to add some logical boolean header, to determine in your error flow, if there is need to return something or just end the flow here:
            HTML Code:
            <method name="processSynchronousRequest" request-channel="ersServiceReqRcvPostValidationChannel">
            	<header name="isVoid" value="true"/>
            </method>
            Recently we've got an improvement issue to extract in the gateway called method metadata: https://jira.springsource.org/browse/INT-3069
            I think it would be useful in your case too.

            Isn't it?

            Comment


            • #7
              I think it would be useful in your case too.
              Yes, kind-of.

              A quick confirmation:


              If i add <header name="isVoid" value="true"/> to my method, will this header *always* be available in the message that lands up on my "error-channel" ? OR I need to first ensure that I always do "copyHeaders" on all my downstream components ?

              Regards
              LB

              Comment


              • #8
                will this header *always* be available in the message that lands up on my "error-channel" ?
                This header will be available before your ErrorMessage will travel to the "error-channel". After that you should use some SpEL trick:
                Code:
                payload.failedMessage.headers['isVoid']
                Or dig the Message in your Java ErrorHandler

                Comment


                • #9
                  Ok, do you think this logic is fine:

                  Code:
                  	
                  <!--  Gateway -->
                  	<int:gateway id="ersOrchestrationSvcGateway"
                  				 service-interface="com.bnym.ecs.report.service.orchestration.gateway.ERSOrchestrationSvcGateway"
                  				 error-channel="globalExceptionHandlerChannel" >
                  
                  		<int:method name="processSynchronousRequest" request-channel="ersServiceReqRcvPostValidationChannel">
                  			<int:header name="isVoid" value="false"/>
                  		</int:method>
                  		<int:method name="processAsynchronousRequest" request-channel="ersServiceReqRcvPostValidationChannel">
                  			<int:header name="isVoid" value="true"/>
                  		</int:method>
                  		<int:method name="processResponse" request-channel="ersServiceResRcvPostValidationChannel">
                  			<int:header name="isVoid" value="true"/>
                  		</int:method>
                  		<int:method name="processCallback" request-channel="ersServiceCallbackRcvChannel">
                  			<int:header name="isVoid" value="true"/>
                  		</int:method>
                  
                  	</int:gateway>
                  Code:
                  	<int:header-value-router input-channel="globalExceptionHandlerChannel" header-name="isVoid">
                  		<int:mapping value="true" channel="synchronousExceptionHandlerChannel" />
                  		<int:mapping value="false" channel="asynchronousExceptionHandlerChannel" />
                  	</int:header-value-router>
                  
                  	<int:chain input-channel="synchronousExceptionHandlerChannel">
                  		<int:transformer ref="exceptionTransformer" method="handleError"/>
                  		<int:service-activator ref="msgResAuditDAOIntegrator" method="persist" />
                  		<int:service-activator ref="msgResAuditDetailDAOIntegrator" method="persist" /> 
                  	</int:chain>
                  
                  	<int:chain input-channel="asynchronousExceptionHandlerChannel">
                  		<int:transformer ref="exceptionTransformer" method="handleError"/>
                  		<int:service-activator ref="msgResAuditDAOIntegrator" method="persist" />
                  		<int:service-activator ref="msgResAuditDetailDAOIntegrator" method="persist" /> 
                  		<int:logging-channel-adapter level="DEBUG" log-full-message="true"/>
                  	</int:chain>
                  Below are my gateway methods:

                  Code:
                  public interface ERSOrchestrationSvcGateway {
                  	
                  	@Gateway(requestChannel="ersServiceReqRcvPostValidationChannel")
                  	public Message<?> processSynchronousRequest(Message<?> message);
                  
                  	@Gateway(requestChannel="ersServiceReqRcvPostValidationChannel")
                  	public void processAsynchronousRequest(Message<?> message);
                  	
                  	@Gateway(requestChannel="ersServiceResRcvPostValidationChannel")
                  	public void processResponse(Message<?> message);
                  
                  	@Gateway(requestChannel="ersServiceCallbackRcvChannel")
                  	public void processCallback(Message<?> message);
                  
                  
                  }
                  Thanks
                  LB

                  Comment


                  • #10
                    LGTM! Well done! Keep her steady!

                    Comment


                    • #11
                      Tx

                      Before I put the changes in my "test" environment, a quick confirmation:

                      Is is mandatory to put <int:header> tag inside <int:header-enricher> tag or Spring will not complain ?

                      In other words, do I need to do this:
                      <int:header-enricher>
                      <int:header name="isVoid" value="false"/>
                      </int:header-enricher>

                      OR Spring does not complain as long as there is a single header key-value pair to be incorporated ? header-enricher is required only if I've more than one header key-value pair to be incorporated ?

                      Regards
                      LB

                      Comment


                      • #12
                        Sorry, don't understand why you ask about <header-enricher> now?

                        Of course, there is no need to declare 'isVoid' header on each your geteway's methods.

                        Comment


                        • #13
                          Looks like the logic isn't working.
                          My client invoked "processResponse" method for which "isVoid" header is true

                          As per header-value-router, it should have routed to "asynchronousExceptionHandlerChannel" but I'm getting following in my logs:
                          Code:
                          --------------------------------------------
                          **Root Cause of Failure** ==>
                          MessageDeliveryException: no channel resolved by router and no default output channel defined
                          --------------------------------------------
                          
                          --------------------------------------------
                          **Stack Trace** ==>
                          org.springframework.integration.MessageDeliveryException: no channel resolved by router and no default output channel defined
                                  at org.springframework.integration.router.AbstractMessageRouter.handleMessageInternal(AbstractMessageRouter.java:153)
                                  at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)
                                  at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:115)
                                  at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:102)
                                  at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
                                  at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:157)
                                  at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:128)
                                  at org.springframework.integration.core.MessagingTemplate.doSend(MessagingTemplate.java:288)
                                  at org.springframework.integration.core.MessagingTemplate.send(MessagingTemplate.java:149)
                                  at org.springframework.integration.gateway.MessagingGatewaySupport.send(MessagingGatewaySupport.java:187)
                                  at org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:309)
                                  at org.springframework.integration.gateway.GatewayProxyFactoryBean.doInvoke(GatewayProxyFactoryBean.java:269)
                                  at org.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:260)
                                  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
                                  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
                                  at $Proxy19.processResponse(Unknown Source)
                                  at com.bnym.ecs.report.service.orchestration.jms.listeners.ERSOrchestrationSvcResMDP.onMessage(ERSOrchestrationSvcResMDP.java:106)
                                  at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:562)
                                  at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:500)
                                  at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:468)
                                  at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:326)
                                  at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:264)
                                  at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1069)
                                  at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1061)
                                  at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:958)
                                  at java.lang.Thread.run(Thread.java:662)
                          ]
                          --------------------------------------------]
                          Any idea what is the cause ?

                          regards
                          LB

                          Comment


                          • #14
                            M-m-m.
                            Sorry, I mentioned it, but didn't point it out in your config.
                            Your router should be like this:
                            HTML Code:
                            <router inpuit-channel="globalExceptionHandlerChannel" expression="payload.failedMessage.headers['isVoid']"
                                      default-output-channel="asynchronousExceptionHandlerChannel">
                                 <mapping value="true" channel="synchronousExceptionHandlerChannel" />
                            </router>
                            However, it would be great if you'd done something yourself

                            Comment


                            • #15
                              Thanks but what is the issue in directly using a header-value-router (as I'd used in my configuration) instead of using a generic router as you mentioned above ?

                              Regards
                              LB

                              Comment

                              Working...
                              X