How error handling works in Spring Integration
Integration, Spring ·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:
- syncChannel: A direct channel that will send the order to an order processor subscribed to this channel.
- asyncChannel: A queue channel from which the order processor will actively retrieve the order.
Once the order is processed, an order confirmation will be sent back to the gateway. Here is a graphic representing this:
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); } } }