Proxy Web services request with Camel

In this tutorial we will show how to proxy a request to a legacy JAX-WS Web service using Camel.

A common scenario is that you have some legacy code (say some Web services) which cannot be reused becuase the interfaces or some conventions (e.g. namespaces) have changed. Proxing request to legacy code can also be a need when you need to monitor or debug some functions which are available on a middleware.

In the Camel quickstarts https://github.com/apache/camel/tree/master/examples we can find an example named camel-example-cxf-proxy that implements this pattern. I will adapt this example to simulate a real world scenario, such as the following one:

apache camel web services

Let's say you have this Legacy Web service implementation running on JBoss EAP 6 (or WildFly 8):

package org.apache.camel.example.cxf.proxy;

import org.apache.camel.example.reportincident.InputReportIncident;
import org.apache.camel.example.reportincident.OutputReportIncident;
import org.apache.camel.example.reportincident.ReportIncidentEndpoint;

@WebService(targetNamespace = "http://reportincident.example.camel.apache.org", name = "ReportIncidentEndpoint")
public class ReportIncidentEndpointService implements ReportIncidentEndpoint {

    public OutputReportIncident reportIncident(InputReportIncident in) {
   
        System.out.println("\n\n\nInvoked real web service: id=" + in.getIncidentId()
                + " by " + in.getGivenName() + " " + in.getFamilyName() + "\n\n\n");

        OutputReportIncident out = new OutputReportIncident();
        out.setCode("OK;" + in.getIncidentId());
        return out;
    }
    
 

}

The structure of the InputReportIncident is stated in the WSDL as follows:

<xs:complexType>
   <xs:sequence>
	<xs:element name="incidentId" type="xs:string"/>
	<xs:element name="incidentDate" type="xs:string"/>
	<xs:element name="givenName" type="xs:string"/>
	<xs:element name="familyName" type="xs:string"/>
	<xs:element name="summary" type="xs:string"/>
	<xs:element name="details" type="xs:string"/>
	<xs:element name="email" type="xs:string"/>
	<xs:element name="phone" type="xs:string"/>
   </xs:sequence>
</xs:complexType>

Now we suppose that the structure has changed so that "givenName" is now named "firstName":

<xs:complexType>
   <xs:sequence>
	<xs:element name="incidentId" type="xs:string"/>
	<xs:element name="incidentDate" type="xs:string"/>
	<xs:element name="firstName" type="xs:string"/>
	<xs:element name="familyName" type="xs:string"/>
	<xs:element name="summary" type="xs:string"/>
	<xs:element name="details" type="xs:string"/>
	<xs:element name="email" type="xs:string"/>
	<xs:element name="phone" type="xs:string"/>
   </xs:sequence>
</xs:complexType>

How can Camel help us on it ? in a simple way, we will expose the Webservice using Camel through the cxf:cxfEndpoint. We will then define a target endpoint available on EAP 6 at: http://localhost:8080/targetWS/ReportIncidentEndpoint

We use a simple Java Bean to manipulate the content of the SOAP packet, so that the Web service is compatible with the legacy Web service. Here is the camel-context.xml file that we will use:

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

  
  <context:property-placeholder location="classpath:incident.properties,file:target/custom.properties"
                                ignore-resource-not-found="true"/>

 

  <!-- bean that enriches the SOAP request -->
  <bean id="enrichBean" class="org.apache.camel.example.cxf.proxy.EnrichBean"/>

  <!-- this is the CXF web service we use as the front end -->
  <cxf:cxfEndpoint id="reportIncident"
                   address="http://localhost:${proxy.port}/camel-example-cxf-proxy/webservices/incident"
                   endpointName="s:ReportIncidentEndpoint"
                   serviceName="s:ReportIncidentEndpointService"
                   wsdlURL="etc/report_incident.wsdl"
                   xmlns:s="http://reportincident.example.camel.apache.org"/>

  <!-- this is the Camel route which proxies the real web service and forwards SOAP requests to it -->
  <camelContext xmlns="http://camel.apache.org/schema/spring">

    <!-- property which contains port number -->
    <propertyPlaceholder id="properties" location="classpath:incident.properties,file:target/custom.properties"/>

    <endpoint id="callRealWebService" uri="http://localhost:8080/targetWS/ReportIncidentEndpoint"/>

    <route>
      <!-- CXF consumer using MESSAGE format -->
      <from uri="cxf:bean:reportIncident?dataFormat=MESSAGE"/>
      <!-- log input received -->
      <to uri="log:input"/>
      <!-- enrich the input by ensure the incidentId parameter is set -->
      <to uri="bean:enrichBean"/>
      <!-- Need to remove the http headers which could confuse the http endpoint -->
      <removeHeaders pattern="CamelHttp*"/>
      <!-- send proxied request to real web service -->
      <to ref="callRealWebService"/>
      <!-- log answer from real web service -->
      <to uri="log:output"/>
    </route>

  </camelContext>

</beans>

We just need to code the EnrichBean. This class actually replaces the node named "givenName" with "firstName". I have added some log trace of the SOAP packets to show excatly what's going on.

package org.apache.camel.example.cxf.proxy;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class EnrichBean {

    public Document enrich(Document doc) {
 
        try {
            System.out.println("Before =============");
            printDocument(doc, System.out);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        // Modify SOAP Request
        NodeList nodes = doc.getElementsByTagName("givenName");

        doc.renameNode(nodes.item(0), null, "firstName");

        try {
            System.out.println("After =============");
            printDocument(doc, System.out);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return doc;
    }

    public static void printDocument(Document doc, OutputStream out) throws IOException, TransformerException {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

        transformer.transform(new DOMSource(doc),
                new StreamResult(new OutputStreamWriter(out, "UTF-8")));
    }

}

Compile the project with:

mvn clear compile

Execute the Camel route with:

mvn camel:run

You should see the Web service is available at: http://localhost:9080/camel-example-cxf-proxy/webservices/incident?wsdl

Now launch any Web service client like SOAP UI which will assist you in creating a sample SOAP request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:rep="http://reportincident.example.camel.apache.org">
   <soapenv:Header/>
   <soapenv:Body>
      <rep:inputReportIncident>
         <incidentId>1</incidentId>
         <incidentDate>2011-11-18</incidentDate>
         <givenName>Bob</givenName>
         <familyName>Smith</familyName>
         <summary>Bla bla</summary>
         <details>More bla</details>
         <email>mymail</email>
         <phone>12345678</phone>
      </rep:inputReportIncident>
   </soapenv:Body>
</soapenv:Envelope>

The JAX-WS legacy Webservice running on JBoss will intercept the request and return a SOAP OK response:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:outputReportIncident xmlns:ns2="http://reportincident.example.camel.apache.org">
         <code>OK;1</code>
      </ns2:outputReportIncident>
   </soap:Body>
</soap:Envelope>

Do you want to see how to deploy the Project as OSGI bundle on JBoss Fuse ? Then check the next tutorial: Mediate Web Services request with JBoss Fuse


Advertisement

Cookie Alert