Page 1 of 2 12 LastLast
Results 1 to 10 of 18

Thread: Error handling with nested chain

  1. #1
    Join Date
    Mar 2011
    Posts
    22

    Default Error handling with nested chain

    I'm seeing that there are no attempts to receive messages from a source after an exception is thrown within a chain that is nested in another chain.

    To demonstrate the behavior I’ve modified the configuration of the errorhandling example from http://git.springsource.org/spring-integration/samples.

    Replace the errorHandlingDemo.xml with the configuration below and run the test you should see only one invitation is generated.

    Thanks,
    John

    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:stream="http://www.springframework.org/schema/integration/stream"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    			http://www.springframework.org/schem...ring-beans.xsd
    			http://www.springframework.org/schema/context
    			http://www.springframework.org/schem...ng-context.xsd
    			http://www.springframework.org/schema/integration
    			http://www.springframework.org/schem...ntegration.xsd
    			http://www.springframework.org/schem...gration/stream
    			http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
    
    	<context:component-scan base-package="org.springframework.integration.samples.errorhandling" />
    
    	<poller default="true" fixed-delay="1000" max-messages-per-poll="1"/>
    
    	<inbound-channel-adapter ref="partyHost"
    		method="nextInvitation" channel="invitations" />
    
    	<channel id="invitations">
    		<queue capacity="100" />
    	</channel>
    
    	<chain input-channel="invitations">
    		<header-enricher>
    			<error-channel ref="failed-invitations" />
    		</header-enricher>
    		<gateway request-channel="nested-chain-input" error-channel="failed-invitations"/>
    	</chain>
    
    	<chain input-channel="nested-chain-input">
    		<header-enricher>
    			<error-channel ref="failed-invitations" />
    		</header-enricher>
    		<service-activator ref="partyGuest" method="onInvitation" />
    	</chain>
    
    	<channel id="failed-invitations" />
    	<channel id="nested-chain-input" />
    
    	<chain input-channel="failed-invitations">
    		<transformer ref="errorUnwrapper" />
    		<service-activator ref="partyHost" method="onInvitationFailed" />
    	</chain>
    
    	<!--
    		If you don't listen to the default error channel you risk losing track
    		of exceptions, as they cannot be passed back to the sender in band. It
    		is recommended to have a generic error handler in your configuration
    		to prevent this.
    	-->
    	<stream:stderr-channel-adapter channel="errorChannel" append-newline="true" />
    
    </beans:beans>

  2. #2
    Join Date
    Mar 2011
    Posts
    22

    Default

    Has anyone had a chance to try this for themselves?

  3. #3
    Join Date
    Jan 2008
    Location
    Mohnton, PA USA (that's near Philadelphia)
    Posts
    2,148

    Default

    I'll take a look

  4. #4
    Join Date
    Jan 2008
    Location
    Mohnton, PA USA (that's near Philadelphia)
    Posts
    2,148

    Default

    Actually everything is working as expected, however. . .

    Gateways are generally for bi-directional communication. Adapters for uni-directional. The exception is the interface-based gateway such as <int:gateway> which can actually be both and where 'bi-directional' or 'uni-directional' depends on the method of the service-interface defined for this gateway. If method returns 'void' then this gateway is uni-directional otherwise bi-directional. Why am I saying all this?
    In your example you are defining gateway which simply invokes another chain, therefore you are not defining service-interface attribute. In this case GatewayProxyFactoryBean creates RequestReplyExchanger interface which defines a single method:
    Code:
    Message<?> exchange(Message<?> request);
    As you can see this is a non-void method which means that 'sendAndReceive' method of MessagingGatewaySupport will be called instead of 'send', which means this gateway will be waiting for a reply which is not coming in your case (sendAndReceive blocks) since component downstream returns void and that is why you are not seeing more then one poll.

    I hope that explains. Although we need to think about it internally and see if gateway in the chain can be a bit more intelligent, I am still wondering about the use case that prompted you to change this sample. Was it simply a learning experience or you have some real requirement and if so could you please describe it since it will help us realize how it can influence the framework.
    Cheers
    Last edited by oleg.zhurakousky; Mar 24th, 2011 at 09:36 AM.

  5. #5
    Join Date
    Mar 2011
    Posts
    22

    Default

    Use case:

    Read messages off of a JMS queue as they arrive and perform a sequence of steps. If any step fails, push the message to an error queue. In a different app read messages from the error queue periodically and perform a few error related steps and then send message to the original sequence of steps to be processed again.

    I’ve configured the two chains, 1) for the original sequence of steps 2) steps performed on retry, with the second chain containing an embedded call to the first chain. The mock xml below may help illuminate the configuration.

    <!— original sequence of steps 
    <chain input-channel="processingChannel" output-channel="cleanupChannel">
    <header-enricher>
    <error-channel ref="failedChannel" />
    </header-enricher>
    <payload-deserializing-transformer />
    <service-activator ref="ref1" method="step1"/>
    <service-activator ref="ref2" method="step2"/>
    <service-activator ref="ref3" method="step3"/>
    </chain>

    <!—retry steps w/embedded call to the chain of the original sequence of steps 
    <chain input-channel="retryChannel" output-channel="cleanupChannel">>
    <header-enricher>
    <error-channel ref=" failedChannel" />
    </header-enricher>
    <payload-deserializing-transformer />
    <service-activator ref="refE1" method="stepE1"/>
    <gateway request-channel=" processingChannel" error-channel="failedChannel" />
    <service-activator ref="refE2" method="stepE2"/>
    </chain>

    The problem I was trying to show with the changes to the errorhandling sample was that if an exception is thrown inside the nested chain, then the error message is sent to the failedChannel and processed, but then no further messages are read. To clarify, ref1->step1, ref2->step2, ref3->step3, refE1->stepE1, refE2-stepE2 all have a non-void return type.

    Hope this helps.
    John

  6. #6
    Join Date
    Mar 2011
    Posts
    22

    Default

    Oleg,

    It turns out the issue was more involved than I thought. Below is a configuration you posted back on Jan. 28th. This example is essentially what I’m trying to do.

    Code:
    	<int:channel id="errorChannelA"/>
    	
    	<int:service-activator input-channel="errorChannelA" expression="'ERROR from errorChannelA'"/>
    		
    	<int:channel id="errorChannelB"/>
    	
    	<int:service-activator input-channel="errorChannelB" expression="'ERROR from errorChannelB'"/>
    		
    
    	<int:gateway id="testGateway" 
    	             service-interface="org.springframework.integration.gateway.InnerGatewayWithChainTests.TestGateway"
    	             default-request-channel="requestChannelA"
    	             error-channel="errorChannelA"/>
    	                    
    	<int:chain input-channel="requestChannelA">
    		<int:service-activator expression="1/(payload-5)"/>
    		<int:gateway request-channel="requestChannelB" error-channel="errorChannelB"/>
    	</int:chain>
    	
    	<int:service-activator input-channel="requestChannelB" expression="10/payload"/>
    The above example works as I’d expect, but if you make a small change to wrap the handling of the message from errorChannelB in a chain, then you should see the behaivor that I’m seeing.

    Code:
    	<int:channel id="errorChannelA"/>
    	
    	<int:service-activator input-channel="errorChannelA" expression="'ERROR from errorChannelA'"/>
    		
    	<int:channel id="errorChannelB"/>
    	
    	<int:chain input-channel=”errorChannelB”>
    		<int:service-activator expression="'ERROR from errorChannelB'"/>
    	</int:chain>
    		
    
    	<int:gateway id="testGateway" 
    	             service-interface="org.springframework.integration.gateway.InnerGatewayWithChainTests.TestGateway"
    	             default-request-channel="requestChannelA"
    	             error-channel="errorChannelA"/>
    	                    
    	<int:chain input-channel="requestChannelA">
    		<int:service-activator expression="1/(payload-5)"/>
    		<int:gateway request-channel="requestChannelB" error-channel="errorChannelB"/>
    	</int:chain>
    	
    	<int:service-activator input-channel="requestChannelB" expression="10/payload"/>
    I know from reading section 7.2 of the Spring Integration Reference Manual that there are times that the expected behavior of a Gateway is for the Gateway method call to hang indefinitely, but I can’t determine why changing the from being handled by a service-activator to a chain should cause this behavior.

  7. #7
    Join Date
    Mar 2011
    Posts
    22

    Default

    What information should I provide to more this forward?

  8. #8
    Join Date
    Jan 2008
    Location
    Mohnton, PA USA (that's near Philadelphia)
    Posts
    2,148

    Default

    John

    Sorry about not replying to you earlier. Few of use have been on the road for the past month and some of the posts slipped through the cracks.
    Let me look at this one and i'll follow up.

  9. #9
    Join Date
    Jan 2008
    Location
    Mohnton, PA USA (that's near Philadelphia)
    Posts
    2,148

    Default

    John

    I am successfully executing your "modified" configuration. In other words the modification where you encapsulated service-activator for 'errorChannelB' in the chain works fine and gives me the same results.
    Could you please provide more info (invoking code, the version of SI you are using etc.)

  10. #10
    Join Date
    Mar 2011
    Posts
    22

    Default

    I'm using SI 2.0.3, and the invoking code is a JMS inbound-channel-adapter.

    So it seems that I figured out what I was doing wrong. In the error flow, the last step was is a JMS outbound-channel-adapter writing a message to an error queue. It appears that no message is returned from the outbound-channel-adapter which breaks the contract with the gateway.

    Making the following small changes to the error flow eliminate the undesired behavior.

    1.) Have the service-activator prior to the outbound-channel-adapter send messages to a publish-subscribe-channel instead of a direct channel.
    2.) Have the outbound-channel-adapter subscribe to the aforementioned channel.
    3.) Have another service-activator (newServiceActivator) subscribe to the same channel that the outbound-channel-adapter is subscribed.
    4.) Implement the newServiceActivator to simply return the object that it receives.

    Having the newServiceActivator return an object appears to fulfill the gateway contract and everything works as expected.

    To see the behavior that I've described you should be able to add an outbound-channel-adapter to the configuration above. I found that the easiest outbound-channel-adapter was the stderr-channel-adapter.

    Do you agree with my conclusions above? Is there a better approach to working with the gateway contract?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •