Announcement Announcement Module
Collapse
No announcement yet.
ErrorChannel not compatible with jms:inbound-gateway ? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ErrorChannel not compatible with jms:inbound-gateway ?

    I have the following configuration file and classes :

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/integration"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:integration="http://www.springframework.org/schema/integration"
    	xmlns:stream="http://www.springframework.org/schema/integration/stream"
    	xmlns:jms="http://www.springframework.org/schema/integration/jms"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    			http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    			http://www.springframework.org/schema/context
    			http://www.springframework.org/schema/context/spring-context-2.5.xsd
    			http://www.springframework.org/schema/integration
    			http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
    			http://www.springframework.org/schema/integration/jms
    			http://www.springframework.org/schema/integration/jms/spring-integration-jms-1.0.xsd
    			http://www.springframework.org/schema/integration/stream
    			http://www.springframework.org/schema/integration/stream/spring-integration-stream-1.0.xsd">
    
    
    	<jms:inbound-gateway id="jmsin"
    		default-reply-destination="defaultReplyQueue" request-destination="requestQueue"
    		request-channel="componentAChannel" />
    
    	<channel id="componentAChannel">
    		<queue capacity="25" />
    	</channel>
    
    	<channel id="componentBChannel">
    		<queue capacity="25" />
    	</channel>
    
    	<channel id="errorChannel">
    		<queue capacity="25" />
    	</channel>
    
    
    	<service-activator input-channel="componentAChannel"
    		output-channel="componentBChannel" ref="componentA" />
    	<service-activator input-channel="componentBChannel"
    		ref="componentB" />
    
    	<integration:poller id="poller" default="true">
    		<integration:interval-trigger interval="1000" />
    	</integration:poller>
    
    	<beans:bean id="componentA" class="test.ComponentA" />
    
    	<beans:bean id="componentB" class="test.ComponentB" />
    
    	<beans:bean id="connectionFactory"
    		class="org.springframework.jms.connection.CachingConnectionFactory">
    		<beans:property name="targetConnectionFactory">
    			<beans:bean class="org.apache.activemq.ActiveMQConnectionFactory">
    				<beans:property name="brokerURL" value="vm://localhost" />
    			</beans:bean>
    		</beans:property>
    		<beans:property name="sessionCacheSize" value="10" />
    		<beans:property name="cacheProducers" value="false" />
    	</beans:bean>
    
    	<beans:bean id="requestQueue" class="org.apache.activemq.command.ActiveMQQueue">
    		<beans:constructor-arg value="queue.request" />
    	</beans:bean>
    
    	<beans:bean id="defaultReplyQueue" class="org.apache.activemq.command.ActiveMQQueue">
    		<beans:constructor-arg value="queue.defaultReply" />
    	</beans:bean>
    
    	<stream:stderr-channel-adapter channel="errorChannel"
    		append-newline="true" />
    
    </beans:beans>
    	
    
    package test;
    
    import org.springframework.integration.annotation.MessageEndpoint;
    import org.springframework.integration.annotation.ServiceActivator;
    
    @MessageEndpoint
    public class ComponentA
    {
    	@ServiceActivator
    	public String execute(String content) {
    		System.out.println("ComponentA");
    		return content.toUpperCase();
    	}
    }
    
    
    package test;
    
    import org.springframework.integration.annotation.MessageEndpoint;
    import org.springframework.integration.annotation.ServiceActivator;
    
    @MessageEndpoint
    public class ComponentB
    {
    	@ServiceActivator
    	public String execute(String content) throws Exception {
    		System.out.println("ComponentB");
    		throw new Exception("This is an Exception");
    	}
    }
    
    package test;
    
    import javax.jms.ConnectionFactory;
    import javax.jms.Queue;
    
    import junit.framework.TestCase;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.jms.core.JmsTemplate;
    import org.springframework.jms.support.converter.SimpleMessageConverter;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "ErrorChannelTest.xml" })
    public class ErrorChannelTest extends TestCase
    {
    	private static String HELLO_WORD = "Hello World";
    	
    	@Autowired
    	private ConnectionFactory factory;
    
    	@Autowired
    	@Qualifier("requestQueue")
    	private Queue requestQueue;
    
    	@Autowired
    	@Qualifier("defaultReplyQueue")
    	private Queue defaultReplyQueue;
    	
    	@Test
    	public void testInboundGatewayWithProvidedReplyDestination() {
    		SimpleMessageConverter converter = new SimpleMessageConverter();
    		JmsTemplate jmsTemplate = new JmsTemplate(factory);
    		jmsTemplate.setMessageConverter(converter);
    		for (int i = 0; i < 10; i++) {
    			String request = HELLO_WORD + i;
    			jmsTemplate.setDefaultDestination(requestQueue);
    			jmsTemplate.convertAndSend(request);
    			jmsTemplate.setDefaultDestination(defaultReplyQueue);
    			Object object = jmsTemplate.receiveAndConvert();
    			String reply = (String) object;
    			assertEquals(request.toUpperCase(), reply);
    		}
    
    	}
    }
    The ComponentA converts the String to upperCase and the ComponentB then throws an exception. I would expect the Exception then to be wriiten to stderr, because this adapter is linked to the errorChannel.

    Nothing is however wriiten to stderror.
    And then the Unit test fails because is receives an MessageHandlingException instead of String.

    What am I doing wrong here, because I would like to build a custom component to subscribe to the error channel in the future. But if this doesn't even work the second one won't work either.
    It seems the errormessages or immediately send back instead of being passed to the errorchannel.
    I thought this was only the case if was in one thread/transaction, but I'm using channels with queues.

  • #2
    By default, inbound gateways will re-throw the error to the caller directly (or in this case return it as a JMS Message) since they set the replyChannel to be the errorChannel for the message they are sending.

    The base class has a 'shouldThrowErrors' property, and setting that to 'false' will produce the behavior you want. Unfortunately, I noticed that we don't have namespace support for that particular property yet. So, you would need to use a plain bean definition for the inbound JMS gateway for this to work right away.

    Can you verify that works for you and also open a JIRA issue for adding the namespace support?

    Thank you.

    Comment


    • #3
      Mark,

      I replaced

      Code:
      <jms:inbound-gateway id="jmsin"
      		default-reply-destination="defaultReplyQueue" request-destination="requestQueue"
      		request-channel="componentAChannel" />
      with

      Code:
      <beans:bean id="jmsin"
      		class="org.springframework.integration.jms.JmsMessageDrivenEndpoint">
      		<beans:constructor-arg ref="listenerContainer" />
      		<beans:constructor-arg ref="listener" />
      	</beans:bean>
      
      
      	<beans:bean id="listenerContainer"
      		class="org.springframework.jms.listener.SimpleMessageListenerContainer">
      		<beans:property name="destination" ref="requestQueue" />
      		<beans:property name="connectionFactory" ref="connectionFactory" />
      		<beans:property name="autoStartup" value="true" />
      	</beans:bean>
      
      	<beans:bean id="listener"
      		class="org.springframework.integration.jms.ChannelPublishingJmsMessageListener">
      		<beans:property name="defaultReplyDestination" ref="defaultReplyQueue" />
      		<beans:property name="requestChannel" ref="componentAChannel" />
      		<beans:property name="expectReply" value="true" />
      	</beans:bean>
      And know I writes the exception to stderror.
      But I would like to create a Component that registers on the errorchannel.
      This components takes the failedMessage from the Exception, transforms it into a byte array, and has no output channel.
      However when I do this the message keeps comming back to this component, because no reply destination can be found.
      So I would like to use the default reply destination together with a custom component taking the messages from the errorchannel and passing them to the default reply destination.
      Is such a thing possible ?
      Last edited by bassegio14; Mar 30th, 2009, 10:28 AM.

      Comment


      • #4
        I guess you could grab the reply-channel from the failedMessage that is wrapped by the ErrorMessage. However, you would want to add that to the reply-channel header with a header-enriching transformer.

        This might be something we should think about in terms of a reply-channel-aware-error-handler component...

        Am I understanding the requirement?

        Comment


        • #5
          I think you are understanding the requirement.
          But to make sure I will explain the problem I am facing :

          I have 10 request and 10 reply queues.
          Each request queue is linked with a reply queue using the default-reply-destination property of the jms inbound gateway.

          However all the messages posted on the request Queue are in EBCDIC.
          http://en.wikipedia.org/wiki/EBCDIC
          So the first thing I have to do is transform them to ASCII.
          After which I do some business logic that can cause error, if this happens I can't just pass the errorMessage back on the queue, because first and foremost I still have to transform it back to EBCDIC or it wont be readable on the other side.
          However instead of just returing the transformed error message I would like to return my own format that respect the service interface.
          So a reply-channel-aware-error-handler would be a nice feature.

          Thank you.

          Comment


          • #6
            The base class has a 'shouldThrowErrors' property, and setting that to 'false' will produce the behavior you want. Unfortunately, I noticed that we don't have namespace support for that particular property yet. So, you would need to use a plain bean definition for the inbound JMS gateway for this to work right away.
            Neither the JmsMessageDrivenEndpoint nor the ChannelPublishingJmsMessageListener nor the AbstractMessageListenerContainer or a subclass of AbstractMessagingGateway.
            So I don't see how to configure one the of the three beans to set the 'shouldThrowErrors' property to 'false'.
            I have looked at the code being executed and it doesn't seem possible to do such a thing. Or you have to start messing around with the channelTemplate of the ChannelPublishingJmsMessageListener and setting the DefaultChannel.
            But that doesn't seem to be the solution you had in mind.
            Are you sure the 'shouldThrowErrors' can be set using the JmsMessageDrivenEndpoint?

            Comment


            • #7
              I'm reading text messages from a jms queue. This sting is actually an xml. Which is unmarshalled using a si-xml:marshalling-transformer.
              However when the xml fails validation, the error is directly posted on the response queue. I don't want this I want to pass the error to the error channel. Here I can put the exception in an xml format that is send back to the sender of the request. This sender can interpret the xml response, but can't do anything with a jms message containing only the exception.

              Can somebody tell me how to accomplish this? A example of a bean configured this way would be nice.

              Comment

              Working...
              X