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>
4 comments:
Hey, I updated this for you so that it uses JBoss AS 7.1.1.Final and JBoss-WS 4.1.0.Final.
Link is: here.
Hi, I have tried the steps mentioned on this page for deploying a REST-ful web-service project using Apache CXF 2.6.2. I have put the cxf-2.6.2.jar file in the org/apache/cxf/main folder and done the entries in the module.xml as advised by you. I have also put the Spring framework JARs after creating the springframework/main folder as advised by you and created the entries in the module.xml there. However, I'm getting the following error message:
Could not configure component org.apache.cxf.wsn.client.Publisher
Caused by: org.jboss.as.server.deployment.DeploymentUnitProcessingException: JBA
S011054: Could not find default constructor for class org.apache.cxf.wsn.client.
Publisher
"WebService.war".INSTALL: org.jboss.msc.service.StartException in service jboss.deployment.unit."WebService.war".INSTALL: Failed to process phase INSTALL of deployment "WebService.war"
Do I need to also deploy the other CXF JARs from the 2.6.2 bundle and create entries for them in the module.xml?
Another thing - I'm not using Maven to build this, I'm using Eclipse. Do I need to manually include anything in my MANIFEST.MF then? Also, there is a warning before this error occurs:
Encountered invalid class name 'org.springframework.context.Application
Context,org.springframework.beans.BeansException' for service type 'org.apache.c
xf.bus.factory'
Modifying the org/apache/cxf JBoss AS modules contents without the matching JBossWS upgrade can basically broke the WS integration for other deployments, so is a big NO. If you want to have a newer version of Apache CXF in JBoss AS, consider using the install script of newer JBossWS releases [1] (see the supported target containers at [2]).
Otherwise, use jboss-deployment-structure.xml to disable jaxrs and webservices subsystem, embed cxf libraries in your war and leave the AS untouched.
Please refer to the JBoss WebService documentation at [3] and also read [4].
[1] http://www.jboss.org/jbossws/downloads
[2] https://community.jboss.org/wiki/JBossWS-SupportedTargetContainers
[3] https://docs.jboss.org/author/display/JBWS/Apache+CXF+integration
[4] http://cxf.apache.org/docs/application-server-specific-configuration-guide.html#ApplicationServerSpecificConfigurationGuide-JBossApplicationServer
Post a Comment