Category Archives: CXF

Creating a CXF client which can get JSON or XML

In the previous post we created a server which serves up JSON or XML via a REST style interface and whilst we also demonstrated calling it from a browser, we really now need to look at implementing a compatible client.

POM first

Create a pom.xml and copy the following into it (within your client project folder)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.putridparrot.client</groupId>
    <artifactId>ppclient</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>ppclient</name>
    <url>http://maven.apache.org</url>

    <properties>
        <cxf.version>2.7.18</cxf.version>
        <httpclient.version>4.3.6</httpclient.version>
        <surefire.version>2.19.1</surefire.version>
        <jackson.version>1.9.13</jackson.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>${httpclient.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-jaxrs</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>
</project>

Now we create our folder structure, so create the following from the root folder

src/main/java

Select the java folder in IntelliJ and Mark Directory As Sources Root.

We also need to copy the previously generated Java files (via XJC) into our java folder.

Now we’re going to use a proxy to access our service, but we’ll still need a representation of the service. This is another good reason why we separated the interface for the service from the implementation, so copy the interface from the server into our java folder (here’s the code)


import com.putridparrot.sample.PurchaseOrderType;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBElement;

@Path("service")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public interface SampleService {
    @GET
    @Path("purchaseOrder")
    public PurchaseOrderType getPurchaseOrder();
}

Nothing new here, so let’s move onto the actual client code. Add a new class, mine’s ClientApplication and it’s listed below

import com.putridparrot.sample.PurchaseOrderType;
import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBElement;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class ClientApplication {
    public static void main(String[] args) throws IOException {

        List<Object> providers = new ArrayList<Object>();
        providers.add(new JAXBElementProvider());
        providers.add(new JacksonJsonProvider());

        SampleService service = JAXRSClientFactory.create(
           "http://localhost:9000", 
           SampleService.class, 
           providers);

        WebClient.client(service)
           .type(MediaType.APPLICATION_XML_TYPE)
           .accept(MediaType.APPLICATION_XML_TYPE);

        PurchaseOrderType request = service.getPurchaseOrder();

        System.out.println(request.getBillTo().getName());
        System.in.read();
    }
}

Now, in the above we’re again supplying providers for the different types, but this is solely for us to test switching between them, if you’re solely interested in XML then remove the providers, but you’ll still need to specify XML as the accept type.

To switch to JSON as the type returned, we simply switch replace the WebClient code with

WebClient.client(service)
   .type(MediaType.APPLICATION_JSON_TYPE)
   .accept(MediaType.APPLICATION_JSON_TYPE);

Ofcourse, in reality we don’t care too much about the format of the message returned when using it with a proxy unless we’re constrained by the server.

Creating a CXF service that responds with JSON or XML

In the first post of the three, we generated the source code from an XSD, now we’re going to create a CXF service that will be able to handle responses in either JSON or XML.

Note: Unlike a previous post on CXF services, this is not a SOAP webservice but is a REST webservice which can respond in the two formats (JSON or XML)

Show me the POM

Here’s the POM (save it as pom.xml in your project’s folder if you’re following along with this post).

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.putridparrot.server</groupId>
    <artifactId>ppserver</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>ppserver</name>
    <url>http://maven.apache.org</url>

    <properties>
        <cxf.version>2.7.18</cxf.version>
        <httpclient.version>4.3.6</httpclient.version>
        <surefire.version>2.19.1</surefire.version>
        <jackson.version>1.9.13</jackson.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>${httpclient.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-jaxrs</artifactId>
            <version>${jackson.version}</version>
        </dependency>
    </dependencies>
</project>

Setting up the project

If using IntelliJ, select File | Open, navigate to the pom and open it, you’ll be prompted to open in different ways – Open as Project and then IntelliJ will generate a new project around it.

Use the IntelliJ Terminal window to run

mvn install

We now need to create the standard folder structure, so off of the top level tree node in the project explorer create the following folders

src/main/java

In the project explorer select the java folder and right mouse click on it, select Mark Directory As and then Sources Root.

Now copy the folders generated by XJC (in the previous post) into the java folder.

Implement the server code

It’s preferable to separate the interface (which we’ll need for our client) from the implementation of our service, so create a SampleService interface in the java folder that looks like this

import com.putridparrot.sample.PurchaseOrderType;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("service")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public interface SampleService {
    @GET
    @Path("purchaseOrder")
    public PurchaseOrderType getPurchaseOrder();
}

Notice we’ve marked the interface as producing either JSON or XML.

We’ve set the path to our services as /service and the getPurchaseOrder method as purchaseOrder (we’ve also marked this method as a GET method) so to access we’ll simply append /service/purchaseOrder to our server’s URL.

Now let’s create the implementation, so add a file to the java folder named SampleServiceImpl and it should look like this

import com.putridparrot.sample.PurchaseOrderType;
import com.putridparrot.sample.USAddress;

public class SampleServiceImpl implements SampleService{
    public PurchaseOrderType getPurchaseOrder() {
        PurchaseOrderType po = new PurchaseOrderType();

        USAddress address = new USAddress();
        address.setCity("New York");
        address.setCountry("USA");
        address.setName("SpongeBob");

        po.setBillTo(address);

        return po;
    }
}

Obviously this isn’t useful, but it’s good enough to demonstrate how things fit together.

Finally we’re going to create a server to run this service, so create a new file in the java folder named SampleServer.java and put the following code into it.

import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

import javax.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SampleServer {
    public static void main(String args[]) throws Exception {
        JAXRSServerFactoryBean factoryBean = new JAXRSServerFactoryBean();
        factoryBean.setResourceClasses(SampleServiceImpl.class);
        factoryBean.setResourceProvider(new SingletonResourceProvider(new SampleServiceImpl()));

        Map<Object, Object> extensionMappings = new HashMap<Object, Object>();
        extensionMappings.put("xml", MediaType.APPLICATION_XML);
        extensionMappings.put("json", MediaType.APPLICATION_JSON);
        factoryBean.setExtensionMappings(extensionMappings);

        List<Object> providers = new ArrayList<Object>();
        providers.add(new JAXBElementProvider());
        providers.add(new JacksonJsonProvider());
        factoryBean.setProviders(providers);

        factoryBean.setAddress("http://localhost:9000/");
        Server server = factoryBean.create();

        System.out.println("Server ready...");

        System.in.read();

        server.destroy();
        System.exit(0);
    }
}

Now if you run this server up and using your preferred browser access http://localhost:9000/service/purchaseOrder you’ll find an error

JAXBException occurred : unable to marshal type “com.putridparrot.sample.PurchaseOrderType” as an element because it is missing an @XmlRootElement annotation. unable to marshal type “com.putridparrot.sample.PurchaseOrderType” as an element because it is missing an @XmlRootElement annotation.

As can be seen, JAXB expects an XmlRootElement and our generated code does not have one. This is why XJC generated more files than types in the XSD. You may have noticed ObjectFactory. Well without an XmlRoot we need to handle things slightly differently and this is where the ObjectFactory comes in – but for this example (and frankly because it’s ultimately simpler) we’ll add XmlRoot to the PurchaseOrderType ourselves, so it should now look like this

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "PurchaseOrderType", propOrder = {
    "shipTo",
    "billTo"
})
public class PurchaseOrderType {
// rest of the code
}

Now if we run the server and again use our browser to access http://localhost:9000/service/purchaseOrder you should see the following

<PurchaseOrder xmlns="http://tempuri.org/PurchaseOrderSchema.xsd">
   <BillTo country="USA">
      <name>SpongeBob</name>
      <city>New York</city>
   </BillTo>
</PurchaseOrder>

Now I mentioned this server should be able to return both JSON or XML. In this case our browser accepts application/xml and hence that’s what’s returned. We can use a chrome application such as ARC and add accept: application/json to return the JSON for the same request, her’s what’s returned

{
"shipTo": [],
"billTo": {
"name": "SpongeBob",
"street": null,
"city": "New York",
"state": null,
"zip": null,
"country": "USA"
},
"orderDate": null
}

Wait, what just happened?

We’ve jumped ahead a little and not described the server. So let’s backtrack a little. In the server we’re creating a JAXRSServerFactoryBean and creating a singleton of the SampleServiceImpl which will be used to handle service requests, but then we do the following

Map<Object, Object> extensionMappings = new HashMap<Object, Object>();
extensionMappings.put("xml", MediaType.APPLICATION_XML);
extensionMappings.put("json", MediaType.APPLICATION_JSON);
factoryBean.setExtensionMappings(extensionMappings);

Here we’re telling the factoryBean that we can handle these specific extension types and mapping them, but we need to have providers which actually do the work for us, so then we add the following

List<Object> providers = new ArrayList<Object>();
providers.add(new JAXBElementProvider());
providers.add(new JacksonJsonProvider());
factoryBean.setProviders(providers);

The JAXBElementProvider handles the XML whilst the JacksonJsonProvider (as I’m sure you guessed) handles the JSON. Basically these will generate the message body for our responses etc.

Finally in the main method we create a server from our bean for the given address.

Interacting with SOAP headers using CXF

Sometimes you might want to interact with data being passed over SOAP within the SOAP headers, for example this is a technique used to pass security tokens or user information etc.

CXF comes with quite a few “insertion points” whereby we can insert our code into the workflow of WSDL creation, SOAP calls etc. Here we’ll just look at the specifics of intercepting the SOAP call and extracting the header (ofcourse the reverse can also be implemented, whereby we intercept an outward bound call and insert a SOAP header item, but that’s for the reader to investigate).

I’m only going to cover implementing this in code, but obviously this can also be setup via Spring configuration also.

To add an interceptor to our JaxWsServerFactoryBean, we do the following

JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
// set up the bean, address etc.

org.apache.cxf.endpoint.Server server = factory.create();
server.getEndpoint().getInInterceptors().add(new SoapInterceptor());

Now let’s look at the SoapInterceptor

public class SoapInterceptor extends AbstractSoapInterceptor {
    public static final String SECURITY_TOKEN_ELEMENT = "securityToken";

    public SoapInterceptor() {
        super(Phase.PRE_PROTOCOL);
        addBefore(WSDLGetInterceptor.class.getName());
    }
    
    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        String securityToken = getTokenFromHeader(message);
        // do something with the token, maybe save in a context
    }

    private String getTokenFromHeader(SoapMessage message) {
        String securityToken = null;
        try {
            List<Header> list = message.getHeaders();
            for(Header h : list) {
                if(h.getName().getLocalPart() == SECURITY_TOKEN_ELEMENT) {
                    Element token = (Element)h.getObject();
                    if(token != null) {
                        securityToken = token.getTextContent().toString();
                        break;
                    }
                }
            }
        } catch (RuntimeException e) {
            throw new JAXRPCException("Invalid User", e);
        } catch (Exception e) {
            throw new JAXRPCException("Security Token failure ", e);
        }
        return securityToken;
    }
}

