Friday, 11 July 2014

Web Services: REST vs. SOAP

Imagine, you have the WSDL file discribing a simple "Hello" web service. These are steps you need to provide a client, using JAX-WS technology :
  • run the wsimport utility to generate all necessary JAX-WS artifacts(java classes) from WSDL. Setting up correctly the wsimport options(attributes) isn't so trivial here
  • write the client code against(or using) the generated artifacts
Though, Netbeans5.5 radically simplifies the client scenario it is not always simple to detect the problems(if occure) or understand the request-response trafic on the wire.
There is an alternative way that could help: REST services. REST (REpresentational State Transfer) isn't a framework or toolkit but architectural style or approach trying to simplify the life in web services. See the RESTful Web Services.
I'd like to compare both styles on a simple HelloService client example.

SOAP Client (supported by Netbeans 5.5) :

This is an example of wsimport ant task that generates JAX-WS artifacts
<target name="wsimport-client-hello" depends="wsimport-init>
 <wsimport sourcedestdir="${build.generated.dir}/wsimport/client"
 extension="true" 
 package="soap.client"
 destdir="${build.generated.dir}/wsimport/binaries" 
 wsdl=""${basedir}/${conf.dir}/xml-resources/web-service-references/hello/wsdl/localhost_8080/SoapHello/hello.wsdl"
 wsdlLocation="http://localhost:8080/SoapHello/hello?wsdl"
 catalog="catalog.xml"/>
</target>
(fortunately all those crazy stuff is generated automatically for you by Netbeans5.5
This is the Netbeans5.5 Projects View with a JSP file from which the "HelloService" web service is called :
SOAP Client

And these are the soap messages on the wire :

SOAP Request


<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://duke.org/hello">
 <soapenv:Body>
 <ns1:hello>
 <name>John</name>
 </ns1:hello>
 </soapenv:Body>
</soapenv:Envelope>

SOAP Response


<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://duke.org/hello">
 <soapenv:Body>
 <ns1:helloResponse>
 <return>Hello John !</return>
 </ns1:helloResponse>
 </soapenv:Body>
</soapenv:Envelope>

Rather compliated, isn't it ? I cannot imagine creating soap request manually and pass it throug http protocol to get the result ("Hello John !" string).

REST Client for REST Service (not supported by Netbeans5.5):

Creating a HelloService client for HelloService created in a REST way is wery simple and supported by all browsers.
All you need to know is address of the web service :
http://localhost:8084/RestHello/HelloServiceand address of the resource:
/name/John
This is the resulted (XML)page :
REST Client

See that "http://localhost:8084/RestHello/HelloService/name/John" URLrepresents the logical resource, not the real html page. The server doesn't have the million of static html pages for each existing (and even non existing) name.
The resource can be accessed by using all HTTP methods: POST, GET, DELETE, PUT. In this case the HTTP GET method was used. 
The REST approach is characterized by creating a "network" of resources that can be accessed or manipulated by HTTP methods.
For example, the GET method can only return the representation of the resource but cannot modify the resource.
These are, in very few words, the main principles of the REST architecture.

Advantages and Disadvantages of SOAP and REST styles.

The SOAP/WSDL style is useful when a formal contract must be established to describe the interface that the web service offers. The Web Services Description Language (WSDL) describes the details such as messages, operations, bindings, and location of the web service. 
Also the SOAP/WSDL style is useful when the application architecture needs to handle asynchronous processing and invocation (e.g. using JAX-WS the assynchronous clients can be created).

The disadvantages of SOAP/WSDL style are 
  • its complexity: tools are required to create a client
  • heavier on Bandwidth : SOAP requires a heavy XML wrapper arround each request or response
  • complicated security rules
The advantages of REST style are
  • simplicity: REST client can be accessed  from any browser (however, this is only true for GET method. Data creation request requires also the XML wrapper). 
  • lighter on Bandwidth : data on the wire are usually bare xml elements (not wrapped within the <Envelope><Body> tags).
  • REST application security rules can be setup using the http standards:  the administrator (or firewall) can discern the intent of each message by analyzing the HTTP command used in the request.
    For example, a GET request can always be considered safe because it can't, by definition, modify any data.
The disadvantege of REST style is that it still doesn't cover all business requirements
  • there is no common standard accepted yet for the formal REST service description
  • REST requests (especially GET method) are not suitable for large amount of data
  • REST doesn't cover all web services standards, like Transactions, Security, Addressing, Trust, Coordination,

SOAP1.2 and REST

Good news is that SOAP1.2 moved forward to support the REST services architecture : the SOAP1.2 specification now allows certain types services to be exposed through URIs (although the response is still a SOAP message). Another good news is that JAX-WS 2.0 implemented few classes that support the REST style significantly : see the Provider interface or Dispatch  interface. Using those classes the REST Service and REST Client creation is pretty straightforward.

This is the example of REST HelloService implementation from RestHello web application.

package server;

import java.io.ByteArrayInputStream;
import java.util.StringTokenizer;
import javax.annotation.Resource;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.Provider;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPException;
import javax.xml.ws.http.HTTPBinding;
import javax.xml.ws.BindingType;

@WebServiceProvider
@BindingType(value=HTTPBinding.HTTP_BINDING)
public class HelloService implements Provider<Source> {
  @Resource(type=Object.class)
  protected WebServiceContext wsContext;
  public Source invoke(Source source) {
    try {
      MessageContext mc = wsContext.getMessageContext();
      String query = (String)mc.get(MessageContext.QUERY_STRING);
      String path = (String)mc.get(MessageContext.PATH_INFO);
      System.out.println("Query String = "+query);
      System.out.println("PathInfo = "+path);
      System.out.println("Request Method = "+mc.get(MessageContext.HTTP_REQUEST_METHOD));
      if ("GET".equals(mc.get(MessageContext.HTTP_REQUEST_METHOD))) {
        if (query != null && query.contains("name=")) {
          return createSource(query);
        } else if (path != null && path.contains("/name")) {
          return createSource(path);
        } else {
          // invalid URL resource
          throw new HTTPException(404);
        }
      } else {
        // not supported method
        throw new HTTPException(500);
      }
    } catch(Exception e) {
      e.printStackTrace();
      throw new HTTPException(500);
    }
  }

  private Source createSource(String str) {
    System.out.println("creating source");
    StringTokenizer st = new StringTokenizer(str, "=/");
    // skip the '/name/' or 'name=' parameter name
    String token = st.nextToken();
    // get the name
    String name = st.nextToken();
    String body =
    "<ns:helloResponse xmlns:ns=\\"http://duke.org/hello\\">"
    +"Hello "+name+" !"
    +"</ns:helloResponse>";
    Source source = new StreamSource(new ByteArrayInputStream(body.getBytes()));
    return source;
  }
}

These are the web.xml and sun-jaxws.xml files used in the RestHello Web Application (WEB-INF directory):

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">
   <listener>
     <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
   </listener>
   <servlet>
     <servlet-name>restful-hello</servlet-name>
     <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
   </servlet>
   <servlet-mapping>
     <servlet-name>restful-hello</servlet-name>
     <url-pattern>/HelloService/\*</url-pattern>
   </servlet-mapping>
</web-app>

<?xml version="1.0" encoding="UTF-8"?>
<endpoints
    xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
    version="2.0">

    <endpoint
        name="restful-hello"
        implementation="server.HelloService"
        url-pattern="/HelloService/\*" />
</endpoints>

No comments:

Post a Comment