RESTful Web Services with Restlet Framework
The acronym REST stands for Representational State Transfer, this basically means that each unique URL is a representation of some object. REST defines a set of architectural principles by which you can design Web services that focus on a system's resources, including how resource states are addressed and transferred over HTTP by a wide range of clients written in different languages.
Implementation of a REST Web service follows four basic design principles: - Use HTTP methods explicitly (GET, POST, PUT, DELETE).
- Be stateless.
- Expose directory structure-like URIs.
- Transfer XML, JavaScript Object Notation (JSON), or both.
- To create a resource on the server, use POST.
- To retrieve a resource, use GET.
- To change the state of a resource or to update it, use PUT.
- To remove or delete a resource, use DELETE.
The following code snippets show the implementation of RESTful Web Services using restlet framework 1.1.9, spring, xstream, jdbc.
UserResource class handles the incoming requests.
package service;
import org.apache.log4j.Logger;
import org.restlet.Context;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.ResourceException;
import org.restlet.resource.StringRepresentation;
import org.restlet.resource.Variant;
import org.springframework.context.ApplicationContext;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import data.beans.UserInfo;
import data.bo.UserInfoBO;
import data.util.ApplicationContextUtil;
/**
* Resource which handle userInfo resource requests.
*/
public class UserResource extends Resource {
private static Logger log = Logger.getLogger(UserResource.class);
@Override
public boolean allowPost() {
return true;
}
@Override
public boolean allowDelete() {
return true;
}
public UserResource(Context context, Request request, Response response) {
super(context, request, response);
// This representation has only one type of representation.
getVariants().add(new Variant(MediaType.APPLICATION_XML));
}
/**
* Handle a GET request and returns xml representation of user.
*/
@Override
public Representation represent(Variant variant) throws ResourceException {
String username = (String) getRequest().getAttributes().get("username");
if (username != null) {
ApplicationContext ctx = ApplicationContextUtil.getApplicationContextInstance();
UserInfoBO userInfoDAO = (UserInfoBO) ctx.getBean("userInfoBO");
UserInfo userInfo = userInfoDAO.getUserInfo(username);
if (userInfo == null)
return new StringRepresentation("UserInfo with username : " + username + " doesn't exists.");
XStream xstream = new XStream(new DomDriver());
xstream.alias("userInfo", UserInfo.class);
Representation result = new StringRepresentation(xstream.toXML(userInfo));
return result;
}
return new StringRepresentation("Invalid Request.");
}
/**
* Handle a POST request.
*/
@Override
public void acceptRepresentation(Representation entity) {
log.info("acceptRepresentation -- ");
String ext = (String) getRequest().getAttributes().get("resType");
try {
if (ext.equalsIgnoreCase("xml")) {
String xmlString = entity.getText();
log.info("input data : " + xmlString);
XStream xstream = new XStream(new DomDriver());
xstream.alias("userInfo", UserInfo.class);
UserInfo userInfo = (UserInfo) xstream.fromXML(xmlString);
log.info("userInfo : " + userInfo);
ApplicationContext ctx = ApplicationContextUtil.getApplicationContextInstance();
UserInfoBO userInfoDAO = (UserInfoBO) ctx.getBean("userInfoBO");
UserInfo dbUserInfo = userInfoDAO.getUserInfo(userInfo.getUserName());
if (dbUserInfo != null) {
getResponse().setEntity(new StringRepresentation("User already exists.\n" + xstream.toXML(dbUserInfo)));
return;
}
userInfo = userInfoDAO.createUserInfo(userInfo);
Representation rep = new StringRepresentation(xstream.toXML(userInfo));
getResponse().setEntity(rep);
} else {
getResponse().setEntity(new StringRepresentation("Invalid Request."));
}
} catch (Exception e) {
e.printStackTrace();
getResponse().setEntity(new StringRepresentation("Bad Request."));
}
}
/**
* Handle DELETE requests.
*/
@Override
public void removeRepresentations() throws ResourceException {
String username = (String) getRequest().getAttributes().get("username");
if (username != null) {
ApplicationContext ctx = ApplicationContextUtil.getApplicationContextInstance();
UserInfoBO userInfoDAO = (UserInfoBO) ctx.getBean("userInfoBO");
UserInfo userInfo = userInfoDAO.getUserInfo(username);
if (userInfo == null) {
getResponse().setEntity(new StringRepresentation("UserInfo with username : " + username + " doesn't exists."));
return;
}
XStream xstream = new XStream(new DomDriver());
xstream.alias("userInfo", UserInfo.class);
userInfoDAO.deleteUserInfo(userInfo);
getResponse().setEntity(
new StringRepresentation("UserInfo with username : " + username + " is deleted.\n"
+ xstream.toXML((userInfo))));
} else {
getResponse().setEntity(new StringRepresentation("Bad Request."));
}
}
}
In RESTful Web Services, URL will be mapped to Resource classes.
The following ApplicationRouter class maps the URLs to the UserResource class.
** We can also run the Router class to handle the request without deploying in server.
package service;
import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Restlet;
import org.restlet.Router;
import org.restlet.data.Protocol;
public class ApplicationRouter extends Application {
/**
* Creates a root Restlet that will receive all incoming calls.
*/
@Override
public synchronized Restlet createRoot() {
// Create a router Restlet that defines routes.
Router router = new Router(getContext());
router.attach("/user/user.{resType}", UserResource.class);
router.attach("/user/{username}/user.{resType}", UserResource.class);
return router;
}
public static void main(String[] args) {
try {
// Create a new Component.
Component component = new Component();
// Add a new HTTP server listening on port 8182.
component.getServers().add(Protocol.HTTP, 9100);
// Attach the sample application.
component.getDefaultHost().attach(new ApplicationRouter());
// Start the component.
component.start();
} catch (Exception e) {
// Something is wrong.
e.printStackTrace();
}
}
}
UserInfoBO class wraps the database calls to DAO.
package data.bo;
import java.util.List;
import data.beans.UserInfo;
import data.dao.UserInfoDAO;
public class UserInfoBO {
private UserInfoDAO userInfoDAO;
public UserInfoDAO getUserInfoDAO() {
return userInfoDAO;
}
public void setUserInfoDAO(UserInfoDAO userInfoDAO) {
this.userInfoDAO = userInfoDAO;
}
public UserInfo getUserInfo(String userName) {
List<UserInfo> userInfo = userInfoDAO.select(userName);
if (userInfo != null && userInfo.size() > 0)
return userInfo.get(0);
return null;
}
public void deleteUserInfo(UserInfo userInfo) {
userInfoDAO.delete(userInfo);
}
public UserInfo createUserInfo(UserInfo userInfo) {
userInfoDAO.create(userInfo);
return getUserInfo(userInfo.getUserName());
}
}
UserInfoDAO.java
package data.dao;
import java.util.List;
import data.beans.UserInfo;
public interface UserInfoDAO {
void create(UserInfo userInfo);
List<UserInfo> select(String userId);
void delete(UserInfo userInfo);
}
UserInfoDAOImpl.java – handle the database operations with USERS_INFO table.
package data.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import data.beans.UserInfo;
public class UserInfoDAOImpl implements UserInfoDAO {
private DataSource dataSource;
public void setDataSource(DataSource ds) {
dataSource = ds;
}
public DataSource getDataSource() {
return dataSource;
}
public void create(UserInfo userInfo) {
JdbcTemplate insert = new JdbcTemplate(dataSource);
insert.update(
"INSERT INTO USERS_INFO(USER_ID, USERNAME, STATE, COUNTRY, IS_ACTIVE) VALUES(USER_ID_SEQ.NEXTVAL, ?,?,?,?)",
new Object[] { userInfo.getUserName(), userInfo.getState(), userInfo.getCountry(), userInfo.getIsActive() });
}
public List<UserInfo> select(String userName) {
JdbcTemplate insert = new JdbcTemplate(dataSource);
return insert.query("SELECT USER_ID, USERNAME, STATE, COUNTRY, IS_ACTIVE FROM USERS_INFO WHERE USERNAME = ?",
new Object[] { userName }, new RowMapper() {
public Object mapRow(ResultSet rs, int arg1) throws SQLException {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(rs.getLong(1));
userInfo.setUserName(rs.getString(2));
userInfo.setState(rs.getString(3));
userInfo.setCountry(rs.getString(4));
userInfo.setIsActive(rs.getString(5));
return userInfo;
}
});
}
public void delete(UserInfo userInfo) {
JdbcTemplate delete = new JdbcTemplate(dataSource);
delete.update("DELETE FROM USERS_INFO WHERE USER_ID = ?", new Object[] { userInfo.getUserId() });
}
}
ApplicationContextUtil.java provides the access to spring configuration and provide the ApplicationContext reference.
package data.util;
import java.io.IOException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextUtil {
private static final String CONTEXT_CONFIG = "applicationContext.xml";
private static ApplicationContext applicationContext = null;
private ApplicationContextUtil() {
}
public static ApplicationContext getApplicationContextInstance() {
if (applicationContext == null) {
synchronized (ApplicationContextUtil.class) {
if (applicationContext == null) {
try {
applicationContext = createApplicationContext();
} catch (IOException io) {
io.printStackTrace();
}
}
}
}
return applicationContext;
}
private static ApplicationContext createApplicationContext() throws IOException {
return new ClassPathXmlApplicationContext(CONTEXT_CONFIG);
}
}
Configuration for spring and jdbc integration.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url"
value="jdbc:oracle:thin:@sbkj2kdbd02.wsjdev.dowjones.net:1521:COMMERCE" />
<property name="username" value="provision" />
<property name="password" value="provision123" />
</bean>
<bean id="userInfoDAO" class="data.dao.UserInfoDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="userInfoBO" class="data.bo.UserInfoBO">
<property name="userInfoDAO" ref="userInfoDAO" />
</bean>
</beans>
If you want deploy it in server, please use the following configuration in web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>user.service2</display-name>
<!-- Application class name -->
<context-param>
<param-name>org.restlet.application</param-name>
<param-value>service.ApplicationRouter</param-value>
</context-param>
<!-- Restlet adapter -->
<servlet>
<servlet-name>RestletServlet</servlet-name>
<servlet-class>org.restlet.ext.servlet.ServerServlet</servlet-class>
</servlet>
<!-- Catch all requests -->
<servlet-mapping>
<servlet-name>RestletServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Database Objects
USERS_INFO Table:
CREATE TABLE USERS_INFO
(USER_ID NUMBER,
USERNAME VARCHAR2(100) unique,
STATE VARCHAR2(100),
COUNTRY VARCHAR2(100),
IS_ACTIVE CHAR(1))
USER_ID_SEQ Sequence:
CREATE SEQUENCE USER_ID_SEQ
INCREMENT BY 1
START WITH 1
NOMAXVALUE;
TestRequests.java to test the UserResource.java
package test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
public class TestRequests {
public static void postMethod() {
System.out.println("in post method");
HttpClient client = new HttpClient();
PostMethod method = new PostMethod("http://localhost:9100/user/user.xml");
BufferedReader br = null;
try {
String xmlString = "<userInfo><userName>testuser</userName><state>NJ</state><country>USA </country><isActive>Y</isActive></userInfo>";
method.setRequestEntity(new StringRequestEntity(xmlString, "application/xml", null));
int returnCode = client.executeMethod(method);
System.out.println("Return Code : " + returnCode);
if (returnCode == HttpStatus.SC_NOT_IMPLEMENTED) {
System.err.println("The Post method is not implemented by this URI");
} else {
br = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
String readLine;
while (((readLine = br.readLine()) != null)) {
System.out.println(readLine);
}
}
} catch (Exception e) {
System.err.println(e);
} finally {
method.releaseConnection();
if (br != null)
try {
br.close();
} catch (Exception fe) {
}
}
}
public static void getMethod() {
System.out.println("in get method");
HttpClient client = new HttpClient();
GetMethod method = new GetMethod("http://localhost:9100/user/testuser/user.xml");
BufferedReader br = null;
try {
int returnCode = client.executeMethod(method);
System.out.println("Return Code : " + returnCode);
if (returnCode == HttpStatus.SC_NOT_IMPLEMENTED) {
System.err.println("The Post method is not implemented by this URI");
} else {
br = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
String readLine;
while (((readLine = br.readLine()) != null)) {
System.out.println(readLine);
}
}
} catch (Exception e) {
} finally {
method.releaseConnection();
if (br != null)
try {
br.close();
} catch (Exception fe) {
}
}
}
public static void deleteMethod() {
System.out.println("in delete method");
HttpClient client = new HttpClient();
DeleteMethod method = new DeleteMethod("http://localhost:9100/user/testuser/user.xml");
BufferedReader br = null;
try {
int returnCode = client.executeMethod(method);
System.out.println("Return Code : " + returnCode);
if (returnCode == HttpStatus.SC_NOT_IMPLEMENTED) {
System.err.println("The Post method is not implemented by this URI");
} else {
br = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
String readLine;
while (((readLine = br.readLine()) != null)) {
System.out.println(readLine);
}
}
} catch (Exception e) {
} finally {
method.releaseConnection();
if (br != null)
try {
br.close();
} catch (Exception fe) {
}
}
}
public static void main(String[] args) {
try {
postMethod();
//deleteMethod();
//getMethod();
} catch (Exception e) {
}
}
}
Required Jar files:
antlr.jar
cglib.jar
com.noelios.restlet-1.1.9.jar
com.noelios.restlet.ext.httpclient-1.1.9.jar
com.noelios.restlet.ext.servlet-1.1.9.jar
com.noelios.restlet.ext.simple-1.1.9.jar
commons-codec-1.4.jar
commons-collections-3.2.1.jar
commons-httpclient-3.1.jar
commons-logging-1.1.1.jar
dom4j.jar
jboss-j2ee.jar
log4j-1.2.15.jar
ojdbc14.jar
org.restlet-1.1.9.jar
org.restlet.ext.json-1.1.9.jar
org.simpleframework-3.1.3.jar
servlet-api.jar
spring.jar
xstream-1.3.1.jar
No comments:
Post a Comment