For the past couple of years, I have developed REST APIs using RESTEasy since the target deployment platform was JBOSS. More recently I have considered using Apache CXF instead. One reason is that we wanted to use a single technology to create web services (we are using CXF for our SOAP based web services).
In this article I will show how to create a very simple REST web service using CXF and Spring and describe how to deploy it on JBOSS AS 7.0.
I have found several tutorials describing how to create CXF based REST web services but none with Spring and working when deployed on JBOSS AS 7.0. Part of the proof of concept (POC) code sample I am describing in this article come from Sandeep Bhandari article "REST Web Service Using CXF - Beginner's Tutorial" which I enriched with Spring integration and deployment configuration for JBOSS AS 7.0.
In my POC example, there is not much differences for the POJO classes representing the order resources (I made them a little bit simple):
class Order
In this article I will show how to create a very simple REST web service using CXF and Spring and describe how to deploy it on JBOSS AS 7.0.
I have found several tutorials describing how to create CXF based REST web services but none with Spring and working when deployed on JBOSS AS 7.0. Part of the proof of concept (POC) code sample I am describing in this article come from Sandeep Bhandari article "REST Web Service Using CXF - Beginner's Tutorial" which I enriched with Spring integration and deployment configuration for JBOSS AS 7.0.
In my POC example, there is not much differences for the POJO classes representing the order resources (I made them a little bit simple):
class Order
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "order") public class Order { private String itemName; private int quantity; private String customerName; @XmlElement public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } @XmlElement public int getQuantity() { return quantity; } public void setQuantity(int quantity) { this.quantity = quantity; } @XmlElement public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } }
import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "OrderList") public class OrderList { Listorders; @XmlElement(name = "order") public List getOrder() { if (orders == null) { orders = new ArrayList (); } return this.orders; } }
<web-app>
<display-name>Archetype Created Web Application</display-name>
<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>/api/*</url-pattern>
</servlet-mapping>
</web-app>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans='http://www.springframework.org/schema/beans'
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<jaxrs:server id="restContainer" address="/" >
<jaxrs:serviceBeans>
<ref bean="simpleService" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="jaxbProvider"/>
<ref bean="jsonProvider"/>
</jaxrs:providers>
</jaxrs:server>
<bean id="simpleService" class="com.acme.poc.restapi.cxf.service.SimpleServiceImpl" />
<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider"/>
<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
</beans>
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
@Path("/order/")
public interface SimpleService {
@GET
@Produces("application/xml")
@Path("{orderId}")
public Order getOrderXml(@PathParam ("orderId") int id);
@GET
@Produces("application/json")
@Path("/")
public Order getOrderJson(@QueryParam("id") @DefaultValue("-1") String strId);
@GET
@Produces("application/xml")
@Path("all")
public OrderList getAllOrders();
}
import java.util.ArrayList; import java.util.List; public class SimpleServiceImpl implements SimpleService { Listlist = new ArrayList (); public SimpleServiceImpl () { Order order = new Order(); order.setItemName("Veggie Pizza"); order.setQuantity(9); order.setCustomerName("OptumInsight"); list.add(order); order = new Order(); order.setItemName("Green Salad"); order.setQuantity(2); order.setCustomerName("OptumInsight"); list.add(order); } @Override public Order getOrderXml(int id) { return getOrder(id); } @Override public Order getOrderJson(String strId) { int id = Integer.valueOf(strId); return getOrder(id); } @Override public OrderList getAllOrders() { OrderList fullList = new OrderList(); for(Order order : list) { fullList.getOrder().add(order); } return fullList; } // Common method returning an Order POJO public Order getOrder(int id) { if ((id > 0) && (id <= list.size())) { return list.get(id - 1); } else return null; } }
<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.acme.poc.restapi</groupId>
<artifactId>poc_restapi_cxf_client</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>poc_restapi_cxf_client</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>3.1.0.RELEASE</spring.version>
<cxf.version>2.6.0</cxf.version>
<jackson.version>1.9.7</jackson.version>
<junit.version>4.8.1</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-search</artifactId>
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>${cxf.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>poc_restapi_cxf_client</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
The file jboss-deployment-structure.xml is used for JBOSS deployment:
<jboss-deployment-structure> <deployment> <dependencies> <module name="org.apache.cxf" services="import"> <imports> <include path="**" /> </imports> </module> </dependencies> </deployment> </jboss-deployment-structure>
1) Download the latest edition of CXF (2.6.0) and placed just one jar from the distribution into the folder
2) Open
Remove all resource elements (the JARs)
Add a new resource element similar to the deleted ones with cxf-2.6.0.jar being the only Jar referenced
Add dependency to module named: javax.ws.rs.api :
<module xmlns="urn:jboss:module:1.1" name="org.apache.cxf">
<resources>
<resource-root path="cxf-2.6.0.jar"/>
<!--
<resource-root path="cxf-api-2.4.4.jar"/>
...
<resource-root path="cxf-xjc-ts-2.4.0.jar"/>
-->
</resources>
<dependencies>
<module name="asm.asm" />
<module name="javax.api" />
<module name="javax.annotation.api" />
<module name="javax.jms.api" />
<module name="javax.jws.api" />
<module name="javax.mail.api" />
<module name="javax.resource.api" />
<module name="javax.servlet.api" />
<module name="javax.ws.rs.api" />
<module name="javax.xml.bind.api" services="import"/>
<module name="com.sun.xml.bind" services="import"/>
<module name="javax.wsdl4j.api" />
<module name="javax.xml.soap.api" />
<module name="javax.xml.stream.api" />
<module name="javax.xml.ws.api" />
<module name="org.apache.commons.lang" />
<module name="org.apache.neethi" />
<module name="org.apache.velocity" />
<module name="org.apache.xml-resolver" />
<module name="org.apache.ws.xmlschema" />
<module name="org.apache.ws.security" />
<module name="org.apache.santuario.xmlsec" />
<module name="org.springframework.spring" optional="true"/>
</dependencies>
</module>
Create module.xml
Copy all the text from the CXF module.xml into this new one
Erase the last dependency
Erase the resource against cxf JAR
Download latest Spring framework, and copy all the Spring jars into the folder
Add all of these JARs to the module.xml inside
Add dependency to module named: org.apache.commons.logging
<module xmlns="urn:jboss:module:1.1" name="org.springframework.spring">
<resources>
<resource-root path="spring-aop-3.1.0.RELEASE.jar" />
<resource-root path="spring-asm-3.1.0.RELEASE.jar" />
<resource-root path="spring-beans-3.1.0.RELEASE.jar" />
<resource-root path="spring-context-3.1.0.RELEASE.jar" />
<resource-root path="spring-core-3.1.0.RELEASE.jar" />
<resource-root path="spring-expression-3.1.0.RELEASE.jar" />
<resource-root path="spring-web-3.1.0.RELEASE.jar" />
<!-- Insert resources here -->
</resources>
<dependencies>
<module name="asm.asm" />
<module name="javax.api" />
<module name="javax.annotation.api" />
<module name="javax.jms.api" />
<module name="javax.jws.api" />
<module name="javax.mail.api" />
<module name="javax.resource.api" />
<module name="javax.servlet.api" />
<module name="javax.xml.bind.api" services="import"/>
<module name="com.sun.xml.bind" services="import"/>
<module name="javax.wsdl4j.api" />
<module name="javax.xml.soap.api" />
<module name="javax.xml.stream.api" />
<module name="javax.xml.ws.api" />
<module name="org.apache.commons.lang" />
<module name="org.apache.commons.logging" />
<module name="org.apache.neethi" />
<module name="org.apache.velocity" />
<module name="org.apache.xml-resolver" />
<module name="org.apache.ws.xmlschema" />
<module name="org.apache.ws.security" />
<module name="org.apache.santuario.xmlsec" />
</dependencies>
</module>