Hosting more than one service using CXF and Jetty

This is a logical “next post” to Hosting a CXF SOAP webservice in an existing Jetty instance.

Let’s look at what’s need to host more than one service using CXF from a running instance of Jetty.

Changes to our SOAP Service

We’re going to do this by hosting our original HelloWorld service (from the previous post) with a single line change and alongside it we’ll create a REST service.

So in the AppConfig from the previous post, rename getService to getSoapServer (or whatever you like) and the code should look like this

@Bean @DependsOn( "cxf" )
public Server getSoapServer() {
   JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
   factory.setServiceBean(new HelloWorldImpl());
   factory.setAddress("/soap");
   return factory.create();
}

Apart from the method name change, we’ve added a setAddress call with a relative path. If you recall from the previous post our Jetty server will run on port 9000, i.e. http://localhost:9000 but now our SOAP service will be on http://localhost:9000/soap/HelloWorld?wsdl.

Adding a REST Service

Update the pom.xml with the following, in the properties we’ll add

<json.version>1.1.0-M1</json.version>
<glassfish.version>1.0.4</glassfish.version>

and these dependencies

<dependency>
   <groupId>javax.json</groupId>
   <artifactId>javax.json-api</artifactId>
   <version>${json.version}</version>
</dependency>

<dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-frontend-jaxrs</artifactId>
   <version>${cxf.version}</version>
