Enterprise Java

Developing RESTful Services using Apache CXF

Introduction

As you already know there are two ways of developing a web service

  1. Simple Object Access Protocol (SOAP)
  2. Representational State Transfer (REST)

Before jumping on how to create a REST based web service using Apache CXF we shall see what is REST. REST is not a technology and certainly is not a standard of some kind. It is merely an architectural style that chalks down how to write a web service in a certain way. This style was defined by a certain Roy Fielding (ring bells? yep you guessed right, he is one of the architects of HTTP) in 2000. Principal protagonist of a REST architecture is a Resource which can be uniquely identified by an Uniform Resource Identifier or URI. State of a resource at any given point of time is represented by a document and is called Representation of resource. The client can update the state of resource by transferring the representation along with the request. The new representation is now returned to client along with the response.  The representation contains the information in formats like html, xml, JSON etc that is accepted by the resource. The resource which adheres to rules of REST architecture is called a RESTfull resource and web service that adheres to this rule are called RESTfull web service.

Create a project to contain your web service

I generally do my web development in struts2+spring using maven Strut2 starter archetype to create my web project. To use CXF in my project I add following dependencies to my POM

<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-frontend-jaxws</artifactId>
	<version>${cxf.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-transports-http</artifactId>
	<version>${cxf.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-transports-http-jetty</artifactId>
	<version>${cxf.version}</version>
</dependency>

The non maven users can find the details of dependencies to be added in following link: http://cxf.apache.org/docs/using-cxf-with-maven.html. The CXF can be directly downloaded from here: http://cxf.apache.org/download.html

How to Create a CXF RESTfull web-service?

Suppose you want to create a RESTfull web service using CXF for managing books in your personal bookshelf.  You would typically want to perform following operations on the bookshelf

  1. Add a book
  2. Update book information
  3. Delete a book from the shelf
  4. Get a book
  5. Get book list
  6. Get book list by author name

Following steps are needed create such a service

  1. Create BookVO, BookList (value object) for passing as representation in request and response.
  2. Bind the objects with request and response.
  3. Create the service implementation class to accept request and generate response.
  4. Registering your webservice with CXF container.
  5. Deploy the service in a web container.
  6. Create clients to invoke methods on the service.

Getting the source code for this tutorial

I have committed the source files for this tutorial in SVN.

Note: Both are ItelliJ maven projects so you can directly import them to your intelliJ IDE or copy over the files manually to other IDE

Create BookVO (value object) for passing as representation in request and response.

BookVO Class

package com.aranin.weblog4j.vo;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;

@XmlRootElement(name="Book")
public class BookVO implements Serializable{

private long bookId;
private String bookName;
private String author;

public long getBookId() {
return bookId;
}

public void setBookId(long bucketId) {
this.bookId = bookId;
}

public String getBookName() {
return bookName;
}

public void setBookName(String bookName) {
this.bookName = bookName;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}
}

BookList Class

package com.aranin.weblog4j.vo;

import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;

@XmlRootElement(name="BookList")
public class BookList {
private List<BookVO> bookList;

public List<BookVO> getBookList() {
if(bookList == null){
bookList = new ArrayList<BookVO>();
}
return bookList;
}

public void setBookList(List<BookVO> bookList) {
this.bookList = bookList;
}
}

Bind the Data object i.e. BookVO with request and response

To bind the BookVO with the request or response it needs to serialized into either XML or JSON streams. The serialization needs to be done using one of the data binding components. CXF uses JAXB for default data binding component. JaXB uses @XmlRootElement annotation to map the data object to the xml. You can see the use of XmlRootElement annotation in code above.

Create the service implementation class to accept request and generate response

Let us see how a CXF RestFull webservice looks. We will create a BookService class which will perform add,update,delete and get operations on BookSelf.

BookService Class

package com.aranin.weblog4j.services.rest;

import com.aranin.weblog4j.hashdb.HashDB;
import com.aranin.weblog4j.vo.BookVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

/**
 * Created by IntelliJ IDEA.
 * User: Niraj Singh
 * Date: 3/13/13
 * Time: 3:58 PM
 * To change this template use File | Settings | File Templates.
 */
public class BookService {

protected final Logger log = LoggerFactory.getLogger(BookService.class);

    @POST
    @Path("/getbook/{name}")
    @Produces({"application/xml","application/json"})
    @Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
    public Response getBucket(@PathParam("name") String name) {
        log.debug("name : " + name);
        BookVO bookVO = null;
        try {
            bookVO = HashDB.getBook(URLDecoder.decode(name, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        if(bookVO == null){
            return Response.status(Response.Status.BAD_REQUEST).build();
        }else{
            return Response.ok(bookVO).build();
        }
    }

    @POST
    @Path("/addbook")
    @Produces({"application/xml","application/json"})
    @Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
    public Response addBook(@FormParam("name") String bookName,
                            @FormParam("author") String author) {
        log.debug("inside addBook");
        BookVO bookVO = new BookVO();
        bookVO.setBookName(bookName);
        bookVO.setAuthor(author);
        HashDB.insertBook(bookVO);
        if(HashDB.getBook(bookName) == null){
            return Response.status(Response.Status.BAD_REQUEST).build();
        }else{
            return Response.ok(bookVO).build();
        }

    }
}

You can see two methods in the BookService class getBook and addBook. They are service methods for getting and adding a book. Rest of the methods for update delete etc can be written in same way. Now lets see what the various annotations and method call means.

  • @POST – This indicates that service receives only POST request.
  • @Path – This is the path of webservice. So the webservice can be invoked using following Url <base_url>/bookservice/getbook/{name} for fetching, <base_url>/bookservice/addbook for adding.
  • @Produces – Indicates the MIME type of response generated. In our case it is both application/xml and application/json.
  • @Consumes – Indicates the MIME type of request which this service can consume.

Registering your webservice with CXF container.

One cool thing with CXF is that it uses a spring based configuration for registering its webservice endpoints so let us create a beans.xml in WEB-INF and configure the CXF in web.xml. For this first we need to wire the beans.xml to get loaded by spring container.

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>

Secondly load the register the CXFServlet in web.xml.

<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

Now open your bean.xml and register your bookservice end point.

<?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:jaxws="http://cxf.apache.org/jaxws"
    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

http://cxf.apache.org/jaxws

http://cxf.apache.org/schemas/jaxws.xsd">

	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

	<jaxws:endpoint
	  id="bookShelfService"
	  implementor="com.aranin.weblog4j.services.BookShelfServiceImpl"
	  address="/bookshelfservice" />

    <bean id="bookserviceclass" class="com.aranin.weblog4j.services.rest.BookService"/>

    <jaxrs:server id="bookservice" address="/bookservice">
        <jaxrs:serviceBeans>
        <ref bean="bookserviceclass" />
        </jaxrs:serviceBeans>
    </jaxrs:server>

</beans>

Now your webservice is ready. Build your web application and deploy it in any servlet container.

Creating Client for your webservice

Clients can be created in many ways, I have used apache Http Components to write my client. The libraries can be found in http://hc.apache.org/httpclient-3.x/.

Maven user can pull the Http components jar using following

<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.3</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.1.3</version>
<scope>compile</scope>
</dependency>

Now to invoke the webservice I have created a util class called DemoRestClient.

package com.aranin.weblog4j.client;

import com.aranin.weblog4j.vo.BookVO;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;

import java.net.URLEncoder;

/**
 * Created by IntelliJ IDEA.
 * User: Niraj Singh
 * Date: 3/13/13
 * Time: 4:15 PM
 * To change this template use File | Settings | File Templates.
 */
public class DemoRestClient {
    public static void main(String[] args){
        DemoRestClient restClient = new DemoRestClient();
        try {
            //restClient.addBook("Naked Sun", "Issac Asimov");
            restClient.getBook("Naked Sun");
        } catch (Exception e) {
            e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
        }

    }

    public BookVO getBook(String bookName) throws Exception {

        String output = null;
        try{
            String url = "http://localhost:8080/weblog4jdemo/bookservice/getbook/";

            url = url + URLEncoder.encode(bookName, "UTF-8");

            HttpClient client = new HttpClient();
            PostMethod mPost = new PostMethod(url);
            client.executeMethod( mPost );
            Header mtHeader = new Header();
            mtHeader.setName("content-type");
            mtHeader.setValue("application/x-www-form-urlencoded");
            mtHeader.setName("accept");
            mtHeader.setValue("application/xml");
            mPost.addRequestHeader(mtHeader);
            client.executeMethod(mPost);
            output = mPost.getResponseBodyAsString( );
            mPost.releaseConnection( );
            System.out.println("out : " + output);
        }catch(Exception e){
            throw new Exception("Exception in retriving group page info : " + e);
        }
        return null;
    }

    public void addBook(String bookName, String author) throws Exception {

        String output = null;
        try{
            String url = "http://localhost:8080/weblog4jdemo/bookservice/addbook";
            HttpClient client = new HttpClient();
            PostMethod mPost = new PostMethod(url);
            mPost.addParameter("name", "Naked Sun");
            mPost.addParameter("author", "Issac Asimov");
            Header mtHeader = new Header();
            mtHeader.setName("content-type");
            mtHeader.setValue("application/x-www-form-urlencoded");
            mtHeader.setName("accept");
            mtHeader.setValue("application/xml");
            //mtHeader.setValue("application/json");
            mPost.addRequestHeader(mtHeader);
            client.executeMethod(mPost);
            output = mPost.getResponseBodyAsString( );
            mPost.releaseConnection( );
            System.out.println("output : " + output);
        }catch(Exception e){
        throw new Exception("Exception in adding bucket : " + e);
        }

    }

}

Run this client to see the output of your webservice. Right now it will sending xmloutput as the response accept header is “application/xml”. You can change it to application/json to get an json output.

Thats all folks. This is a very basic introduction to developing RestFull web service using apache CXF, there is a lot more to explore. Happy exploring, till then good bye. Please drop some comments as and if you read this to keep me inspired.
 

Reference: Developing RESTful Services using Apache CXF from our JCG partner Niraj Singh at the Weblog4j blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

30 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
clarenced
clarenced
11 years ago

Why are you using a @Post to get the getBucket?

Amit
Amit
11 years ago
Reply to  clarenced

It may b – getBook

Frankie
Frankie
10 years ago

that’s not restful at all

Sunil Gulabani
10 years ago

When we deploy the Apache CXF war file, all the SOAP and RESTful web services are listed. I want hide this listing of both the SOAP and RESTful web services.

I tried for the configuration in cxf-servlet.xml mentioned in:
http://stackoverflow.com/questions/12619171/hiding-the-restful-service-from-webservice-listapache-cxf

But no success :(

How can i achieve it ?

yash
yash
10 years ago

Thanks .
You saved my Day..!!

ravi
ravi
10 years ago

hi Niraj ,
In client code, why you added these two lines of code couple of times???

mtHeader.setName(“content-type”);
mtHeader.setValue(“application/x-www-form-urlencoded”);

mtHeader.setName(“accept”);
mtHeader.setValue(“application/xml”);

Niraj Singh
10 years ago
Reply to  ravi

Hi Ravi,

The headers are different

1. Content_type – This is the mime type of data that the client is sending to the server. In this case it is form data.
2. Accept – This header is set by client and it tells the server what kind of data client is expecting. The sever can read this header and respond accordingly.

Regards
Niraj

ravi
ravi
10 years ago
Reply to  Niraj Singh

Thanks niraj.

Simone
Simone
10 years ago
Reply to  Niraj Singh

Hi Niraj,

didn’t you forget to add the header after the first name-value pair?

Something like that:

Header mtHeader = new Header();
mtHeader.setName(“content-type”);
mtHeader.setValue(“application/x-www-form-urlencoded”);
mPost.addRequestHeader(mtHeader);

mtHeader = new Header();
mtHeader.setName(“accept”);
mtHeader.setValue(“application/xml”);
//mtHeader.setValue(“application/json”);
mPost.addRequestHeader(mtHeader);

Right?

Niraj Singh
9 years ago
Reply to  Simone

Hi Simone,

Sorry for late reply. You are absolutely correct. We need instantiate the header once more after adding it to the request.

Regards
Niraj

Dhanu
Dhanu
10 years ago

HashDB.getBook(URLDecoder.decode(name, “UTF-8”));

what is HashDB

and how do we send a xml data to Rest server using Chrome’s Advanced RestClien

Niraj Singh
9 years ago
Reply to  Dhanu

Hi Dhanu,

HashDB only contains a static hashmap. I just used this for this example. There is nothing more to it.

I have not used chrome’s advance rest client. But a quick google shows that there is a header tab where you can add new header Content_type – text/xml

Hope this helps.

Regards
Niraj

Rahul
Rahul
8 years ago
Reply to  Niraj Singh

Hi Niraj,

refering to the point that HashDB contians a static hashmap, can I assume following hashmap

private static Map myDB;

if yes, then, how does the insertBook(bookVO), work, should it not be insertBook(urldecoder(String),bookVO)

Please let me know if I am missing somehting

Rahul
Rahul
8 years ago
Reply to  Rahul

the map *
private static Map less thank String, BookVO greater than myDB;

Ramiro
Ramiro
9 years ago

You make a POST call when you are trying to GET a resource. Sorry but this is not RESTful.

Niraj Singh
9 years ago
Reply to  Ramiro

Hi Ramiro, Thanks for the comment. I agree that the above example is not truely a restful service. But I beg to differ on your point. Though it is a common practice to use various HTTP verbs a.k.a GET, PUT, DELETE, POST to perform crud operations i.e read create, delete and update. But it is only a practice (and a good one too). But using this practice does not constute a REST style or a RESTFul webservice. So if you read Dr. Fielding’s dissertation carefully a REST system has following properties. 1. Client–server. Above example is that. 2. Stateless –… Read more »

vivek
vivek
9 years ago

Hi … How to display that json response to the front end(jsp).

Niraj Singh
9 years ago
Reply to  vivek

Hi Vivek,

You can use javascript to read and parse the json. Once the json is parsed javascript can do some DOM manipulation to display the values in desired format. You can get lots of example on net. Here is one using jquery

http://runnable.com/UhY_jE3QH-IlAAAP/how-to-parse-a-json-file-using-jquery

Hope this helps.

Regards
Niraj

prashant
prashant
9 years ago

Hi Neeraj,

How we will consume JSON coming in request payload? Please help.

Niraj Singh
9 years ago
Reply to  prashant

Hi Prashant,

Depends where you are using the endpoint. You can use in a html etc using a javascript library see the above post. Assuming that you want to use it from a java client. You can use Jackson or Gson. Here is an simple example

responsePojo = new Gson().fromJson(result.toString(), responsePojo .getClass());

where result is a json response string.

Hope it helps

Regards
Niraj

hamza
hamza
9 years ago

can not download the projects :/

lahiru
lahiru
9 years ago

cannot download project :(

Sahil Pruthi
Sahil Pruthi
9 years ago

404 error when I’m trying to download the project. Request you to upload the project the web app and client again, Niraj. Thanks!

neeraj
neeraj
9 years ago

unable to download code using url’s

Willer Reis
Willer Reis
8 years ago

Hi… Thanks for the post.. !!

I have a question, and i need help. My CXF rest doesnt work with POSTS methods, just GET like this:

CXF REST SERVICE:

@POST
@Produces({“application/xml”,”application/json”})
@Consumes({“application/xml”,”application/json”,”application/x-www-form-urlencoded”})
@Path(“/news/subscribe”)
public Response subscribeNews( @QueryParam(“email_news”) String email_news, @QueryParam(“name_news”) String name_news, @QueryParam(“state_news”) String state_news) throws Exception;

CLIENT:
$.ajax({
type: ‘POST’,
url: ‘http://localhost:8080/api/news/subscribe’,
data: {
“email_news”: $scope.emailnews,
“name_news”: $scope.namenews,
“state_news”: $scope.statenews
},
contentType: ‘application/x-www-form-urlencoded; charset=utf-8’,
dataType: “json”,
headers: {
‘Content-Type’: ‘application/x-www-form-urlencoded’
},

If i replace POST to GET, its work perfectly!

could you help me please?

Tks

N
N
8 years ago

Hi,

Could you please help understand usage of jaxws end point?

john
john
8 years ago

Links DEAD

dude
dude
7 years ago
dude
dude
7 years ago
Reply to  dude
usm426
usm426
5 years ago

Any other option with out using @XmlRootElement(name=”Book”) annotation at the POJO class

Back to top button