Skip to main content

Creating REST based Webservice with Apache CXF

As you know, there are 3 types of webservice out there named, RPC/Encoded type, Document/Literal/SOA type and REST type. But, the most supported ones are the RPC and Document types.

REST type gives you a lot of flexibility. This is how to create the server and the corresponding client using Apache CXF.



Prerequisites

You should have installed:
  • Sun Java 6
  • Maven 3
OK, let's get started.

Creating a REST based Webservice

1) On Maven pom.xml please include:
<packaging>war</packaging>

<properties>
    <cxf.version>2.6.0</cxf.version>
    <spring.version>3.0.5.RELEASE</spring.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>${cxf.version}</version>
        <scope>runtime</scope>
    </dependency>

    <!-- REST based WS: Required libraries during compile time since Java 6 
        doesn't include REST specification libraries. -->
    <dependency>
        <groupId>org.apache.servicemix.specs</groupId>
        <artifactId>org.apache.servicemix.specs.jsr311-api-1.1</artifactId>
        <version>1.6.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.servicemix.specs</groupId>
        <artifactId>org.apache.servicemix.specs.jaxb-api-2.1</artifactId>
        <version>1.6.0</version>
    </dependency>

    <!-- REST based WS: Required libraries. In OSGi environment, these may be provided -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxrs</artifactId>
        <version>${cxf.version}</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>


2) Create an interface defining a webservice
The primary annotations for SOAP based WS are: @Path, @Produces and a group defining the REST methods (@POST, @PUT, etc)
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/calculator")  // REST based WS: Path of this resource (or service)
@Produces({"application/xml", "application/json"})  // REST based WS: Response format
public interface CalculatorService {

    @POST  // REST based WS: Called using POST method
    @Path("/sum")  // REST based WS: Path of a sub resource
    public int sum(
        @FormParam(value = "a") // REST based WS: Define a form parameter (http://uri.com/sum?a=2)
            int a,
        @FormParam(value = "b")
            int b);

    @POST
    @Path("/algorithm/{code}")
    public Data process(
        @PathParam("code") // REST based WS: This argument is taken from a path
            String dCode,
            Data dData);
}

3) Add XML annotations for complex object
Default constructor is mandatory!!!
@XmlRootElement(name = "data")
public class Data {

  @XmlElement(name = "entry")
  public Collection getEntries() {
    return entries;
  }   

  public void setEntries(Collection entries) {
    this.entries = entries;
  }

}
@XmlRootElement
public class FieldEntry {
  // Create setter getter methods for 'field' and 'value'
}

4) Create the implementation
public class MyCalculatorServiceImpl implements CalculatorService

5) Create Spring context files
Inside resources directory (src/main/resources) create context files:
5.1) META-INF/spring/application-context.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:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="calculatorService"
    class="michsan.research.cxf.wsserver.impl.MyCalculatorServiceImpl" />
</beans>

5.2) META-INF/spring/ws-context.xml (Watch for namespace!)
<?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:p="http://www.springframework.org/schema/p"
  xmlns:jaxrs="http://cxf.apache.org/jaxrs"
  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">

  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <!-- REST based WS and SOAP based WS: Required -->
  <import resource="classpath:META-INF/cxf/cxf-extension-*.xml" />

  <!--    if (Simple Web Application) { -->
   <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
  <!--    } -->
 
  <!-- REST based WS: Root context -->
  <jaxrs:server id="wsServiceRoot" address="/rest">
    <jaxrs:serviceBeans>
      <ref bean="calculatorService" />
    </jaxrs:serviceBeans>
    <jaxrs:extensionMappings>
      <entry key="xml" value="application/xml" />
      <entry key="json" value="application/json" />
    </jaxrs:extensionMappings>
  </jaxrs:server>

</beans>


