You are on page 1of 9

Maintaining state with REST

Introduction
A webservice is a software system acknowledged by a URI, A Webservice is an application constituent accessible over open protocols, Webservices model follows the publish, find, and bind paradigm, Webservices ties together SOAP, WSDL, and UDDI to provide a simple plug-and-play interface for finding and invoking Webservices. Webservices are usually associated with SOAP. Using the REpresentational State Transfer architecture, REST is based on principles, which are used in the largest distributed application on the World Wide Web. Without intention there are many search engines, shops or booking systems that are already available as REST based Webservices. TheREpresentational State Transfer architecture is an architecture that describes how the Web should work. REST is neither a product nor a standard. The address space between REST and SOAP is a crucial difference. REST uses a global address space with URLs, over which each resource can be addressed. In REST, the resource is the center of attention. Each request to REST service from a client covers all the info and server does not grip client context in the session.

REST is stateless
HTTP is a protocol that treats each request as an independent transaction that is unrelated to any previous request so that the communication consists of independent pairs of requests and responses. Furthermore it does not require the server to retain session information or status about each communications partner for the duration of multiple requests. REST is an architectural style which is designed work upon HTTP and hence it is stateless. It requires each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. However there are exceptional cases where the architecture of an application demands saving session state with the service. The below sections talk about three ways to maintain state with REST.

How to make REST stateful?


There are three options discussed below-

1) Maintain state with the client


The statelessness in REST requires each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. Every request should be an independent request so that REST can be free of managing the sessions. This way REST increases its scalability by avoiding the overhead of managing sessions for millions of users. Independent request means that the request should send the state so that the REST can identify

the user and respond the accordingly. Infact not the REST, but the communication with REST becomes stateful here. For instance, below are two simple representations, the first representation is sent for logging in. <Login> <Name>centurylink.com</Name> <Password>rosewin#43445</Password> </Login> If you are logged in successfully you receive the below representation. <Login> <Success>true</Success> </Login> If you want to search for customer, you would need to send the below representation stating that you are in a state of successful logging in and would like to search all customers. <Customer> <Filter>All</Filter> <Success>true</Success> </Customer> In other words, every request is independent and the server does not need to remember your previous request and states. This is achieved in REST using the standard HTTP methods. The following list describes the meaning of the HTTP methods and how they are used by REST.

Method GET

POST

PUT DELETE

Description GET queries the representation of a resource. The execution of requests should be free from side effects. GET requests can be sent arbitrarily often. You cannot blame the client for effects caused by GET requests. That means a GET can be sent heedlessly. With POST you can change the state of a resource. For example you can add a good to a shopping cart. POST isn't free from side effects. For example you can change fields in a data base or start a new process on the server with a POST request. New resources can be produced with PUT or you can replace the content of existing resources. Resources can be deleted using DELETE.

Example scenario:

2) Use JAX-WS RI instead of JAX-RS


JAX-RS is not the only API which can be used to build REST. It is possible to use JAX-WS Provider and Dispatch APIs as well (Apache CXF is one of the commonly accepted framework which supports building REST style Webservices using JAX-WS Provider/Dispatch and XML binding). REST based architectures typically will use a lightweight data format, like JSON, to send data back and forth. This is in contrast to JAX-WS which uses XML. XML by itself is not so significantly heavier than JSON (which some people may argue), but with JAX-WS it's how much XML is used that ends up making REST with JSON the lighter option. It's mostly that JAX-WS specification includes lots of overhead in how it communicates. You may follow the below steps to build REST service using JAX-WS. Exposing a Restful Webservice with JAX-WS can be started by creating an implementation of the javax.xml.ws.Provider<T> interface. The Provider interface is the dynamic alternative to a standard endpoint implementation class. It is analogous to the javax.xml.ws.Dispatch interface used on the client. You will notice that Provider<T> is a generic class. It can support Provider<javax.xml.transform.Source> and Provider<javax.xml.soap.SOAPMessage> with the SOAP/HTTP binding, or Provider<javax.activation.DataSource> and Provider<javax.xml.transform.Source> with XML/HTTP binding. When creating an implementation of Provider you choose which form the requests and response messages will be processed by your implementation. The example Webservice that has shown below will add two numbers and return the result. It uses Provider<Source> for the provider implementation and uses the XML/HTTP binding. The first step in writing the Calculator Provider implementation is to declare the Calculator class and then declare a @Resource annotation that will be used by the JAX-WS runtime to inject a WebServiceContext into the CalculatorImpl instance. Public class CalculatorImpl implements Provider { @Resource protected WebServiceContext wsContext; } The next step is to implement the T Provider.invoke(T request) method. Notice that this method takes a Source object as the request and returns a Source object as the response. This corresponds to the type of Provider that has been created.

