Logo Computer scientist,
engineer, and educator
• Articles • Articles about computing • Articles about software development • Apache integration software

Creating a Web Service using Apache CXF from the ground up

This article describes how to create a Java Web Service and simple test client using the Apache CXF framework, a text editor and simple command-line tools — no IDEs, no Maven, no Ant. My purpose is to demonstrate what's going on behind the scenes when you use all these fancy tools, and to make it easier for people who don't want to use such tools to get started with CXF.

The specific command-line examples I give are for Linux, because that's what I use for development work. However, they should work on other platforms, provided you know how to run java and javac at the command line.

I'm assuming that the reader has a good working knowledge of Java, and knows at least a little about WSDL and SOAP. I'll be pointing out how entries in the WSDL file correspond to the Java source code, but there isn't space to explain WSDL in detail.

Overview

Preliminary stuff

To run the example in this article you will need the Sun/Oracle Java JDK1.7. CXF will work with earlier Java versions, but these require some configuration changes which I don't have space to document. You'll need Apache CXF of course. I'm using version 2.7.7, but the exact version should not be critical. I'm assuming you have installed this — installation is simply a matter of getting the binary distribution of CXF from the Apache CXF download page, and unpacking it in any convenient directory.

Outline of the example

This example builds a Web Service implementation and command-line client from a WSDL file. CXF provides tools to work the other way around — to construct a WSDL file from Java interfaces, but starting with WSDL is arguably more general. The WSDL file will be used to create the skeleton of a Web Service implementation, which we then fill in with Java code (trivial Java code, in this case). We'll also embed the implementation into a Java class that runs the embedded HTTP server in CXF; this relieves us of the need to install a separate Web server and build WAR files, etc.

After testing the Service using a browser, we'll construct a simple test client that will invoke it from the command line.

The Web service is very simple — it has only one operation, which takes a string argument and returns a string value.

Directory structure

We'll need to create a directory structure, so that files that are generated by the compiler and CXF tools end up in useful places. Because the server and client are, in principle, entirely separate apart from having a WSDL file in common, in this example I am suggesting a directory structure with separate client and server directories, like this:
server
   src          # Source files for the Web services server
   target  
       classes  # Generated classes for the server

client
   src          # Source files for the Web services client 
   target  
       classes  # Generated classes for the client 

build
   src          # Java generated by the wsdl2java tool goes here

wsld            # WSDL goes here
Within each src directory, there will be a further directory hierarchy, to match the Java package structure. In this example, all the files are in the package net.kevinboone.cxftest, and I've organized the src directories accordingly. Compiled classes will end up in the classes directory, and in this case the directory structure will automatically match the package structure, because that's how the Java compiler works by default. Similarly, the Java code generated by the wsdl2java utility will end up under build/src and, again, with a sub-structure that matches the package hierarchy.

All the source code, with the appropriate directory structure, is included in the download bundle (at the end of this article).

Step 1: The WSDL file

In this example the starting point for both client and server will be a WSDL file, stored in the directory wsdl. The WSDL file is listed below — it's about as simple as a WSDL file can be, and still have some useful function. This one defines one Web service, within which is one port, which defines one operation 'testMe', which takes one string argument and returns one string value.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="CXFTest" targetNamespace="http://kevinboone.net/cxftest"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://kevinboone.net/cxftest"
    xmlns:types="http://kevinboone.net/cxftest/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <wsdl:types>
        <schema targetNamespace="http://kevinboone.net/cxftest/types"
            xmlns="http://www.w3.org/2001/XMLSchema"
            xmlns:tns="http://kevinboone.net/cxftest/types"
            elementFormDefault="qualified">

            <element name="testMe">
                <complexType>
                    <sequence>
                        <element name="requestType" type="string"/>
                    </sequence>
                </complexType>
            </element>
            <element name="testMeResponse">
                <complexType>
                    <sequence>
                        <element name="responseType" type="string"/>
                    </sequence>
                </complexType>
            </element>
        </schema>
    </wsdl:types>
    <wsdl:message name="testMeRequest">
        <wsdl:part element="types:testMe" name="in"/>
    </wsdl:message>
    <wsdl:message name="testMeResponse">
        <wsdl:part element="types:testMeResponse" name="out"/>
    </wsdl:message>

    <wsdl:portType name="testmePort">
        <wsdl:operation name="testMe">
            <wsdl:input message="tns:testMeRequest" name="testMeRequest"/>
            <wsdl:output message="tns:testMeResponse" name="testMeResponse"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="testMe_SOAPBinding" type="tns:testmePort">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="testMe">
            <soap:operation soapAction="" style="document"/>
            <wsdl:input name="testMeRequest">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="testMeResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="CXFTestService">
        <wsdl:port binding="tns:testMe_SOAPBinding" name="TestMeSoapPort">
            <soap:address location="http://lars.bears:9001/cxftest/testme"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

