Using Defne Framework with RESTful Web Services via Apache Wink
Join the DZone community and get the full member experience.
Join For FreeDefne is a service oriented web application framework. The main motivation behind Defne is ease of use. Defne allows developers to concentrate on their business logic while it provides all other application requirements such as transaction and security. With Defne, you can implement Java EE based business logics easily.
Apache Wink is a complete Java based solution for implementing and consuming REST based Web Services. The goal of the Wink framework is to provide a reusable and extendable set of classes and interfaces that will serve as a foundation on which a developer can efficiently construct applications. Apache Wink 1.1 aims to be a fully compliant implementation of the JAX-RS v1.1 specification.
In this article, we are going to see how one implements a web application using Wink and Defne together. While Wink provides RESTful web services, Defne provides implementation of the business services with service oriented fashion.
Requirements
In this article we use SIwpas, http://code.google.com/p/siwpas, Simple Web Profile Application Server that contains all necessary libraries. If you wish to use Tomcat, Jetty or any other application server, you have to bundle necessary libraries with your application or server classpath.
Defne libraries could be downloaded from http://code.google.com/p/defne/. Apache Wink libraries could be downloaded from http://incubator.apache.org/wink/.
Sample Web Application
In this article, our application is a simple web application that
- Creates a book instance with "name and author" and returning "identity" of the created book : We will POST form parameters "name" and "author" to create a book instance.
- Gets a book info from given "book identity" : We will get book information using GET request, such as; http://localhost:8080/rest-example/books/{book_id}
RESTful Web Service
We first write RESTful Web Service
@Path("/books")
public class BookResource
{
@POST
@Consumes(value=MediaType.APPLICATION_FORM_URLENCODED)
@Produces(value=MediaType.TEXT_PLAIN)
public String createBook(MultivaluedMap<String, String> formParams)
{
Message message = MessageFactory.newMessage(IBookService.SERVICE_NAME, IBookService.ADD_OPERATION.OPERATION_NAME);
message.putMessageParameter(IBookService.ADD_OPERATION.INPUT.NAME, formParams.getFirst(IBookService.ADD_OPERATION.INPUT.NAME));
message.putMessageParameter(IBookService.ADD_OPERATION.INPUT.AUTHOR, formParams.getFirst(IBookService.ADD_OPERATION.INPUT.AUTHOR));
message = PojoServiceExecutor.execute(message);
return message.getMessageParameter(String.class, IBookService.ADD_OPERATION.OUTPUT.ID);
}
@GET
@Path(value="{bookid}")
public String showBookInfo(@PathParam(value="bookid") String id)
{
Message message = MessageFactory.newMessage(IBookService.SERVICE_NAME, IBookService.GET_INFO_OPERATION.OPERATION_NAME);
message.putMessageParameter(IBookService.GET_INFO_OPERATION.INPUT.ID, id);
message = PojoServiceExecutor.execute(message);
return message.getMessageParameter(Book.class, IBookService.GET_INFO_OPERATION.OUTPUT.BOOK).toString();
}
}
Here, you see RESTful root resource, BookResource that contains one sub-resource.
From Wink User Guide
Resources are one of the fundamental concepts in REST. REST emphasizes the manipulation of resources rather than issuing function calls. Resources have unique identifiers. In HTTP terms, this means associating every resource with at least one URL.
In order to manipulate a resource, requests are made with a specific HTTP method. For instance, in order to retrieve a representation of a resource, an HTTP GET request to the resource's URL is issued. In order to create a new item in a collection, an HTTP POST can be used with the collection URL. Application developers define resources and the HTTP methods in order to quickly manipulate them by using regular plain old Java objects and JAX-RS annotations.
Let's return our case, we have one Root Resource, BookResource, for handling " http://localhost:8080/rest-sample/rest/books" URL and one sub-resource for handling "http://localhost:8080/rest-sample/rest/books/{book_id}" URL.
- One is accessed using http://localhost:8080/rest-sample/rest/books. When the application form is submitted, "createBook" method is called by the Apache Wink runtime. It creates Defne message and call ADD_OPERATION of the Book Service.
- One is accessed using http://localhost:8080/rest-sample/rest/books/{book_id}. When you hit the url with the book_id, "showBookInfo" method is called by the Apache Winkruntime. It creates Defne message and call GET_INFO_OPERATION of the Book Service.
Defne Book Service
Here is the Defne Book Service interface and implementation.
Service interface,
public interface IBookService
{
String SERVICE_NAME = "BookService";
interface ADD_OPERATION{
String OPERATION_NAME = "createBook";
interface INPUT{
String NAME = "NAME";
String AUTHOR = "AUTHOR";
}
interface OUTPUT{
String ID = "ID";
}
}
interface GET_INFO_OPERATION{
String OPERATION_NAME = "getBookInfo";
interface INPUT{
String ID = "ID";
}
interface OUTPUT{
String BOOK = "BOOK";
}
}
}
And here is the implementation. Its very easy to write Defne services,
@Service(name="BookService")
public class BookServiceImpl
{
@Operation
@TransactionAttribute(TransactionPolicy.WITH_TRANSACTION)
@EntityManagerAttribute
public static Message createBook(Message message) throws DefneException
{
String name = message.getMessageParameter(String.class, IBookService.ADD_OPERATION.INPUT.NAME);
String author = message.getMessageParameter(String.class, IBookService.ADD_OPERATION.INPUT.AUTHOR);
Book book = new Book();
book.setName(name);
book.setAuthor(author);
EntityManager manager = EntityManagerUtil.getEntityManagerFromBag(message);
manager.persist(book);
Message oMessage = MessageFactory.newMessage();
oMessage.putMessageParameter(IBookService.ADD_OPERATION.OUTPUT.ID,Integer.toString(book.getId()));
return oMessage;
}
@Operation
@TransactionAttribute(TransactionPolicy.NO_TRANSACTION)
@EntityManagerAttribute
public static Message getBookInfo(Message message) throws DefneException
{
EntityManager manager = EntityManagerUtil.getEntityManagerFromBag(message);
Query query = manager.createQuery("select c from Book c where c.id=:id");
query.setParameter("id", Integer.parseInt(message.getMessageParameter(String.class, IBookService.GET_INFO_OPERATION.INPUT.ID)));
Book book = (Book) query.getSingleResult();
Message oMessage = MessageFactory.newMessage();
oMessage.putMessageParameter(IBookService.GET_INFO_OPERATION.OUTPUT.BOOK,book);
return oMessage;
}
}
TransactionAttribute and EntityManagerAttribute annotations are used for defining Transaction and JPA requirements of the service operation.
In the first operation, we get form parameters (name and author) and create Book instance. In the second operation, we get "id" of the book instance that we query for the book info.
Wink Application Class
To register our RESTful service with Wink, we have to write application class that extends javax.ws.rs.core.Application.
public class WinkBookApplication extends Application
{
@Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(BookResource.class);
return classes;
}
}
Our HTML Page
This is the our simple HTML page for posting form parameters that provide Book information.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>REST Example</title>
</head>
<body>
<form action="/rest-sample/rest/books/" method="post">
<label>Name : </label>
<input type="text" name="NAME"/>
<br/>
<label>Author : </label>
<input type="text" name="AUTHOR"/>
<br/>
<input type="submit" value="Add Book"/>
</form>
</body>
</html>
When the form is posted, Wink finds related RESTful service from URL and call related method, BookResource#createBook, with MultivaluedMap<String, String> instance that contains form parameters.
When you hit the URL like, http://localhost:8080/rest-sample/rest/books/2, Wink finds related RESTful service from URL and call related method, BookResource#showBookInfo with @PathParam(value="bookid") String instance that contains "2".
Configuration of the Wink
To use Wink, we have to add some configuration into "web.xml". Here is the complete web.xml file.
<web-app id="WebApp_ID" 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>Rest Example Application</display-name>
<listener>
<listener-class>org.defne.service.scanner.ScannerListener</listener-class>
</listener>
<servlet>
<servlet-name>Books</servlet-name>
<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.defne.server.rest.WinkBookApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Books</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
Here we register our Application instance with init-param. We also provide REST servlet URL handling, /rest/*. Listener is used for scanning Defne services.
beans.xml and defne-service.xml Files
Those files are used by the Apache OpenWebBeans and Defne respectively. defne-service.xml file is a marker file that is put into META-INF/ folder of the application classpath. Defne framework looks for this file to scan deployment. beans.xml is a CDI configuration file.
JPA persistence.xml File
Defne framework uses Java Persistence API for handling database operations. Here is the persistence.xml file used by the application,
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>org.defne.server.rest.Book</class>
<properties>
<property name="openjpa.jdbc.DBDictionary" value="hsql" />
<property name="openjpa.ConnectionDriverName" value="org.hsqldb.jdbcDriver" />
<property name="openjpa.ConnectionURL" value="jdbc:hsqldb:mem:test" />
<property name="openjpa.ConnectionUserName" value="sa" />
<property name="openjpa.ConnectionPassword" value="" />
<property name="openjpa.Log" value="DefaultLevel=TRACE"/>
<property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(ForeignKeys=true)"/>
</properties>
</persistence-unit>
</persistence>
We use Apache OpenJPA, that runtime is provided with SIwpas. If you wish to use another persistence provider, you have to bundle its libraries with your application and update configuration accordingly.
Complete Application
You can get complete rest-example application from Defne SVN, http://tiny.cc/zs9sx
Conclusion
Defne provides an easy way to write your business logic using Java EE technologies. Defne services could be called by different clients easily such as, Pure AJAX, Standalone Java, Java Servlet, JSF, RESTful Services etc. We will add more clients at the next release, such as JMS, TCP, Web Service etc.
Wink facilitates the development and consumption of REST web services by providing the means for modeling the service according to the REST architectural style. Wink provides the necessary infrastructure for defining and implementing the resources, representations and uniform methods that comprise a
service.
Defne URLs
Web
Site : http://code.google.com/p/defne/
SVN
Site : http://code.google.com/p/defne/source/browse/
Maven
Snapshot : https://oss.sonatype.org/content/repositories/snapshots/org/defne/
Maven : http://repo1.maven.org/maven2/org/defne/
Defne
Discussion : http://groups.google.com/group/defnedev
Apache Wink URLs
Web Site : http://incubator.apache.org/wink/
SVN Site : http://svn.apache.org/repos/asf/incubator/wink/
Enjoy!
Gurkan Erdogdu
ASF Member,http://apache.org
PMC Chair, Apache OpenWebBeans
CTO, MechSoft Mechanical and Software Solutions, http://www.mechsoft.com.tr
Opinions expressed by DZone contributors are their own.
Comments