</dependency>

<dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-rs-extension-providers</artifactId>
   <version>${cxf.version}</version>
</dependency>

<dependency>
   <groupId>org.glassfish</groupId>
   <artifactId>javax.json</artifactId>
   <version>${glassfish.version}</version>
</dependency>

Next we’ll create a simple REST service (I’ll actually duplicate the one from here).

So create a new Java class, mine’s named PeopleService and here’s the code

package com.putridparrot.core;

import javax.json.JsonArray;
import javax.json.Json;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/people")
public class PeopleService {
    @Produces({"application/json"})
    @GET
    public JsonArray getPeople() {
        return Json.createArrayBuilder()
                .add(Json.createObjectBuilder()
                        .add("firstName", "Brian")
                        .add("lastName", "Kernighan "))
                .add(Json.createObjectBuilder()
                        .add("firstName", "Dennis")
                        .add("lastName", "Ritchie "))
                .add(Json.createObjectBuilder()
                        .add("firstName", "Bjarne")
                        .add("lastName", "Stroustrup"))
                .build();
    }
}

within AppConfig, place the following

@Bean @DependsOn( "cxf" )
public Server getRestServer() {
   JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
   factory.setServiceBean(new PeopleService());
   factory.setProvider(new JsrJsonpProvider());
   factory.setAddress("/rest");
   return factory.create();
}

if you run the App.main, you should be able to navigate to http://localhost:9000/soap/HelloWorld?wsdl to get the SOAP service’s wsdl and http://localhost:9000/rest/people to get the results of the PeopleService call.

Just to complete the picture – the PeopleService is given a path to the REST calls. We’re simply returning JSON and so we annotate with the @Produces line. As we’re only support a GET call to this service we annotate @GET. Within the getPeople method we build up a JSON document and that’s all there is to it.

Hosting a CXF SOAP webservice in an existing Jetty instance

In the previous post we looked at creating a web service, exposing SOAP and running in a CXF created instance of Jetty, but what if you already have an application with an existing instance of Jetty running. All you want to do is drop a SOAP capable service into this instance.

Like most of the things I’ve discovered working with CXF (and Spring), it’s not that difficult, once you know how. It’s the finding out “how” that seems more difficult :)

First off, much credit goes to this post Embedded Jetty and Apache CXF: Secure REST Services With Spring Security or more specifically the source code for this post, here.

I’m not interested (at this time) in security, so we’ll ignore that and just concern ourselves with injecting a service into an instance of Jetty created by the application itself (not by CXF).

First up, let’s create our pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.putridparrot.core</groupId>
  <artifactId>cxfws</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>cxfws</name>
  <url>http://maven.apache.org</url>

  <properties>
    <cxf.version>3.0.1</cxf.version>
    <spring.version>4.1.0.RELEASE</spring.version>
    <jetty.version>9.4.2.v20170220</jetty.version>
  </properties>

  <dependencies>

    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxws</artifactId>
      <version>${cxf.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-server</artifactId>
      <version>${jetty.version}</version>
    </dependency>

    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-servlet</artifactId>
      <version>${jetty.version}</version>
    </dependency>
  </dependencies>
</project>

As usual, run mavn install to grab the dependencies.

Now create the usual folder structure for our source, /src/main/java and within this your package folders, i.e. com/putridparrot/core.

We’re going to create four files, two of which we’ve used in previous posts, HelloWorld and HelloWorldImpl, I’ll include those here first just for completeness

HelloWorld.java

package com.putridparrot.core;

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

@WebService
public interface HelloWorld {
    String reply(@WebParam(name="text") String text);
}

HelloWorldImpl.java

package com.putridparrot.core;

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

@WebService(endpointInterface = "com.putridparrot.core.HelloWorld", serviceName="HelloWorld")
public class HelloWorldImpl implements HelloWorld {
    public String reply(@WebParam(name = "text") String text) {
        return "Hello " + text;
    }
}

As per the example from the referenced post Embedded Jetty and Apache CXF: Secure REST Services With Spring Security we’re going to create the configuration in code, so create AppConfig.java and place the following in this file

package com.putridparrot.core;

import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
public class AppConfig {
    @Bean( destroyMethod = "shutdown" )
    public SpringBus cxf() {
        return new SpringBus();
    }

    @Bean @DependsOn( "cxf" )
    public Server getServer() {
        JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
        factory.setServiceBean(getService());
        return factory.create();
    }

    @Bean
    public HelloWorldImpl getService() {
        return new HelloWorldImpl();
    }
}

Finally create App.java and place the following inside it

package com.putridparrot.core;

import org.apache.cxf.transport.servlet.CXFServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

public class App {
    public static void main( String[] args ) throws Exception {

        Server server = new Server( 9000 );

        ServletHolder servletHolder = new ServletHolder( new CXFServlet() );
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath( "/" );
        context.addServlet( servletHolder, "/*" );
        context.addEventListener( new ContextLoaderListener() );

        context.setInitParameter( "contextClass", AnnotationConfigWebApplicationContext.class.getName() );
        context.setInitParameter( "contextConfigLocation", AppConfig.class.getName() );

        server.setHandler( context );
        server.start();
        server.join();

        System.in.read();

        System.exit(0);
    }
}

Once this is built, run App.main and you should be able to use your web browser to navigate to http://localhost:9000/HelloWorld?wsdl and see the wsdl definitions for this service.

Great, but what’s it all doing?

Let’s start with the App.main method. The server variable is our Jetty instance. So really the only thing we’re interested here is which port have we assigned the instance to and the server.setHandler method call. This is where we embed our servlet context into the Jetty engine. From what I can understand, this basically allows us a way to intercept calls and pass to our specific servlet, in this case (ultimately) our CXFServlet.

The ServletHolder, as the name suggests, holds the servlet object that we want to use and will be added to the ServletContextHandler. As you can see this is where the bulk of the code is. The setContextPath call sets up the root level path of our context, i.e. changing to context.setContextPath( “/ws” ) means our webservice will now be located off of the ws path, thus http://localhost:9000/ws/HelloWorld?wsdl.

When we call addServlet we can, in essence, extend the root path further, so for example context.addServlet( servletHolder, “/services/*” ) will simply moved the servlet to your root path/services.

We add the spring ContextLoaderListener to the ServletContext and this is where we can setup the parameters required for spring to bring in our webservices etc.

The ContextLoaderListener “Create a new ContextLoaderListener that will create a web application context based on the “contextClass” and “contextConfigLocation” servlet context-params.” we’ll need to supplied the contextClass and contextConfigLocation parameters and that’s exactly what we do in the two setInitParameter lines of code.

The contextClass is going to use the web annotations (hence our webservices are annotated) and the contextConfigLocation will use our AppConfig class (instead of a config. file).

Configuration class

The AppConfig class supplies the configuration for the ContextLoaderListener and is first annotated with the Configuration, although removing this seemed to have no effect. What does have a big effect is the requirement for a bean named cxf – if this is missing you’ll get the following exception


Exception in thread “main” org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘cxf’ is defined

In our case we return a SpringBus

Next up we supply a getServer bean which depends on the cxf bean – this simply means the cxf bean should be created before the getServer bean. This code creates the web service implementation (we could have created a REST implementation using JAXRSServerFactoryBean). Within this code we set the factory bean to use the web service supplied via our getService method (which above is marked as a bean but needn’t be). The we simply return the result of the create method and we’re done.

Writing the Hello World CXF webservice

Before I get into this post, I’m well aware that SOAP hasn’t been the cool kid on the block in terms of web service messaging for a while, but I’m looking at updating an old application’s webservice from a framework which is no longer supported on an older version of Java to a supported framework. So we need to keep everything working using SOAP.

Following these steps, we can get a service up and running in next to no time (ofcourse it took me far longer than “no time” to find out how to do this :)).

  • Create yourself a folder for your project, mine’s cxftest
  • Within your project’s folder create a Maven pom.xml to grab all our required dependencies we’ll require – here’s the pom
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.putridparrot.core</groupId>
      <artifactId>cxftest</artifactId>
      <packaging>jar</packaging>
      <version>1.0-SNAPSHOT</version>
      <name>cxftest</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <cxf.version>2.2.3</cxf.version>
      </properties>
    
      <dependencies>
    
        <dependency>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-rt-frontend-jaxws</artifactId>
          <version>${cxf.version}</version>
        </dependency>
    
        <dependency>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-rt-transports-http</artifactId>
          <version>${cxf.version}</version>
        </dependency>
    
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </project>
    
  • We’re going to do everything from scratch here, but continue with the layout that Maven might generate for us, so off of your project’s folder create src/main/java/putridparrot/core
  • Now add three files, App.java, HelloWorld.java and HelloWorldImpl.java
  • Paste the following code into HelloWorld.java
    package com.putridparrot.core;
    
    import javax.jws.WebParam;
    import javax.jws.WebService;
    
    @WebService
    public interface HelloWorld {
        String reply(@WebParam(name="text") String text);
    }
    
  • Now let’s paste the following into the HelloWorldImpl
    package com.putridparrot.core;
    
    import javax.jws.WebParam;
    import javax.jws.WebService;
    
    @WebService(endpointInterface = "com.putridparrot.core.HelloWorld", serviceName="HelloWorld")
    public class HelloWorldImpl implements HelloWorld {
        public String reply(@WebParam(name = "text") String text) {
            return "Hello " + text;
        }
    }
    
  • Finally paste the following into App.java
    package com.putridparrot.core;
    
    import javax.xml.ws.Endpoint;
    import java.io.IOException;
    
    public class App {
       public static void main( String[] args ) 
          throws IOException, InterruptedException  {
    
       HelloWorldImpl impl = new HelloWorldImpl();
       String address = "http://localhost:9000/HelloWorld";
       Endpoint.publish(address, impl);
    
       // hold the service open
       System.in.read();
    
       System.exit(0);
    }
    

As you’ve probably noticed, we’re not using Spring to create our webservice (the HelloWorldImpl) and thus there’s no spring configuration or the likes. This really is a bare bones implementation to get us started.

Now if you run this you should be able to use the URL http://localhost:9000/HelloWorld?wsdl to grab the definition meta data. Hence from a C# client you can use wsdl.exe or using Visual Studio’s Add Service reference to generate C# classes to use this service.

Alternate code to start the server

In the above we used Endpoint to create the server, the alternative is to use the JaxWsServerFactoryBean, here’s the code

JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
factory.setServiceClass(HelloWorld.class);
factory.setAddress("http://localhost:9000/HelloWorld");
factory.create();

Beanify it

Okay, so we’ve proved the service works at this point, but we really don’t want to hardcode the service like this, so it’s type to turn it into a Spring bean.

First off let’s add to our pom.xml file, you’ll need the following added to the properties element

<spring.version>3.0.5.RELEASE</spring.version>

and then the following dependencies need to be added

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot</artifactId>
      <version>1.5.2.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>1.5.2.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
    </dependency>

and we’re going to use Jetty as the HTTP server, so also add the following dependency

<dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-transports-http-jetty</artifactId>
   <version>${cxf.version}</version>
</dependency>

Obviously you’ll need to run mvn install to get the dependencies updated.

Next we need to create our beans file, within this we’ll actually create a server/endpoint which will be executed. So create a resources folder off of src/main in your project’s folder and add an XML file, mine’s named cxf.xml – within this file we can use one of either of these configurations

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />

    <jaxws:server id="helloWorld"  serviceClass="com.putridparrot.core.HelloWorld" address="http://localhost:9000/HelloWorld">
        <jaxws:serviceBean>
            <bean class="com.putridparrot.core.HelloWorldImpl"/>
        </jaxws:serviceBean>
    </jaxws:server>
</beans>

or using an Endpoint we can use the following

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
    <import resource="classpath:META-INF/cxf/cxf.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />

 <jaxws:endpoint id="helloWorld" implementor="com.putridparrot.core.HelloWorldImpl"
                    address="http://localhost:9000/HelloWorld">
        <jaxws:features>
            <bean class="org.apache.cxf.feature.LoggingFeature"/>
        </jaxws:features>
    </jaxws:endpoint>
</beans>

You can change the address to address=”/HelloWorld” if you are using the default port etc. Otherwise to remove this will require looking into overriding Jetty’s port etc. (which is outside the scope of this post).

Finally we need to have the main method wire everything up, so within main we should have the following

new ClassPathXmlApplicationContext("cxf.xml");

// stop the server shutting down
System.in.read();
System.exit(0);