Announcement Announcement Module
Collapse
No announcement yet.
Not able to return a valid response back to client after receiving an exception Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Not able to return a valid response back to client after receiving an exception

    Hi

    I've the understanding that Spring Integration (SI) will wrap any exception (under the SI domain) to a MessageException instance and place it on the "error-channel".

    Following are few snippets from my spring config file :

    Code:
    	
    	<int:channel-interceptor pattern="ersServiceReqRcvPostValidationChannel,ersServiceResRcvPostValidationChannel" order="1">
        	<bean class="com.bnym.ecs.report.service.orchestration.interceptors.MsgJSONSyntaxValidationInterceptor"/>
    	</int:channel-interceptor>
    
    	<int:channel-interceptor pattern="ersServiceReqRcvPostValidationChannel,ersServiceResRcvPostValidationChannel" order="2">
        	<bean class="com.bnym.ecs.report.service.orchestration.interceptors.MsgMetaDataValidationInterceptor"/>
    	</int:channel-interceptor>
           
    <!--  Gateways -->
    	<int:gateway id="ersServiceReqRcvGateway"
    		service-interface="com.bnym.ecs.report.service.orchestration.gateway.ERSOrchestrationSvcReqGateway"
    		error-channel="reqRcvExceptionHandlerChannel">
    		<int:method name="processRequest" request-channel="ersServiceReqRcvPostValidationChannel" />
    	</int:gateway>
    
    	<!--  Chain to handle all incoming request *after* doing all validations -->
    	<int:chain input-channel="ersServiceReqRcvPostValidationChannel">
    		<int:service-activator ref="msgReqAuditDetailDAOIntegrator" method="persist" />
    		<!--  Router -->
    		<int:router ref="ersServiceReqRcvRouter" />
    	</int:chain>
    
    	<!--  6) Pass the message through ERS svc to Exec svc ADH chain - Chain2 -->
    	<int:chain input-channel="ersSvc2execSvcQMRChannel" output-channel="ersServiceResRcvPostValidationChannel">
    		<int:transformer ref="json2ObjTransformer" method="transformToERSOrchestrationSvcReq" />
    		<int:service-activator ref="executionSvcReqMsgBuilder" method="getRptExecutionSvcReqForDataEngine" />
    		<int:transformer ref="obj2JsonTransformer" method="transformFromRptExecutionSvcReqForDataEngine" />
    		<int:service-activator ref="msgReqAuditDAOIntegrator" method="persist" />
    		<int:service-activator ref="msgReqAuditDetailDAOIntegrator" method="persist" />
    		<int:service-activator ref="executionSvcRESTStub" method="executeReportJSON" />
    	</int:chain>
    
    	<int:chain input-channel="reqRcvExceptionHandlerChannel">
    		<int:transformer ref="exceptionTransformer" method="handleError"/>
    	</int:chain>
    The client makes a REST call to my implementation class which inturn places the received request on the Gateway defined in above spring config file

    Code:
    @Path("/reportExecutor")
    public class ERSOrchestrationServiceImpl {
    
    	@Autowired
    	private ReportInstanceDAO reportInstanceDAO;
    
    	private static final ERSOrchestrationSvcDiagnosticLogger _logger = 
    		ERSOrchestrationSvcDiagnosticLogger.getInstance(ERSOrchestrationServiceImpl.class);
    
    	@Context
    	HttpServletRequest request;
    	@Context
    	HttpServletResponse response;
    
    	@POST
    	@Path("/executeOnlineReport")
    	@Produces({MediaType.APPLICATION_JSON})
    	public String executeOnlineReport(String jsonRequest) {
    
    			ApplicationContext appCtx = SpringApplicationContextUtil.getApplicationContext();
    			
    			ERSOrchestrationSvcReqGateway ersOrchestrationSvcReqGateway = 
    				(ERSOrchestrationSvcReqGateway) appCtx.getBean("ersServiceReqRcvGateway");
    			
    			Message<String> inputMsg = MessageBuilder.withPayload(jsonRequest)
    													 .setHeader(ERSServiceConstants.KEY_MSG_CORRELATION_ID, correlationId)
    													 .setHeader(ERSServiceConstants.KEY_MSG_REPORT_INSTANCE_ID, reportInstanceId)
    													 .build();
    			
    			Message<String> returnMsg = ersOrchestrationSvcReqGateway.processRequest(inputMsg);
    			return returnMsg.getPayload();
    			
    		}
    As mentioned in above spring config file, the error-channel is read by a Transformer that creates a valid failed response message for the client and returns the message.

    Code:
    public class ErrorMessageUnwrapTransformer {
    
    	@Autowired
    	private Gson gsonUtil;
    	@Autowired
    	private ReportInstanceDAO reportInstanceDAO;
    	@Autowired
    	private ERSOrchestrationSvcFailedResMsgBuilder executionSvcFailedMsgBuilder;
    
    	private static final ERSOrchestrationSvcDiagnosticLogger _log = 
    		ERSOrchestrationSvcDiagnosticLogger.getInstance(ErrorMessageUnwrapTransformer.class); 
    
    	@Transformer
    	public Message<?> handleError(Message<?> message) {
    		try{
    			failedMsg = ((MessagingException) message.getPayload()).getFailedMessage();
    
                            //some code logic to build a valid failed response message goes here
     			Message<?> failedResponseMsg =                  executionSvcFailedMsgBuilder.getERSOrcSvcFailedResMsg(failedMsg );
    
    return failedResponseMsg;
    }
    All seems to work fine when I get an exception, i.e., the exception is wrapped as MessagingException, put on the error-channel, the Transformer is able to read the channel, get failedMessage out of it, able to create a valid failed response message and return it.

    However, the only issue I get is the call does not go back to the caller. In other words, the handle does not go back to the following code that had initiated the processing flow:

    Code:
    Message<String> returnMsg = ersOrchestrationSvcReqGateway.processRequest(inputMsg);
    Can someone pls let me know why is the message returned by error-channel-read-Transformer not returning back to the class that invoked the Gateway method ?

  • #2
    Hi!
    Here you are using Transformer. It is one of handlers, who doesn't modify returned Message before seding it further.
    From other side: Gateway is endpoint, who is waitng response on TemporaryReplyChannel, which is placed in the REPLY_CHANNEL Message Header before send request.
    So, configuration looks good, however you have to copy ReplyChannel from incoming Message to the return Message in your ErrorMessageUnwrapTransformer.

    Good luck,
    Artem

    P.S. But... Where is a ChannelResolutionException with 'no output-channel or replyChannel header available' ?..

    Comment


    • #3
      Artem, there's no error, because by using the replyChannel from the failedMessage, the result is being sent to the "old" replyChannel (and nobody's listening anymore). As you say, the result has to be sent to the replyChannel from the ErrorMessage.

      We recently added a warning for when replies are sent to "stale" reply channels. https://jira.springsource.org/browse/INT-3004

      In general, it would be better to use a signature like this

      Code:
      @Transformer
      	public Object handleError(MessagingException exception) {
      and let the framework take care of managing the headers (unless you have headers in your failedMessage that you need to retain for some reason).

      Comment


      • #4
        Hi

        Thanks for responding. So, if I understand correctly, the below code in the Transformer should fix the issue ?

        Code:
        Message<?> returnMsg = MessageBuilder.withPayload(failedResponseMsg).copyHeaders(message.getHeaders()).build()
        Also, does <gateway> tag support any attribute where user can define his own REPLY_CHANNEL instead of relying on the default one ?

        Best Regards
        LB

        Comment


        • #5
          1. Correct.

          2. No; the replyChannel is an internal device used by the gateway; if you supply a 'reply-channel' (on a method element) or a 'default-reply-channel' on the <gateway/> element, that channel is simply bridged internally to the message-specific replyChannel that was inserted by the gateway.

          When a downstream process throws an exception, a new 'ErrorMessage' is sent on the error-channel, and it gets a new replyChannel header, on which the gateway is now waiting for its response.

          Comment


          • #6
            Hi Gary

            I added the code to copy headers in the outgoing message but strangely still the control is not returned backed to the caller that invoked the Gateway method.

            Code:
            //some code logic to build a valid failed response message goes here
             			Message<?> failedResponseMsg =                  executionSvcFailedMsgBuilder.getERSOrcSvcFailedResMsg(failedMsg );
            
            		   	Message<?> returnMsg = MessageBuilder.withPayload(failedResponseMsg)
            		   										 .copyHeaders(message.getHeaders())
            		   										 .build();
            
            return returnMsg;
            On debugging the flow (after adding the copyHeaders code), I see that the TemporaryReplyChannel object seems to be same for both incoming and outgoing message of ErrorMessageUnwrapTransformer class.

            Headers for Incoming Message ==>

            {timestamp=1370332654255, id=a0a1f52b-3c21-4110-a84b-bf3c3e24d636,
            errorChannel=org.springframework.integration.core. MessagingTemplate$TemporaryReplyChannel@2abf83, replyChannel=org.springframework.integration.core. MessagingTemplate$TemporaryReplyChannel@2abf83}

            Headers for Outgoing Message ==>

            [timestamp=1370332672583, id=6163787b-1595-4604-b0ad-e58d6e174e0a,
            errorChannel=org.springframework.integration.core. MessagingTemplate$TemporaryReplyChannel@2abf83, replyChannel=org.springframework.integration.core. MessagingTemplate$TemporaryReplyChannel@2abf83]

            Any idea what else could be the probable cause of this issue ?

            Many thanks & Best Regards
            LB

            Comment


            • #7
              I'd need to see a full DEBUG trace.

              Can you try with 3.0.0.M2 (which includes the extra diagnostics when data is sent to a stale gateway channel).

              Comment


              • #8
                If I drill down the "cause" of exception object, the error message I get is:

                java.lang.ClassCastException: org.springframework.integration.message.GenericMes sage cannot be cast to java.lang.String

                Pls find attached the scrn shot.

                Attachment

                I earlier had my method signature defined as below in the Gateway interface:

                Code:
                public interface ERSOrchestrationSvcReqGateway {
                	
                	@Gateway(requestChannel="ersServiceReqRcvPostValidationChannel")
                	public Message<String> processRequest(Message<String> message);
                
                }
                and caller code was:

                Code:
                Message<String> returnMsg = ersOrchestrationSvcReqGateway.processRequest(inputMsg);
                I replaced <String> with <?> in above code snippets but issue still persist (same ClassCastException error message).

                Any pointers ?

                Best Regards
                LB
                Attached Files

                Comment


                • #9
                  java.lang.ClassCastException: org.springframework.integration.message.GenericMes sage cannot be cast to java.lang.String
                  Right. If your method is declared as 'public Message<String>', so you should return value with exactly the same type.
                  It is an issue of your code:
                  MessageBuilder.withPayload(failedResponseMsg)
                  Here you have to build Message with String payload, as you are expecting on Gateway's method return type.

                  Comment


                  • #10
                    Thanks for responding but as I mentioned in my earlier response that I tried changing the signature to <?> but it still did not resolve the issue.
                    In other words, I tried with following method signature for Gateway interface:

                    Code:
                    public interface ERSOrchestrationSvcReqGateway {
                    	
                    	@Gateway(requestChannel="ersServiceReqRcvPostValidationChannel")
                    	public Message<?> processRequest(Message<?> message);
                    
                    }
                    and the caller code as:

                    Code:
                    Message<?> returnMsg = ersOrchestrationSvcReqGateway.processRequest(inputMsg);

                    Are you saying changing the type to <?> will also not work irrespective of what is being sent in the payload ?

                    Thanks
                    LB

                    Comment


                    • #11
                      Try to investigate a StackTrace.
                      Ort just show it here

                      Comment


                      • #12
                        I've already sent a screen shot of the Exception object that has details of "cause" from the point it was thrown until it was caught back by the caller.
                        Are you asking to look for anything specific in that ?

                        Thanks
                        LB

                        Comment


                        • #13
                          Are you asking to look for anything specific in that ?
                          I want to see which line of code causes ClassCastException

                          Comment


                          • #14
                            Got the issue fixed (kind-of).

                            I saw that the message being returned was embedded with two Payloads, something like:

                            [Payload=[Payload={"messageHeader":{"

                            I then ensured that my Transformer returns just a String payload and it now works fine (i.e., I get the handle back to the caller).

                            However I'm still curious to know that even though I changed the type to <?>, handle was not being sent back to caller. Why was I forced to have a String payload only ?

                            I did a recheck of my code and nowhere I was restricting the type to be a String.

                            I did try to see if I can get a stack trace, but I get nothing other than what I've shared earlier in the screen shot.

                            Thanks again for all your guidance.

                            Best Regards
                            LB

                            Comment

                            Working...
                            X