Tutorials on Apache Camel, Apache Kafka, JBoss Fuse

Search our tutorials

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

Creating the Legacy JAX-WS Endpoint

The source code for the Legacy JAX-WS Endpoint is available here: https://github.com/fmarchioni/masteringintegration/tree/master/cxf-proxy/targetWS

This project contains the following JAX-WS Web service:

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>

Let's deploy this Web Service on WildFly. Thanks to the WildFly plugin we can do it with just one command:

$ mvn clean install wildfly:deploy

The Web service will be available at the following Endpoint address:

http://localhost:8080/targetWS/ReportIncidentEndpointService

Creating the Camel CXF Proxy

The source code for this example is avalable at: https://github.com/fmarchioni/masteringintegration/tree/master/cxf-proxy/camel-example-cxf-proxy

Now let's edit the camel-cxf-proxy example from Camel distribution to use as target Web Service the JAX-WS Web service running on WildFly at: http://localhost:8080/targetWS/ReportIncidentEndpoint

We then 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:

<?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: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">

  <!-- spring property placeholder, ignore resource not found as the file resource is for unit testing -->
  <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/ReportIncidentEndpointService"/>

    <route>
      <!-- CXF consumer using MESSAGE format -->
      <from uri="cxf:bean:reportIncident?dataFormat=RAW"/>
      <!-- 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 uri="ref:callRealWebService"/>
      <!-- log answer from real web service -->
      <to uri="log:output"/>
    </route>

  </camelContext>

</beans>

We just need to code the EnrichBean. This class is used to enrich the proxied Web service to ensure the input is valid and add additional information:

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

import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class EnrichBean {

    public Document enrich(Document doc) {
        Node node = doc.getElementsByTagName("incidentId").item(0);
        String incident = node.getTextContent();

        // here we enrich the document by changing the incident id to another value
        // you can of course do a lot more in your use-case
        node.setTextContent("456");
        System.out.println("Incident was " + incident + ", changed to 456");

        return doc;
    }
}

 Finally, we use the Unit Test class from the camel-cxf-proxy example to send a SOAP Request and assert that the return code is correct:


package org.apache.camel.example.reportincident;

import java.io.File;
import java.io.FileOutputStream;

import org.apache.camel.spring.Main;
import org.apache.camel.test.AvailablePortFinder;
import org.apache.camel.util.FileUtil;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;


public class ReportIncidentRoutesTest {

    // should be the same address as we have in our route
    private static String url;

    protected Main main;

    @BeforeClass
    public static void setupFreePort() throws Exception {
        // find a free port number, and write that in the custom.properties file
        // which we will use for the unit tests, to avoid port number in use problems
        int port = AvailablePortFinder.getNextAvailable();
        String s = "proxy.port=" + port;
        int port2 = AvailablePortFinder.getNextAvailable();
        String s2 = "real.port=" + port2;

        File custom = new File("target/custom.properties");
        FileOutputStream fos = new FileOutputStream(custom);
        fos.write(s.getBytes());
        fos.write("\n".getBytes());
        fos.write(s2.getBytes());
        fos.close();

        url = "http://localhost:" + port + "/camel-example-cxf-proxy/webservices/incident";
    }

    @AfterClass
    public static void cleanup() {
        File custom = new File("target/custom.properties");
        FileUtil.deleteFile(custom);
    }

    protected void startCamel() throws Exception {
        if (!"true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"))) {
            main = new Main();
            main.setApplicationContextUri("META-INF/spring/camel-config.xml");
            main.start();
        } else {
            System.out.println("Skipping starting CamelContext as system property skipStartingCamelContext is set to be true.");
        }
    }
    
    protected void stopCamel() throws Exception {
        if (main != null) {
            main.stop();
        }
    }

    protected static ReportIncidentEndpoint createCXFClient() {
        // we use CXF to create a client for us as its easier than JAXWS and works
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.setServiceClass(ReportIncidentEndpoint.class);
        factory.setAddress(url);
        return (ReportIncidentEndpoint) factory.create();
    }

    @Test
    public void testReportIncident() throws Exception {
        // start camel
        startCamel();

        runTest();

        // stop camel
        stopCamel();
    }
    
    protected void runTest() throws Exception {
       
        // create input parameter
        InputReportIncident input = new InputReportIncident();
        input.setIncidentId("123");
        input.setIncidentDate("2008-08-18");
        input.setGivenName("Claus");
        input.setFamilyName("Ibsen");
        input.setSummary("Bla");
        input.setDetails("Bla bla");
        input.setEmail("This email address is being protected from spambots. You need JavaScript enabled to view it.");
        input.setPhone("0045 2962 7576");

        // create the webservice client and send the request
        
        ReportIncidentEndpoint client = createCXFClient();
        OutputReportIncident out = client.reportIncident(input);

        // assert we got a OK back
        Assert.assertEquals("OK;456", out.getCode());
    }
}

 Run the test with:

$ mvn clean install

The test should complete successfully and on the WildFly console you should have the following log:

Invoked real web service: id=456 by Claus Ibsen

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

FREE WildFly Application Server - JBoss - Quarkus - Drools Tutorials