6) Create web.xml file
In src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <display-name>CXF Calculator Service</display-name>
  <description>Learning how to create CXF Webservice server</description>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/*-context.xml</param-value>
  </context-param>

  <!-- Creates the Spring Container shared by all Servlets and Filters -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <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>/ws/*</url-pattern>
  </servlet-mapping>

</web-app>


7) Deploy (or mvn tomcat:run)
8) See it running in http://localhost:8080/cxf-ws-server/ws

Consuming the REST based Webservice

1) On Maven pom.xml
<dependencies>
  <dependency>
    <artifactId>cxf-rt-frontend-jaxrs</artifactId>
    <groupId>org.apache.cxf</groupId>
    <version>2.6.0</version>
  </dependency>
</dependencies>

2) Understand how to invoke the service.
Usually the service provider gives you a page describing how to use the service. If this is the case: Data sent and received is in the following format
<data>
  <entry>
    <field>name</field>
    <value>JACK</value>
  </entry>
  <entry>
    <field>cif</field>
    <value>343434</value>
  </entry>
</data>
Next, you must use complex object Data (see previous post)
3) On static main:
Given that center of all REST services is on http://localhost:8080/cxf-ws-server/ws/rest and the exact REST resource is on calculator/algorithm/{NAME_OF_ALGORITHM} using POST method and XML format, then in main method:
String baseUri = "http://localhost:8080/cxf-ws-server/ws/rest";
WebClient client = WebClient.create(baseUri);
client.accept("application/xml");

// Service 1 ------------------------------
client.reset().path("calculator/sum");

Form form = new Form();
form.set("a", 4);
form.set("b", 9);

Response response = client.form(form);

if (response.getStatus() == 200) { // OK
    InputStream inStream = (InputStream) response.getEntity();
    BufferedReader reader = new BufferedReader(new InputStreamReader(
            inStream));
    System.out.println(reader.readLine());
    reader.close();
}


// Service 2 ------------------------------
client.reset().path("calculator/algorithm/INTEGRAL");

Data requestData = new Data();
requestData.add("arg1", "5");
requestData.add("arg2", "8");

Data responseData = client.post(requestData, Data.class);
// Print a response value
System.out.println(responseData.getEntries().iterator().next().getValue());

For testing the WS: http://niftybits.wordpress.com/2009/08/26/recipe-unit-testing-apache-cxf-restful-services/

Comments

ayu sari said…
This comment has been removed by the author.
ayu sari said…
Sehari-hari saya bekerja sebagai SPG menjual produk kosmetik terbaik di indonesia. Di sela waktu kerja saya browsing dengan hape untuk berkunjung ke berbagai blog. Saya mendapatkan banyak manfaat dan tambahan pengetahuan dengan membaca artikel seperti di blog ini. Trimakasih sudah berbagi.

Popular posts from this blog

Distributed OSGi services in JBoss Fuse

JBoss Fuse (Fuse ESB) is a great container featuring Apache Karaf as OSGi container. It has a great feature called Fabric, on which you can start another container efficiently and do provisioning from any container connected in one Fabric. It means that, you can uninstall bundles from in other container from your current container.

Fabric also allows a bundle to publish services and let them be consumed by other bundles. Not just by bundles in the same container, but in other bundles as long as publishers and consumers are connected in one Fabric.
I'm here not to to tell you how to use JBoss Fuse or what it is. But to tell you that it's been hard to find references about how to publish services and consume them in distributed environment. So, I want to share my experience to handle that with blueprint or with Spring DM 1.x.x based on scattered sources of information I have read for weeks.

Bagaimana Ujian SIM C secara sukses di SatPas Daan Mogot

Atas berkat rahmat Allah Yang Maha Pengasih lagi Maha Penyayang, saya berhasil mendapatkan SIM C secara legal dalam 1 kali kedatangan yakni sore tadi (23 Januari 2010). Makasih ya Allah.

Sorry, kali ini saya berbicara non IT. Mungkin ini adalah momen bersejarah bagi saya yang perlu direkam dalam blog saya dan ini juga merupakan sesuatu yang sangat penting untuk dibagikan kepada orang lain, seperti halnya judul blog saya "Let's share"

SELECT ... FOR UPDATE with Spring JDBC

SELECT ... FOR UPDATE is required to lock specific row in database in order to prevent other query thread to read or make changes to the same row. The lock is released once you update the data or commit it.

As a Spring lover, it's hard to find references about implementing the technique in Spring JDBC template. But finally I managed to use it.