You'll probably need to change at least the line
 <soap:address location="http://lars.bears:9001/cxftest/testme"/>
to match the hostname of your computer, and possibly the port number as well. If you're testing with the client and server on the same machine, then it's OK to specify localhost as the hostname but, of course, that wouldn't work in a distributed environment.

Step 2: Implementing the Web Service and supporting infrastructure

This section describes how to generate the necessary Java artefacts from the WSDL file, and then implement the Web Service in Java, using the CXF embedded HTTP server.

Generating Web Services infrastructure from WSDL

[I should point out that this step is necessary for both the client and the Web Service implementation. However, the same generated files will serve both client and server, so there's no particular need to perform it twice.]

To make use of the WSDL file, we must generate Java 'plumbing' code that represents the service, the port, and the data types specified in the WSDL. With the CXF framework, this is accomplished using the wsdl2java utility, which takes a WSDL file as input, and writes its output to the directory specified by the -d parameter (build/src, in this case).

$ /path/to/CXF/bin/wsdl2java -d build/src wsdl/cxftest.wsdl
Naturally, you'll want to replace /path/to/CXF with the specific location you installed Apache CXF.

Let's look at the files generated by this utility.
CXFTestService.java Models the whole Web Service; contains getXXX() methods by clients can obtain a reference to a specific port; defines constants that can be used to refer to the service and the port in API calls.
TestmePort.java A Java interface that represents the specific port type called testme, as defined in the WSDL file. The Web service implementation class will provide the concrete implementation of the methods in this class by implementing this interface. Clients of the Web Service will obtain a reference to a stub generated by the CXF framework that implements the methods in this interface. The practical result is that both client and server see an implementation of this interface although, of course, it is a different implementation in each case.
types/TestMe.java A Java class that wraps the data type specified as the input parameter to the service — in this case a simple string
types/TestMeResponse.java A Java class that wraps the data type specified as the return value of the service — also a simple string in this case
Some other helper classes are generated; these are not used in this example. In fact, the classes in the types directory are not used either — the CXF framework takes care of marshalling and unmartially between Java and XML types when these are simple types (strings, numbers). Consequently, the interface TestMePort.java specifies parameters and return types as Java Strings, not application-dependent types. However, you might well need these additional type wrapper classes if you're using complex data types.

Implementing the Web Service

We need to provide a Java implementation of all the operations specified in the interface TestmePort — simple enough in this case, as there's only one method. Note that you'll normally need a Java implementation for each WSDL port, not one for the whole service. In this trivial example, the comments are longer than the actual implementation.
/*
Implementation of the Web Service defined in wsdl/cfxtest.wsdl,
(c)2013 Kevin Boone
*/
package net.kevinboone.cxftest;

// This annotation marks this class as the implementation of a Web Service,
//  with a particular specification in WSDL. It is for checking purposes
//  only — the class will build and run without it. The "name"
//  parameter is arbitrary here, as it is not referred to elsewhere. The
//  serviceName and targetNamespace attributes should match the WSDL
@javax.jws.WebService(name = "CXFTestService", serviceName = "CXFTestService",
                      targetNamespace = "http://kevinboone.net/cxftest",
                      wsdlLocation = "file:./wsdl/cxftest.wsdl")

// In this class we provide implementations of the methods found in the
//  interface TestmePort. This interface is generated from the WSDL file
//  using the wsdl2java utility. The one method defined here corresponds
//  to the WSDL definition:
//  <wsdl:portType name="testmePort">
//          <wsdl:operation name="testMe">
// In the WSDL file, this portType definition includes further definitions
//  of the intput and output messages, both consisting of exactly one
//  string in this case. 
public class CXFTestImpl implements TestmePort
  {
    public String testMe (String in) {
        System.out.println ("Executing operation test");
        System.out.println("Message received: " + in + "\n");
        return "You said: " + in;
    }
}

Providing a server for the implementation class

In a practical Web Service, you would probably deploy the implementation class(es) to a Web server in the form of a WAR file, with a servlet forming the link between the client's invocation and the implementation class. However, Apache CXF is supplied with an embedded HTTP server which can be used for simple testing. To make use of this, we need to create a class that invokes the embedded server with the URL on which to serve requests, and the implementation class as created in the previous step. Here is the code.
/*
Server wrapper for the Web Service defined in wsdl/cfxtest.wsdl,
using the CXF embedded Web server
(c)2013 Kevin Boone
*/
package net.kevinboone.cxftest;

import javax.xml.ws.Endpoint;

public class Server
  {
  protected Server() throws Exception
    {
    System.out.println("Starting Server");
    Object implementor = new CXFTestImpl();
    String address = "http://lars.bears:9001/cxftest/testme";
    Endpoint.publish (address, implementor);
    }

  public static void main(String args[]) throws Exception
    {
    new Server();
    System.out.println("Server ready...");
    Thread.sleep(5 * 60 * 1000);
    System.out.println("Server exiting");
    System.exit(0);
    }
}
Because it has a main() method, we'll be able to run this class at the command line. Once started, the Thread.sleep() call will keep the program running for five minutes, and then allow it to quit.

Compiling the server

To compile the server we need to include the Java source code as listed above, and the Java classes generated by the wsdl2java utility. The output in this case will go to server/target/classes.
$ javac  -d server/target/classes build/src/net/kevinboone/cxftest/types/*.java \\
build/src/net/kevinboone/cxftest/*.java server/src/net/kevinboone/cxftest/*.java
Note that the command above should be entered as one long line.

Testing the server

We should now be in a position to run the server, and test it with a Web browser. All the compiled code is in the directory server/target/classes, so this needs to be in the class search path. However, the class search path must also specify the manifest JAR for the CXF framework, cxf-manifest.jar. This JAR file contains references that will link the rest of the framework JARs — you don't need to specify them explicitly on the command line.
$CXF_HOME=/path/to/CXF
$java -classpath server/target/classes:$CXF_HOME/lib/cxf-manifest.jar net.kevinboone.cxftest.Server
INFO: Started SelectChannelConnector@lars.bears:9001
Nov 26, 2013 12:16:02 AM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
...
Server ready...
To test that the server is basically functional, you can try retrieving the service's WSDL file using a Web browser. Since my hostname is lars.bear, I would point the browser at
http://lars.bears/9001/cxftest/testme?wsdl

Step 3: Constructing and testing the client

The client will use the same Java plumbing created using the wsdl2java utility as above; so all we have to do here is write the code that invokes the service.

Coding and compiling the client

Here is the simple client. As was the case for the Web Service implementation class, there is more comment than code in this example.
/*
Simple client for the Web Service defined in wsdl/cxftest.wsdl
(c)2013 Kevin Boone
*/
package net.kevinboone.cxftest;
import java.io.*;
import java.net.*;
import javax.xml.namespace.*;
import net.kevinboone.cxftest.TestmePort;
import net.kevinboone.cxftest.CXFTestService;

public class Client
  {
  public static void main(String args[]) throws Exception
    {
    // Yes, we do still need the WSDL file at runtime, although WS
    //  stubs have been generated from it using the CXF tools
    URL wsdlURL = new URL("file:wsdl/cxftest.wsdl");

    CXFTestService service = new CXFTestService(wsdlURL,
      CXFTestService.SERVICE);

    // TestmePort is a Java interface that defines the service port's
    //  inputs and outputs; generated from the WSDL file by wsdl2java
    TestmePort port = service.getPort(CXFTestService.TestMeSoapPort,
      TestmePort.class);

    System.out.println("Invoking testMe...");

    // The name testMe() comes from the definition of the port type in
    //  the WSDL file:
    //  <wsdl:portType name="testmePort">
    //    <wsdl:operation name="testMe">
    String resp = port.testMe (System.getProperty("user.name"));
    System.out.println("Server responded with: " + resp);
    System.out.println();
    }
  }
When compiling the client, we must include the 'plumbing' classes generated by CXF, as we did for the Web Service implementation:
$ javac  -d client/target/classes build/src/net/kevinboone/cxftest/types/*.java \\ build/src/net/kevinboone/cxftest/*.java client/src/net/kevinboone/cxftest/*.java

Running the client

Ensure that the server process created in Step 2 is running. All the compiled code for the client is in the directory client/target/classes, but you'll need to ensure that the Java class search path includes the CXF manifest JAR:
[kevin@lars cxftest]$ java -classpath client/target/classes:$CXF_HOME/lib/cxf-manifest.jar net.kevinboone.cxftest.Client
Nov 26, 2013 9:55:13 AM net.kevinboone.cxftest.CXFTestService 
INFO: Can not initialize the default wsdl from wsdl/cxftest.wsdl
Invoking testMe...
Server responded with: You said: kevin
You should see a message from the server process as well, saying that the service has been invoked.

Summary

Web Services frameworks are pretty complicated, and most developers use IDE tools that hide the unsightly details. The problem with these tools is that — well, that they hide the unsightly details. This might be good for productivity, but it's generally not so good for implementation. I hope that this article has done a little to demystify the process of implementing Web Services using Apache CXF.

Downloads

Source code bundle

Copyright © 1994-2013 Kevin Boone. Updated May 01 2014