About Xavier Padro

Xavier is a software developer working in a consulting firm based in Barcelona. He is specialized in web application development with experience in both frontend and backend. He is interested in everything related to Java and the Spring framework

Retry web service operations with RequestHandlerRetryAdvice

1.Introduction

Sometimes when invoking a web service, we may be interested in retrying the operation in case an error occurs. When using Spring Integration, we can achieve this functionality with RequestHandlerRetryAdvice class. This class will allow us to retry the operation for a specified number of times before giving up and throwing an exception. This post will show you how to accomplish this.

The test application will invoke a web service and if it fails to respond, it will wait for a specified time and try it again until it receives a response or it reaches a retry limit. If the limit is reached, the failed request will be stored into a database. Mainly, this post shows an example of the following:

The source code of the application can be found at github.

You can also get the source code of the web service project that is called by the application at github.

2.Web service invocation

Use case: The client invokes the web service and receives a response.

grafic1

The request enters the messaging system through the “system entry” gateway. It then reaches the outbound gateway, invokes the web service and waits for the response. Once received, the response is sent to the response channel.

The above image is the result of this configuration:

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

<!-- Initial service request -->
<int:gateway id="systemEntry" default-request-channel="requestChannel"
    service-interface="xpadro.spring.integration.gateway.ClientService" />
    
<int:channel id="requestChannel">
    <int:queue />
</int:channel>

<int-ws:outbound-gateway id="marshallingGateway"
    request-channel="requestChannel" reply-channel="responseChannel"
    uri="http://localhost:8080/spring-ws/orders" marshaller="marshaller"
    unmarshaller="marshaller">
    
    <int:poller fixed-rate="500" />
</int-ws:outbound-gateway>

<oxm:jaxb2-marshaller id="marshaller" contextPath="xpadro.spring.integration.types" />


<!-- Service is running - Response received -->
<int:channel id="responseChannel" />

<int:service-activator ref="clientServiceActivator" method="handleServiceResult" input-channel="responseChannel" />

Mapped to the response channel there’s a service activator which just logs the result.

TestInvocation.java: Sends the request to the entry gateway

@ContextConfiguration({"classpath:xpadro/spring/integration/config/int-config.xml",
    "classpath:xpadro/spring/integration/config/mongodb-config.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class TestInvocation {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Autowired
    private ClientService service;
    
    @Test
    public void testInvocation() throws InterruptedException, ExecutionException {
        logger.info("Initiating service request...");
        
        ClientDataRequest request = new ClientDataRequest();
        request.setClientId("123");
        request.setProductId("XA-55");
        request.setQuantity(new BigInteger("5"));

        service.invoke(request);
        
        logger.info("Doing other stuff...");
        Thread.sleep(60000);
    }
}

With this configuration, if the service invocation fails, a MessagingException will be raised and sent to the error channel. In the next section, we will add the retry configuration.

3.Adding the retry advice

Use case: The initial request failed because the service is not active. We will retry the operation until a response is received from the service.

In this case, we need to add the retry advice to the web service outbound gateway:

<int-ws:outbound-gateway id="marshallingGateway" interceptor="clientInterceptor"
    request-channel="requestChannel" reply-channel="responseChannel"
    uri="http://localhost:8080/spring-ws/orders" marshaller="marshaller"
    unmarshaller="marshaller">
    
    <int:poller fixed-rate="500" />
    
    <int-ws:request-handler-advice-chain>
        <ref bean="retryAdvice" />
    </int-ws:request-handler-advice-chain>
</int-ws:outbound-gateway>

Now the web service outbound gateway will delegate the invocation to the retry advice, which will try the operation as many times as specified until it gets a response from the service. Let’s define the retry advice:

<bean id="retryAdvice" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice" >
    <property name="retryTemplate">
        <bean class="org.springframework.retry.support.RetryTemplate">
            <property name="backOffPolicy">
                <bean class="org.springframework.retry.backoff.FixedBackOffPolicy">
                    <property name="backOffPeriod" value="4000" />
                </bean>
            </property>
            <property name="retryPolicy">
                <bean class="org.springframework.retry.policy.SimpleRetryPolicy">
                    <property name="maxAttempts" value="4" />
                </bean>
            </property>
        </bean>
    </property>
</bean>

To accomplish its objective, the advice uses a RetryTemplate, which is provided by the Spring Retry project. We can customize its behavior by defining backoff and retry policies.

Backoff policy: Establishes a period of time between each retry. The more interesting types are:

  • FixedBackOffPolicy: Used in our example. It will wait for the same specified amount of time between each retry.
  • ExponentialBackOffPolicy: Starting with a determined amount of time, it will double the time on each retry. You can change the default behavior by establishing a multiplier.

Retry policy: Establishes how many times will retry the failed operation. Some of the types:

  • SimpleRetryPolicy: Used in our example. Specifies a retry attempts limit.
  • ExceptionClassifierRetryPolicy: Allows us to establish a different maxAttempts depending on the exception raised.
  • TimeoutRetryPolicy: Will keep retrying until a timeout is reached.

4.No luck, logging the failed request

Use case: The service won’t recover, storing the request to the database.

adv1

The final part of the configuration is the following:

<!-- Log failed invocation -->
<int:service-activator ref="clientServiceActivator" method="handleFailedInvocation" input-channel="errorChannel" output-channel="logChannel" />

<int:channel id="logChannel" />

<bean id="mongoDbFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
    <constructor-arg>
        <bean class="com.mongodb.Mongo"/>
    </constructor-arg>
    <constructor-arg value="test"/>
</bean>

<int-mongodb:outbound-channel-adapter id="mongodbAdapter" channel="logChannel"
    collection-name="failedRequests" mongodb-factory="mongoDbFactory" />

The service activator subscribed to the error channel will retrieve the failed message and send it to the outbound adapter, which will insert it to a mongoDB database. The service activator:

public Message<?> handleFailedInvocation(MessagingException exception) {
    logger.info("Failed to succesfully invoke service. Logging to DB...");
    return exception.getFailedMessage();
}

If we not succeed in obtaining a response from the service, the request will be stored into the database:

mongodb

6.Conclusion

We’ve learnt how Spring Integration gets support from the Spring Retry project in order to achieve retry of operations. We’ve used the int-ws:request-handler-advice-chain, but the ‘int’ namespace also supports this element to add this functionality to other types of endpoints.
 

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


four − = 3



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books