Public class CalculatorImpl implements Provider { @Resource protected WebServiceContext wsContext; public Source invoke(Source request) { try { } catch(Exception e) { e.printStackTrace(); throw new HTTPException(500); } } } For this example, the Calculator Webservice will accept requests by extracting the numbers to be added from either the URL path or from an HTTP query. The query string and path string can be retrieved from the MessageContext which is retrieved from the WebServiceContext wsContext that will be injected into the Calculator object. The following code can be used to retrieve the PATH_INFO from the URL and check to see if it is in the proper format. String path = (String)mc.get(MessageContext.PATH_INFO); if (path != null && path.contains("/num1") && path.contains("/num2")) { return prepareResultSource(path); }

The prepareResultSource(String str) method simply creates a Source object from a properly formatted MessageContext.PATH_INFO string, by extracting the two numbers to be added from the path and adding them together and calling the prepareResultSource(int sum)method. The source for these two methods is: private Source prepareResultSource(String str) { StringTokenizer st = new StringTokenizer(str, "=&/"); String token = st.nextToken(); int number1 = Integer.parseInt(st.nextToken()); st.nextToken(); int number2 = Integer.parseInt(st.nextToken()); int sum = number1+number2; return prepareResultSource(sum); } private Source prepareResultSource(int sum) { String body = "<ns:calculatorResponse xmlns:ns="http://java.duke.org"><ns:return>" +sum +"</ns:return></ns:calculatorResponse>"; Source source = new StreamSource( new ByteArrayInputStream(body.getBytes())); return source; }

So the example invokes method looks like the following. public Source invoke(Source source) { try { MessageContext mc = wsContext.getMessageContext(); // check for a PATH_INFO request String path = (String)mc.get(MessageContext.PATH_INFO); if (path != null && path.contains("/num1") && path.contains("/num2")) { return prepareResultSource(path); } throw new HTTPException(404); } catch(Exception e) { e.printStackTrace(); throw new HTTPException(500); } } You will notice that if the proper MessageContext.PATH_INFO is not found, the example throws a HTTPException(404). Instead of throwing this exception, the example endpoint can instead check the query string for the method arguments. Like the MessageContext.PATH_INFO, the query string can be retrieved from the MessageContext with the following code. String query = (String)mc.get(MessageContext.QUERY_STRING); Given this you can either pass the query string to the prepareResultSource(String str) method which will parse the parameters from the query string or you can use the standard ServletRequest object to extract the query arguments if you are deploying to a servlet container. If you were to deploy a Java SE based endpoint you should have to use the MessageContext.QUERY_STRING. The following code extracts the num1 and num2 query values, adds them together and invokes the prepareResultSource(int sum) method to create the Source object that will be returned from the invoke method. ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST); int num1 = Integer.parseInt(req.getParameter("num1")); int num2 = Integer.parseInt(req.getParameter("num2")); return prepareResultSource(num1+num2); To finish off the Calculator, you need to declare some annotations on the class that instructs the JAX-WS runtime how to use this class. The @WebServiceProvider annotation specifies that this class is a Provider based endpoint rather than a service endpoint implementation class that would be annotated with @WebService. The final annotation to be added is @BindingType(value=HTTPBinding.HTTP_BINDING). This annotation specifies that Calculator endpoint should be published using the HTTPBinding.HTTP_BINDING as opposed to a SOAPBinding.SOAP11HTTP_BINDING or a SOAPBinding.SOAP12HTTP_BINDING binding

@WebServiceProvider @BindingType(value=HTTPBinding.HTTP_BINDING) public class CalculatorImpl implements Provider { @Resource protected WebServiceContext wsContext; public Source invoke(Source source) { try { MessageContext mc = wsContext.getMessageContext(); // check for a PATH_INFO request String path = (String)mc.get(MessageContext.PATH_INFO); if (path != null && path.contains("/num1") && path.contains("/num2")) { return prepareResultSource(path); } String query = (String)mc.get(MessageContext.QUERY_STRING); System.out.println("Query String = "+query); ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST); int num1 = Integer.parseInt(req.getParameter("num1")); int num2 = Integer.parseInt(req.getParameter("num2")); return prepareResultSource(num1+num2); } catch(Exception e) { e.printStackTrace(); throw new HTTPException(500); } } private Source prepareResultSource(String str) { StringTokenizer st = new StringTokenizer(str, "=&/"); String token = st.nextToken(); int number1 = Integer.parseInt(st.nextToken()); st.nextToken(); int number2 = Integer.parseInt(st.nextToken()); int sum = number1+number2; return prepareResultSource(sum); } private Source prepareResultSource(int sum) { String body = "<ns:calculatorResponse xmlns:ns="http://java.duke.org"><ns:return>" +sum +"</ns:return></ns:calculatorResponse>"; Source source = new StreamSource( new ByteArrayInputStream(body.getBytes())); return source; } } To deploy the endpoint on a servlet container running with the JAX-WS RI you need to create a WAR file. The WAR file needs a very simple web.xml file to configure the JAX-WS RI servlet. The following will work.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"> <listener> <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener </listener> <servlet> <servlet-name>restful-calculator</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>restful-calculator</servlet-name> <url-pattern>/calculator/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app> Next you can add a sun-jaxws.xml deployment descriptor to the WAR file. The following is an example. <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0"> <endpoint name="restful-calculator" implementation="restful.server.calculatorImpl" wsdl="WEB-INF/wsdl/calculator.wsdl" url-pattern="/calculator/*" /> </endpoints>

You will notice that a wsdl file is specified sun-jaxws.xml deployment descriptor. This is currently necessary to tell the JAX-WS RI not to try to generate a WSDL for this endpoint. The contents of calculator.wsdl are simply the following. <?xml version="1.0" encoding="UTF-8"?> <definitions> </definitions> Once the calculator endpoint has been deployed, you can test it by entering the following URLs into a browser (slight modifications maybe necessary depending on your own configuration).
http://localhost:8080/jaxws-restful/calculator/num1/10/num2/20

should return the following: <ns:calculatorResponse xmlns:ns="http://java.duke.org"> <ns:return>30</ns:return> </ns:calculatorResponse>

http://localhost:8080/jaxws-restful/calculator?num1=10&num2=50

should return the following: <ns:calculatorResponse xmlns:ns="http://java.duke.org"> <ns:return>60</ns:return> </ns:calculatorResponse>

3) Use Stateful EJB as the REST component


Consider a situation where the webservice returns very large result after complex and time consuming computations; but client just need a subset of it. The service should be able to store the entire result at server side and serve what client needs at that point in time. Here REST can use stateful EJB as the underlying component and store the result there. The bean should be annotated with @SessionScoped in order to tell JAX-RS to create request objects with session lifetime. The default is @RequestScoped. Please see the example of this implementation belowpackage com.ctli.restdemo;

@Path("/similarity/") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Stateful @StatefulTimeout(600000) // 10 minutes @SessionScoped

public class ResourceCategories {

@POST @Path("/atom-count/") public List<Category> getCategories(JAXBElement<Category> cat) { ..... ..... ..... return categories; }

Pros & Cons


Each method discussed above has its own advantages and disadvantageous. As discussed in first option REST architectural style recommends maintaining the state at client side to improve scalability. However the types of clients for a service cannot be restricted and it can be a browser, a java application, a .NET application, a C++ application, a JavaScript application or even a browser. Managing the session for different types of client may not be unique and it could be an overhead if an application uses more than one types of client. In addition to it a heavy request and response is passed to and fro between the client and server even if they need just a subset of it. The other two options actually violate REST architectural style as they maintain state at server side. The enormous growth of the World Wide Web has shown in which extent web technologies scale. Millions of users use resources, which are offered by many thousands of servers. Proxies and caches increase the performance. Components such as servers, proxy and web applications can separately be installed and attended. There are lots of formats like HTML, SVG, AVI; even new formats can be easily added with MIME types. The size of the transferred documents varies from few bytes to many megabytes. In the web artificially sessions is generated with cookies and URL rewriting as HTTP is stateless. In this point REST deviates from the web. JAX-WS originally introduced to bind SOAP based websevices and now it is extended to support REST .Here we deviates from the standard rules and leave the additional burden to the server. Also the simplicity of REST is defeated reintroducing WSDL etc. REST architectures often use JSON to send and receive data. JAX-WS uses XML which is comparatively heavier than JSON. Also JAX-WS specification includes lots of overhead in how it communicates. This could be under performed in a mission critical application where thousands of requests to be processed at a time. The third one utilize EJB as the REST component and hence leverage all the underlying services like multithreading, resource pooling, security, transaction management along with session management. However the demerits mentioned above are applicable to this as well.

Conclusion
As we know Websevices are widely used over internet and its performance is became an important factor. REST only uses ripe standards such as HTTP or URLs, which are available already for some years. It takes advantage of well-known ideas used in the web such as hypertext and an unbounded, global address space. REST is designed to be stateless since HTTP is stateless. However there could be exceptional situations arise where the service has to maintain state to support specific architecture of an application. Here the decision to choose how to make it stateful has to be made elegantly considering its advantages and disadvantages.

You might also like