How error handling works in Spring Integration

The target of this post is to show you how error handling works in Spring Integration, using the messaging system. You will see that error handling is different between synchronous and asynchronous messaging. As usual, I’ll skip the chat and proceed with some examples.

You can get the source code at my Github repository

1 The sample application

I will use a basic example, since I want to focus on exception handling. The application consists in an order service. It receives an order, processes it and returns a confirmation.

Below we can see how the messaging system is configured:

int-config.xml

<context:component-scan base-package="xpadro.spring.integration"/>

<int:gateway default-request-channel="requestChannel" service-interface="xpadro.spring.integration.service.OrderService"/>

<int:channel id="requestChannel"/>

<int:router input-channel="requestChannel" ref="orderRouter" method="redirectOrder"/>

<int:channel id="syncChannel"/>

<int:channel id="asyncChannel">
    <int:queue capacity="5"/>
</int:channel>

<int:service-activator method="processOrder" input-channel="syncChannel" ref="orderProcessor"/>

<int:service-activator method="processOrder" input-channel="asyncChannel" ref="orderProcessor">
    <int:poller fixed-delay="2000"/>
</int:service-activator>

 

The gateway is the entry point of the messaging system. It will receive the order and send it to the direct channel “requestChannel”. There, a router will redirect it to the appropriate channel based on the order id:

Once the order is processed, an order confirmation will be sent back to the gateway. Here is a graphic representing this:

Integration flow about how error handling works in Spring Integration

Ok, let’s start with the simplest case, synchronous sending using a Direct Channel.

2 Synchronous sending with Direct channel

The order processor is subscribed to the “syncChannel” Direct Channel. The “processOrder” method will be invoked in the sender’s thread:

public OrderConfirmation processOrder(Order order) {
    logger.info("Processing order {}", order.getId());
    
    if (isInvalidOrder(order)) {
        logger.info("Error while processing order [{}]", ERROR_INVALID_ID);
        throw new InvalidOrderException(ERROR_INVALID_ID);
    }
    
    return new OrderConfirmation("confirmed");
}

 

Now, we will implement a test that will provoke an exception by sending an invalid order. This test will send an order to the gateway:

public interface OrderService {
    @Gateway
    public OrderConfirmation sendOrder(Order order);
}

 

Implement the test

TestSyncErrorHandling.java

@ContextConfiguration(locations = {"/xpadro/spring/integration/config/int-config.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TestSyncErrorHandling {
    
    @Autowired
    private OrderService service;
    
    @Test
    public void testCorrectOrder() {
        OrderConfirmation confirmation = service.sendOrder(new Order(3, "a correct order"));
        Assert.assertNotNull(confirmation);
        Assert.assertEquals("confirmed", confirmation.getId());
    }
    
    @Test
    public void testSyncErrorHandling() {
        OrderConfirmation confirmation = null;
        try {
            confirmation = service.sendOrder(new Order(1, "an invalid order"));
            Assert.fail("Should throw a MessageHandlingException");
        } catch (MessageHandlingException e) {
            Assert.assertEquals(InvalidOrderException.class, e.getCause().getClass());
            Assert.assertNull(confirmation);
        }
    }
}