Entity view (Content)

Versioning SOAP Web Services in Mule using CBR and CXF Proxy

By aabed
Jul. 14, 2014

SOAP Web Services in Mule

Mule uses Apache CXF to create and consume SOAP web services; it also supports both contract-first and code-first approaches. Creating a SOAP web service in Mule and exposing it though HTTP channel is very easy.

To create a code-first web service, one needs to define the service interface that specifies which operations it performs; this is done by creating a Java interface for the service and annotating it with JAXB annotations to control which method should be used to expose and what are the parameter names, namespace, etc. Then one should proceed to create a Java component that implements that service interface.

Listings below show both service interface and service implementation Java classes.

import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

@WebService(namespace="http://greeting.appnovation.com")
public interface Greeting {
@WebResult(name="response", partName="response")
String hello(@WebParam(name="name", partName="name") String name);
}
public class GreetingImpl implements Greeting {
public String hello(String name) {
return "Hello " + name;
}
}

 

In the Mule Flow, the service needs to be declared using SOAP message processor and exposed using an HTTP endpoint (Mule support other channel types, such as JMS). In the example below, the SOAP message processor is configured as CXF-Service, and sets the service interface to the Java interface we created earlier. We also define the HTTP parameters (host, port, and path) in the HTTP endpoint.

Figure below shows the Mule Studio Flow diagram along with the flow definition XML. 

<flow name="GreetingFlow" doc:name="GreetingFlow">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="ws/greeting" doc:name="HTTP"/>
<cxf:jaxws-service serviceClass="com.appnovation.mule.Greeting" doc:name="GreetingWS"/>
<component class="com.appnovation.mule.GreetingImpl" doc:name="Greeting"/>
</flow>

 

Modifying the Web Service

After creating our web service, new requirements come up and we need to change the greeting interface to accepting both first and last names instead of just one name parameter. This change will not be backward compatible--and exposing it through the same HTTP URL will break existing clients. We need to create two versions of the web service that will co-exist temporarily until all existing clients are moved to the newer version. To create two versions of the web service, we either publish both web services through different endpoint URLs, or use different SOAP namespace URL for each service or both.

For the newer version, we will create another set of service interface and implementation based on the new requirements. After that, exposing it through a different endpoint URL couldn not be any easier--just create another flow with SOAP message processor that points to the new class/interface, and expose it using an HTTP endpoint with a different URL (you can just change the path, and keep the server and port the same as version 1.0).

If we would like to keep the same HTTP URL for both old and new versions, and redirect the request based on the namespace URL, we need to use a content based router.

First, we need to change the old version service endpoint URL and namespace URI into a different one (for example namespace = "v1.greeting.appnovation.com" and URL = "/ws/greeting/v1"). Then, we will create a new flow with the original endpoint URL that uses a content based router to redirect the request either to the new or old version service based on the namespace URL. This is done by passing the web service request message through a CXF proxy message processor which allows one to access the SOAP envelop. After, we will use XPath for evaluating the namespace URL in the content based router.

Figure below shows the Mule Studio Flow diagram along with the flow definition XML. 

<flow name="GreetingFlow" doc:name="GreetingFlow">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="ws/greeting" doc:name="HTTP"/>
<cxf:proxy-service payload="body" doc:name="SOAP Proxy Service"/>
<choice doc:name="Choice">
<when expression="xpath://*[contains(namespace-uri(), 'v1.greeting.appnovation.com')]">
<cxf:proxy-client payload="body" doc:name="SOAP Proxy Client"/>
<http:outbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="ws/greeting/v1" method="POST" doc:name="GreetingWS V1.0"/>
</when>
<when expression="xpath://*[contains(namespace-uri(), 'v2.greeting.appnovation.com')]">
<cxf:proxy-client payload="body" doc:name="SOAP Proxy Client"/>
<http:outbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="ws/greeting/v2" method="POST" doc:name="GreetingWS V2.0"/>
</when>
<otherwise>
<set-property propertyName="http.status" value="404" doc:name="Property"/>
</otherwise>
</choice>
</flow>

The old and new version web service flows will look like the following:

<flow name="GreetingV1Flow" doc:name="GreetingV1Flow">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="ws/greeting/v1" doc:name="HTTP"/>
<cxf:jaxws-service serviceClass="com.appnovation.mule.v1.Greeting" doc:name="GreetingWS V1.0"/>
<component class="com.appnovation.mule.v1.GreetingImpl" doc:name="Greeting"/>
</flow>
<flow name="GreetingV2FLow" doc:name="GreetingV2FLow">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="ws/greeting/v2" doc:name="HTTP"/>
<cxf:jaxws-service serviceClass="com.appnovation.mule.v2.Greeting" doc:name="GreetingWS V2.0"/>
<component class="com.appnovation.mule.v2.GreetingImpl" doc:name="Greeting"/>
</flow>

 

Post Tags: