Monday, August 27, 2012

Apache CXF Working with Spring

These days I have been playing with Apache CXF, which is great open source java-based library for creating both SOAP and REST web services. CXF is a great library, specially if you are using the Spring Framework and despite the outdated and inaccurate documentation, I managed to make it work and here is how to do it.

I have assembled a sample project which is on my GitHub repository.

A Small Introduction


CXF is designed with a very flexible architecture based on interceptors, this architecture allows the developer to make CXF work with different types of front end technologies such as JAX-WS, JAX-RS, CORBA and more!

CXF is also very friendly with the Spring framework so it is (or at least should be) very easy to integrate in this kind of applications.

Working with Maven

CXF artifacts are published on the central maven repository, so first of all, you need to add the dependencies into your project:


<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-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>

At the moment of writing this post CXF latest version is 2.6.2. Also you need to include dependencies for the Spring Framework, please refer to the sample project for these.

Spring Configuration

To start with this little how to, I created a service class with has mixed JAX-WS and JAX-RS annotations. This makes easy to export the same functionality as SOAP and REST web services:


@WebService
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface MyWebService {

    @WebMethod
    String doSomeWork(String work);
    
    
    public String beRestful(String name);
    
    
    @WebMethod
    public ArrayList<Customer> findCustomers(CustomerRequest request);
}

And the implementation:


@WebService
public class MyWebServiceImpl implements MyWebService {
    
    @WebMethod
    @Override
    public String doSomeWork(String work) {
        return "Doing some work with this: "+work;
    }
    
    
    @Override
    @GET @Path("/beRestful") @Produces("text/plain") //REST Annotations
    @WebMethod //SOAP Annotations
    public String beRestful(@QueryParam("name") String name) {
        return "Your name is: "+name;
    }
    
    
    @WebMethod
    public ArrayList<Customer> findCustomers(CustomerRequest request) {
        
        
        ArrayList<Customer> ret = new ArrayList<Customer>();
        
        Customer customer = new Customer();
        
        customer.setCustomerName(request.getName());
        customer.setMembershipLevel(request.getMembershipLevel());
        customer.setCustomerSince(new Date());
        
        ret.add(customer);
        
        return ret;
    }
    
}


The Customer and CustomerRequest classes are pretty simple and therefore not shown here, please refer to the sample project for the source code.

These exposes three methods through SOAP and one through REST, CXF has its own XML namespaces for JAX-WS and JAX-RS, the header of the applicationContext.xml file looks like the following:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd/spring-spring-context-3.1.xsd-3.1.2.RELEASE.xsd
          http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
          http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
          http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
">


CXF also has its own configuration files included inside the JAR files, even though these files look empty, the documentation suggests to include them so here are the lines for it:


    <!-- import cxf webservices -->
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

Finally, we need to declare the bean we just created as a Spring Bean and reference it in a SOAP and a REST endpoint:


    <jaxws:endpoint 
        id="mywebservice" 
        implementor="#webserviceImpl" 
        address="/myws" />
    
    
    <jaxrs:server id="myrestservice" address="/rest">
        <jaxrs:serviceBeans>
            <ref bean="webserviceImpl" />
        </jaxrs:serviceBeans>
    </jaxrs:server>
    
    <bean id="webserviceImpl" class="com.juancavallotti.cxfdemo.MyWebServiceImpl" /> 

The last ingredient is configuring the CXF servlet in our web.xml file.

Configuration of web.xml

The configuration of this file is pretty standard, first I have declared the CXF servlet and map it to the /ws/* URL pattern, and then I just declared the standard listeners for the Spring Framework to work on a web environment.


    <!-- the cxf servlet -->
    <servlet>
        <servlet-name>cxfservlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxfservlet</servlet-name>
        <url-pattern>/ws/*</url-pattern>
    </servlet-mapping>
    
    <!-- spring context and request listeners -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

Running the example


After following these steps we are ready to run the example application, if you have forked or cloned the sample application just run: $ mvn tomcat:run command line and after the startup, point the browser to: http://localhost:8080/CxfDemo/ws, you should see a very simple webpage that informs the webservices running on the CXF servlet and links to the WSDL and the WADL:


Conclusions

CXF is a very powerful and complete framework for exposing services as web services, I think it is the natural choice if you want to enable web services in your spring application. Be aware that you will find outdated documentation and be sure to read the documents about the architecture before planning to reverse-engineer the framework or else you'll find the task rather frustrating.