Announcement Announcement Module
Collapse
No announcement yet.
n00b needs help with error handling Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • n00b needs help with error handling

    Hi!
    I've been trying out Spring Integration a bit, so far with unexpected amounts of success, but when it comes to handling exceptions I'm stumped.

    I have a JMS queue which takes requests, the service does stuff and the result is posted to another queue, defined in the request's JMSRepyTo header. This works fine, but if an exception occurs I'd like some error message to be sent on the reply queue and that I've had no luck achieving.

    This is the configuration I have at the moment:
    Code:
        <!-- Channels -->
        
        <si:channel id="inputChannel"/>
        <si:chain input-channel="inputChannel" output-channel="outputChannel">
            <si:header-enricher error-channel="errorChannel"/>
            
            <si:filter ref="messageRouter" method="reroute"/>
            <si:transformer ref="payloadExtractor" method="extract"/>
            <si:transformer ref="OXMapper" method="unmarshall"/>
            <si:service-activator ref="messageEndpoint" method="doStuff"/>
            <si:transformer ref="OXMapper" method="marshall"/>
            <si:transformer ref="payloadExtractor" method="soapify"/>
        </si:chain>
        <si-jms:message-driven-channel-adapter channel="inputChannel" connection-factory="connectionFactory"/>
        
        <si:channel id="outputChannel"/>
        <si-jms:outbound-channel-adapter channel="outputChannel" jms-template="jmsTemplate" />
        
        <si:channel id="errorChannel"/>
        <si:chain input-channel="errorChannel" output-channel="outputChannel">
            <si:transformer ref="errorHandler" method="handleMessage"/>
        </si:chain>
    
        <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
            <property name="connectionFactory" ref="connectionFactory"/>
            <property name="defaultDestinationName" value="DEFAULTOUTPUTQUEUE"/>
        </bean>
    
        <!-- Classes -->
    
        <!-- changes jmsTemplate's defaultDestinationName to queue in message's JMSReplyTo header  -->
        <bean id="messageRouter" class="test.MessageRouter">
            <property name="jmsTemplate" ref="jmsTemplate"/>
        </bean>
        
        <!-- extracts/wraps payload from/in SOAP message -->
        <bean id="payloadExtractor" class="test.PayloadTransformer">
            <property name="jmsTemplate" ref="jmsTemplate"/>
        </bean>
        
        <!-- unmarshalls/marshalls objects from/to XML -->
        <bean id="OXMapper" class="test.OXMapper">
            <property name="jmsTemplate" ref="jmsTemplate"/>
            <property name="mapper" ref="testMapper"/>
        </bean>
        
        <!-- does stuff -->
        <bean id="messageEndpoint" class="test.MessageEndpoint">
            <property name="jmsTemplate" ref="jmsTemplate"/>
            <property name="localityService" ref="localityService"/>
        </bean>
        
        <!-- is supposed to handle error messages -->
        <bean id="errorHandler" class="test.ErrorHandler">
            <property name="jmsTemplate" ref="jmsTemplate"/>
        </bean>
    and this is the ErrorHandler:
    Code:
    public class ErrorHandler extends MessageSender {
      
      @Transformer
      public String handleMessage(Message errorMessage) {
        System.out.println("Here!");
        return "Something happened!";
      }
    }
    When I throw an exception in the input chain nothing is printed and no reply is sent to any queue.
    Please help me! What am I doing wrong?

  • #2
    Okay. The 'error-channel' would only take effect if the Exception-throwing message handler were invoked within the scope of an asynchronous task (one submitted to a TaskExecutor or resulting from a poller). In your case, it's actually the thread that receives the JMS Message in the DefaultMessageListenerContainer that is invoking the entire chain. The default handing of Exceptions in that container is to log only unless it's a JMSException (then you can provide a JMS ExceptionListener to deal with those).

    There are a few options, but first let me know if what I stated above is clear.

    Comment


    • #3
      Yeah, I think so.

      Comment


      • #4
        Mark? Don't forget about me!

        Comment


        • #5
          Well, based on what I was saying, your error channel is not taking effect. The error-channel only comes into play if there is an asynchronous boundary - if the task is submitted to a TaskExecutor along the way (e.g. when a channel with a 'queue' is used). However, that would also break the transactional boundary that starts with the JMS message being received.

          Where does your Exception occur. Is there a way to catch it explicitly?

          Are you seeing the Exception simply logged from the DefaultMessageListenerContainer?

          Comment


          • #6
            Exceptions will most likely occur somewhere in the doStuff method. As far as catching them, I'm acctually doing that right now and printing the stack trace.

            I guess I could just let doStuff return an error message in a String instead of the intended value if an exception was caught, but I'd just like to know if there was a "proper" way to do it.

            Comment


            • #7
              The reason that the error-channel is only used in an asynchronous dispatch is that the standard behavior for synchronous dispatch is to simply throw the Exception to the caller. In other words, if you invoke channel.send(m) directly and that pipeline throws an Exception without any TaskExecutors to cause a new Thread to take over, then any runtime Exceptions would be thrown right there at the send(m) call.

              Now, that is great in most cases, but there is one problem with this in a JMS Message-driven scenario. The DefaultMessageListenerContainer is the "caller" in this case, and this is the code that handles any RuntimeException (in the AbstractMessageListenerContainer base class):
              Code:
              protected void handleListenerException(Throwable ex) {
              	if (ex instanceof MessageRejectedWhileStoppingException) {
              		// Internal exception - has been handled before.
              		return;
              	}
              	if (ex instanceof JMSException) {
              		invokeExceptionListener((JMSException) ex);
              	}
              	if (isActive()) {
              		// Regular case: failed while active.
              		// Log at error level.
              		logger.warn("Execution of JMS message listener failed", ex);
              	}
              	else {
              		// Rare case: listener thread failed after container shutdown.
              		// Log at debug level, to avoid spamming the shutdown log.
              		logger.debug("Listener exception after container shutdown", ex);
              	}
              }
              As you can see, only logging occurs for RuntimeExceptions (only JMSExceptions are "handled"). The reason for this is that the listener container assumes any MessageListener should handle its own Exceptions. The JMSExceptions signify something at the "infrastructure" level, so those can be handled by the framework. However, there is no clear one-size-fits-all approach for handling an Exception that occurs while executing business logic within the scope of the handler.

              So, one thing you could do is subclass DefaultMessageListenerContainer and override that method. Then you can provide that in your "container" reference in the message-driven-channel-adapter. That probably does not sound ideal, but it would at least keep your generic Exception handling code out of the business logic.

              I'm sure you are thinking that we should provide some out-of-the-box support for this situation in Spring Integration, and I actually agree. There are a few things we could do. First, we could catch any Exception (re-throwing only JMSExceptions) thrown within the scope of our MessageListenter, and we could check for an "error-channel" header on the Message. Second, we could provide a boolean for "sendErrorMessagesOnReplyChannel". Third, we could provide a separate configurable reply-to for errors. I am also open to other suggestions.

              If you think something along these lines seems right, then please open a JIRA issue, and we can address this within Spring Integration during the 2.0 roadmap.

              Thanks,
              Mark

              Comment


              • #8
                Will try your suggestion of overriding the handler method.
                Thanks a lot!

                Comment


                • #9
                  I subclassed DefaultMessageListenerContainer and used it as the container in channel adapter like you suggested. However, now when I try to deploy I get stuck in a loop which every second logs this message:

                  Code:
                  DEBUG Consumer [org.jboss.resource.adapter.jms.JmsMessageConsumer@9a5d54] of session [org.jboss.resource.adapter.jms.JmsSession@1602bbc] did not receive a message at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:347)
                  Any idea what's going on?

                  Comment


                  • #10
                    Um.. nevermind. I guess it's supposed to do that.

                    Comment


                    • #11
                      Back again
                      I realize this might not have anything to do with Spring, but I need help.

                      I've been trying to create SOAP fault messages through the following code:
                      Code:
                            // Create a SOAPMessage
                            MessageFactory messageFactory = MessageFactory.newInstance();
                            SOAPMessage soapMessage = messageFactory.createMessage();
                            
                            //Get parts
                            SOAPPart soapPart = soapMessage.getSOAPPart();
                            SOAPEnvelope envelope = soapPart.getEnvelope();
                            SOAPBody body = envelope.getBody();
                            SOAPFault fault = body.addFault();
                                  
                            //Add error message
                            Name code = envelope.createName("Server", null, SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE);
                            fault.setFaultCode(code);
                            fault.setFaultString(errorMessage);
                            
                            soapMessage.saveChanges();
                      At the line
                      Code:
                      fault.setFaultCode(code);
                      I get a SOAPException stating that "{http://www.w3.org/2003/05/soap-envelope}Server is not a standard Code value." I don't know what might be causing this.

                      I'm using SAAJ 1.3 to create SOAP 1.2 messages, using com.sun.xml.messaging.saaj.soap.ver1_2.SOAPMessage Factory1_2Impl as the MessageFactory.

                      Comment


                      • #12
                        Nevermind. In SOAP 1.2 it's supposed to be "Reciever", not "Server".

                        Comment


                        • #13
                          Originally posted by Mark Fisher View Post
                          The reason that the error-channel is only used in an asynchronous dispatch is that the standard behavior for synchronous dispatch is to simply throw the Exception to the caller. In other words, if you invoke channel.send(m) directly and that pipeline throws an Exception without any TaskExecutors to cause a new Thread to take over, then any runtime Exceptions would be thrown right there at the send(m) call.

                          Now, that is great in most cases, but there is one problem with this in a JMS Message-driven scenario. The DefaultMessageListenerContainer is the "caller" in this case, and this is the code that handles any RuntimeException (in the AbstractMessageListenerContainer base class):
                          Code:
                          protected void handleListenerException(Throwable ex) {
                          	if (ex instanceof MessageRejectedWhileStoppingException) {
                          		// Internal exception - has been handled before.
                          		return;
                          	}
                          	if (ex instanceof JMSException) {
                          		invokeExceptionListener((JMSException) ex);
                          	}
                          	if (isActive()) {
                          		// Regular case: failed while active.
                          		// Log at error level.
                          		logger.warn("Execution of JMS message listener failed", ex);
                          	}
                          	else {
                          		// Rare case: listener thread failed after container shutdown.
                          		// Log at debug level, to avoid spamming the shutdown log.
                          		logger.debug("Listener exception after container shutdown", ex);
                          	}
                          }
                          As you can see, only logging occurs for RuntimeExceptions (only JMSExceptions are "handled"). The reason for this is that the listener container assumes any MessageListener should handle its own Exceptions. The JMSExceptions signify something at the "infrastructure" level, so those can be handled by the framework. However, there is no clear one-size-fits-all approach for handling an Exception that occurs while executing business logic within the scope of the handler.

                          So, one thing you could do is subclass DefaultMessageListenerContainer and override that method. Then you can provide that in your "container" reference in the message-driven-channel-adapter. That probably does not sound ideal, but it would at least keep your generic Exception handling code out of the business logic.

                          I'm sure you are thinking that we should provide some out-of-the-box support for this situation in Spring Integration, and I actually agree. There are a few things we could do. First, we could catch any Exception (re-throwing only JMSExceptions) thrown within the scope of our MessageListenter, and we could check for an "error-channel" header on the Message. Second, we could provide a boolean for "sendErrorMessagesOnReplyChannel". Third, we could provide a separate configurable reply-to for errors. I am also open to other suggestions.

                          If you think something along these lines seems right, then please open a JIRA issue, and we can address this within Spring Integration during the 2.0 roadmap.

                          Thanks,
                          Mark
                          Hey Mark -

                          I'm beginning to use 1.0.3 and have not yet looked at the 2.0 milestone, yet. I have this same issue where I'm using a message driven channel adapter, and am simply routing it out to a different JMS destination. If an error occurs, it bubbles all of the way up to the DefaultMessageListenerContainer. What seems ideal, at least for me, would be that if an error channel header is found in the message, dump that error off to the error channel, regardless of whether or not the processing was done synchronous or asynchronous. That would allow me to decide when laying out the flow how I wanted errors to be handled, without having to override anyone else's class which is used in the flow (Spring Framework or otherwise). Or, if I wanted to override a class, I simply wouldn't add the error channel header.

                          Maybe you've already implemented a solution to this problem in the 2.0 milestone, so forgive me if that's that case, I just haven't had a chance to look, yet.

                          Comment


                          • #14
                            The DefaultMessageListenerContainer in Spring 3.0 supports the setting of an ErrorHandler strategy. So, one idea we have here is to simply allow one to pass that through (and it's the same strategy interface implemented by our MessagePublishingErrorHandler).

                            However, I also think the issue is a bit more generic. In other words, it doesn't only apply to JMS but to any inbound gateway (HTTP, WS, etc.). Additionally, some users might not want to publish the Error message or *handle* the Exception at all on the server side. Instead, it's quite common to convert that Exception or ErrorMessage into an "error" or "fault" response message. Therefore, what I think we really need is an error handler/post-processor that would support either "handling" the error on the server-side or creating a response from it.

                            I've created the following JIRA issue:
                            http://jira.springframework.org/browse/INT-904

                            Feel free to watch/vote and comment on that issue with any other ideas you might have.

                            Thanks,
                            Mark

                            Comment


                            • #15
                              I agree, and it sounds like you're on the path to the most flexible solution that would allow for different use cases to handle errors in different ways. I know in our envirnonment, I could make use of each option in different scenarios.

                              I'll pull in the milestone and check it out.

                              Thank much.

                              Comment

                              Working...
                              X