In an earlier blog, I showed you how to develop APIs using REST and Spring RS. Since Spring does not fully implement JAX-RS, in this blog, I will show you how to use Apache CXF with Spring to write RESTful web services.
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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.noushin</groupId>
<artifactId>cxfws</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>cxfws</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cxf.version>3.0.2</cxf.version>
<junit.version>4.10</junit.version>
<log4j.version>1.2.17</log4j.version>
<spring.version>4.1.1.RELEASE</spring.version>
<spring.security.version>3.2.0.RELEASE</spring.security.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Spring Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</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>
<!-- JSON Binding -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.5.7</version>
</dependency>
<!-- CXF -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
<build>
<finalName>cxfws</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
The first class you need to write is an interface to expose your web services:
package com.noushin.cxfws.user.service;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.noushin.cxfws.user.model.Status;
import com.noushin.cxfws.user.model.User;
@Path("/user")
public interface UserService {
/*
* curl -i -v -X GET -H "Accept: application/json" http://localhost:8080/cxfws/services/user/123
* return: {"id":"123","firstName":"John","lastName":"Smith"}
*/
@GET
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces({ MediaType.APPLICATION_JSON })
@Path("/{id}")
public User getUser(@PathParam("id") String id);
/*
* curl -i -v -X POST -H "Content-type: application/json" -H "Accept: application/json" http://localhost:8080/cxfws/services/user/ -d @param.json
* return: {"id":"456","firstName":"Jane","lastName":"Doe"}
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces({ MediaType.APPLICATION_JSON })
@Path("/")
public User saveUser(User user);
/*
* curl -i -v -X PUT -H "Accept: application/json" http://localhost:8080/cxfws/services/user/123/abcd
* return: {"id":"123","firstName":"abcd","lastName":"old lastname"}
*/
@PUT
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces({ MediaType.APPLICATION_JSON })
@Path("/{id}/{fn}")
public User updateUser(@PathParam("id") String id, @PathParam("fn") String firstName);
/*
* curl -i -v -X DELETE -H "Accept: application/json" http://localhost:8080/cxfws/services/user/123
* return: {"message":"User removed successfully.","code":0}
*/
@DELETE
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces({ MediaType.APPLICATION_JSON })
@Path("/{id}")
public Status removeUser(@PathParam("id") String id);
}
Now, you have to write the implementation class:
package com.noushin.cxfws.user.service;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.noushin.cxfws.user.dao.UserDao;
import com.noushin.cxfws.user.model.Status;
import com.noushin.cxfws.user.model.User;
import com.noushin.cxfws.user.reference.StatusCodes;
@Service("userService")
public class UserServiceImpl implements UserService {
private final static Logger logger = Logger.getLogger(UserServiceImpl.class);
@Autowired
UserDao userDao;
public User getUser(String id) {
String msg = "getting user with id: " + id + ".";
logger.info(msg);
return userDao.getUser(id);
}
public User saveUser(User user) {
String msg = "saving user with id: " + user.getId() + ".";
logger.info(msg);
return userDao.saveUser(user);
}
public User updateUser(String id, String firstName) {
String msg = "updating user with id: " + id + ".";
logger.info(msg);
User user = new User();
user.setId(id);
user.setFirstName(firstName);
return userDao.saveUser(user);
}
public Status removeUser(String id) {
String msg = "removing user with id: " + id + ".";
logger.info(msg);
Status status = null;
if (userDao.removeUser(id))
return new Status(StatusCodes.FAILED, "User removed successfully.");
return new Status(StatusCodes.FAILED, "User could not be removed.");
}
}
The JSON objects that will be passed to or retuned from user web services, will be presented as simple POJOS.
User JSON:
package com.noushin.cxfws.user.model;
public class User {
private String id;
private String firstName;
private String lastName;
public User() {
}
public User(String id, String firstName, String lastName) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String toString() {
return "User {id:" + id + ", firstName:" + firstName + ", lastName:" + lastName + "}";
}
}
Status JSON:
package com.noushin.cxfws.user.model;
public class Status {
private int code;
private String message;
public Status() {
}
public Status(int code, String message) {
super();
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
UserDao.java:
package com.noushin.cxfws.user.dao;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.noushin.cxfws.user.model.User;
@Component
public class UserDao {
private final static Logger logger = Logger.getLogger(UserDao.class);
public User getUser(String id) {
User user = new User(id, "John", "Smith");
String msg = "getting user with id: " + id + ".";
logger.info(msg);
return user;
}
public User saveUser(User user) {
String msg = "saving user with id: " + user.getId() + ".";
logger.info(msg);
return user;
}
public boolean removeUser(String id) {
String msg = "removing user with id: " + id + ".";
logger.info(msg);
return true;
}
}
Reference data:
package com.noushin.cxf.user.reference;
public class StatusCodes {
public final static int SUCCESS = 1;
public final static int FAILED = 0;
}
~cxfws/src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
<display-name>Spring CXF WS Demo</display-name>
<description>Spring CXF WS Demo</description>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/cxfws-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
Spring application context is in ~cxfws/src/main/webapp/WEB-INF/cxfws-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:cxf="http://cxf.apache.org/core"
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://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">
<context:component-scan base-package="com.noushin.cxfws.user" />
<context:annotation-config />
<jaxrs:server id="userServer" address="/">
<jaxrs:serviceBeans>
<ref bean="userService" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
</jaxrs:providers>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
</jaxrs:extensionMappings>
<jaxrs:features>
<cxf:logging/>
</jaxrs:features>
</jaxrs:server>
<bean id="userServiceImpl" class="com.noushin.cxfws.user.service.UserServiceImpl" />
</beans>