Developer Articles

How to Easily Build REST Web-Services With Java, Spring and Apache CXF

As an integration platform for Web applications, AppDirect relies heavily on APIs and Web-Services. That's why when we designed our architecture, we looked for technologies that are open and flexible, while sticking to the golden KISS principle. We found that exposing CRUD (Create, Read, Update and Delete) operations through RESTful Web-Services was a great choice with these requirements. In this article, we're going to show how easy it is to implement this type of Web-Services on a Java stack with the Spring Framework and Apache CXF.

To keep this article short and sweet, we're going to start from the point where you have a working JEE web app running with Spring (3.0.5) configured.

Configure Spring and CXF

First, we need to do a little configuration to integrate CXF in our Spring application. CXF implements JAX-RS (JSR-311), which is what we are going to use. We need to add the CXF dependency to our project. The Maven dependencies are:

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxrs</artifactId>
    <version>2.4.2</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>2.4.2</version>
</dependency>

To hook up Spring and CXF we create a Spring XML called applicationContext-cxf.xml:

<?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:jaxrs="http://cxf.apache.org/jaxrs"
    xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
        http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
    ">
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
    <jaxrs:server id="restContainer" address="/">
        <jaxrs:serviceBeans>
            <!-- This is where we tell which beans CXF should expose as Web-Services -->
        </jaxrs:serviceBeans>
    </jaxrs:server>
</beans>

Finally, we update our web.xml to define the servlet that will process the requests to our Web-Service and map it to the "/api/" path (for example):

<servlet>
    <servlet-name>cxf</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>cxf</servlet-name>
    <url-pattern>/api/*</url-pattern>
</servlet-mapping>

Whew! This was the hard part! Luckily this is only done once. We can now focus on creating actual functionality.

 

Defining the business logic

For the sake of this example, we're going to expose CRUD operations on an entity called "User". We're defining the User object as a simple POJO:

public class User {
    private String uuid;
    private String email;
    private String firstName;
    private String lastName;
    <getters and setters>
}

The interface to manage that data should look something like that:

public interface public interface UserService {
    public User create(User user);
    public User read(String uuid);
    public void update(String uuid, User user);
    public void delete(String uuid);
}

and the class implementing that logic like:

@Service("userService") // Use Spring IoC to create and manage this bean.
public class UserServiceImpl {
    public User create(User user) {
        // Some logic...
    }
    public User read(String uuid) {
        // Some logic...
    }
    public void update(String uuid, User user) {
        // Some logic...
    }
    public void delete(String uuid) {
        // Some logic...
    }
}

Nothing fancy here. Just some basic Java code... Now get ready for some magic!

 

Expose the business logic as a Web-Service

This is where things get interesting. We are going expose the business logic created above as REST Web-Services without writing any extra code, by sprinkling a few Java annotations on the classes we already have. To be as flexible as possible, we want our Web-Services to be able to consume and produce data in XML as well JSON. CXF supports both out of the box, and, in a lot of cases, all you need to do is add the @XmlRootElement annotation on your User class declaration:

@XmlRootElement(name = "user")
public class User {
    // Same code as before...
}

With this, instances of the User class can be serialized to / deserialized from: 

XML:

<user>
    <uuid>550e8400-e29b-41d4-a716-446655440000</uuid>
    <email>christophe.levesque@...com</email>
    <firstName>Christophe<firstName>
    <lastName>Levesque</lastName>
</user>

or JSON:

{
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "email": "christophe.levesque@...com",
    "firstName": "Christophe",
    "lastName": "Levesque"
}

We can now use the JAX-RS annotations on the interface we created earlier to declare our Web-Services:

@Path("v1")
public interface UserService {
    @POST
    @Path("users")
    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    public User create(User user);

    @GET
    @Path("users/{uuid}")
    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    public User read(@PathParam("uuid") String uuid);

    @PUT
    @Path("users/{uuid}")
    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    public void update(@PathParam("uuid") String uuid, User user);

    @DELETE
    @Path("users/{uuid}")
    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
    public void delete(@PathParam("uuid") String uuid);
}

These annotations map URL paths and HTTP verbs (GET, POST...) to the methods we created.

Reference: http://cxf.apache.org/docs/jax-rs-basics.html

Last bit of configuration is to add our Spring bean "userService" to the list of CXF service beans in applicationContext-cxf.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
...
    <jaxrs:server id="restContainer" address="/">
        <jaxrs:serviceBeans>
            <ref bean="userService" />
        </jaxrs:serviceBeans>
    </jaxrs:server>
</beans>

And voila! You have now implemented a set of RESTful Web-Services to manage users. You can create a user by POSTing XML or JSON to http://yourhost/api/v1/users, read info about a user at http://yourhost/api/v1/users/550e8400-e29b-41d4-a716-446655440000, etc. CXF will automatically detect which media type to use from the "Accept" or "Content-Type" header of your HTTP request. More advanced topic notes:

  • You can configure CXF to use other (de)serializers than the packages ones (like the excellent Jackson JSON processor) or implement your own
  • You can map exceptions thrown by your API to particular HTTP status codes (403, 404, 500...) using beans implementing javax.ws.rs.ext.ExceptionMapper
  • You can (and should) configure CXF to secure your APIs (start with http://cxf.apache.org/docs/secure-jax-rs-services.html)
  • You can also easily implement clients for RESTful Web-Services using org.apache.cxf.jaxrs.client.JAXRSClientFactory

Tags for this Post

apache cxf jax-rs jee rest rest web services
Photo of Christophe Levesque Posted by Christophe Levesque on Monday, September 26th, 2011

In The News

Popular Tags