Saturday 29 July 2017

SOAP client Custom Timeout


The interceptor-based solution provides a very flexible way of configuring timeouts for Apache CXF based SOAP clients.For custom timeout configuration per operation(Methods defined inside wsdl), we will use an interceptor that sets a MessageProperty. It must be configured in the right Phase, otherwise, the MessageProperty might not have an effect. 


Java Interceptor : 

package com.interceptor;

import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.cxf.interceptor.MessageSenderInterceptor;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;


/**
 * <p>
 * An SOAPCustomTimeoutInterceptor {@link AbstractPhaseInterceptor} that
 * timeout based on the QName of the operation.
 * </p>
 *
 */
public class SOAPCustomTimeoutInterceptor extends AbstractPhaseInterceptor<Message> {


  /** Keep all timeouts per OperationName in milliseconds */
  private Map<QName, Long> receiveTimeoutByOperationName = new HashMap<QName, Long>();

  public SOAPCustomTimeoutInterceptor(String phase) {
    super(phase);
    addBefore(MessageSenderInterceptor.class.getName());
  }

  public SOAPCustomTimeoutInterceptor() {
    this(Phase.PREPARE_SEND);
  }

  /**
   * Setting timeout based on QName in message
   * 
   * @param message
   */
  @Override
  public void handleMessage(Message message) {
    final QName operationName = message.getExchange().getBindingOperationInfo().getName();
    final Long receiveTimeout = receiveTimeoutByOperationName.get(operationName);
    if (receiveTimeout != null) {
      message.put(Message.RECEIVE_TIMEOUT, receiveTimeout);
    }
  }

  public void setReceiveTimeoutByOperationName(Map<QName, Long>receiveTimeoutByOperationName) {
    this.receiveTimeoutByOperationName = receiveTimeoutByOperationName;
  }
}

This handleMessage takes a Map of operation QNames and timeout-values. If it finds a match, it configures the timeout, otherwise, it does nothing.

Spring Configuration :

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:cxf="http://camel.apache.org/schema/cxf"
xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/cxf
http://camel.apache.org/schema/cxf/camel-cxf.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd 
http://cxf.apache.org/transports/http/configuration 
http://cxf.apache.org/schemas/configuration/http-conf.xsd">

<cxf:cxfEndpoint id="bookService"
address="http://locathost:8080/someServiceName"
serviceClass="someServiceClass" />
<cxf:outInterceptors>
<ref bean="timeoutPerOperation" />
</cxf:outInterceptors>
</cxf:cxfEndpoint>

<bean  id="timeoutPerOperation" class="sample.camel.CustomTimeoutInterceptor"
<property name="receiveTimeoutByOperationName">
<map key-type="javax.xml.namespace.QName" value-type="java.lang.Long">
<entry value="600">
<key>
<bean class="javax.xml.namespace.QName" factory-method="valueOf">
<constructor-arg value="{http://tempuri.org/}someMethod" />
</bean>
</key>
</entry>
</map>
</property>
</bean>   

</beans>

Above configuration defines timeout per operation wise.You can add N entries inside the map with different with operations.{http://tempuri.org/} - is target namespace defined in WSDL file.tns defines the target name space.
example : xmlns:tns="http://tempuri.org/"
someMethod - is operation name defined in WSDL file.Inside portType name tag, you can find all opertion name(method name).

SOAP client Custom Timeouts using Apache CXF

Apache CXF is a convenient services framework, e.g. for integrating SOAP-based web services in a Java application. It offers SpringFramework integration, which together takes away much complexity of using such services.We can achieve time out for SOAP client using Conduits in Apache CXF.
Conduit simply means channel or pipe. HTTP conduits are channels allow us to apply certain HTTP related properties or attributes which can affect the way messages are exchanged between endpoints. The conduit is always defined by the client or consumer of the service.

Conduit configuration in Spring DSL :

In order to use the HTTP configuration elements, you will need to add the lines shown below to the beans element of your endpoint's configuration file. In addition, you will need to add the configuration elements' namespace to the xsi:schemaLocation attribute.

<beans ...
       ...
       xsi:schemaLocation="...
       ...">

You configure an HTTP client using the http-conf:conduit element and its children. The http-conf:conduit element takes a single attribute, name, that specifies the WSDL port element that corresponds to the endpoint. The value for the name attribute takes the form portQName.http-conduit.All the ReceiveTimeouts specified in milliseconds.

 <http-conf:conduit name="*.http-conduit">
  <!-- you can also using the wild card to specify
       the http-conduit that you want to configure -->

 <!-- This configuration will set global timeout for all SOAP calls -->

 <http-conf:client ReceiveTimeout="50" />
  </http-conf:conduit>
    ...
  </http-conf:conduit>

 <http-conf:conduit name="{http://widgets/widgetvendor.net}widgetSOAPPort.http-conduit">
 <!-- This configuration will set Service based timeout for SOAP calls -->
<!--Add configuration for an endpoint that is specified by the WSDL fragment.
http://widgets.widgetvendor.net is wsdl target namespace and
widgetSOAPPort is the operation specified in WSDL inside portType name tag name-->
     <http-conf:client ReceiveTimeout="50" />
  </http-conf:conduit>
  <http-conf:conduit name="http://localhost:8080/service14.*">
<!--Add configuration for an endpoint that is specified by the WSDL fragment.
http://localhost:8080/service14 is wsdl service address location-->
 <!-- This configuration will also set Service based timeout for SOAP calls -->
    <http-conf:client ReceiveTimeout="50" />
  </http-conf:conduit>

Based on above configuration we can set the timeout for SOAP clients globally and Services wise.
We can't set the timeout on operation wise(Methods specified inside each service) use of conduit.