SOA, Web Services, and RESTful Systems

Representational state transfer, or "REST" for short, is a less restrictive form of SOA than web services.


June 08, 2007
URL:http://www.drdobbs.com/web-development/soa-web-services-and-restful-systems/199902676

Eric is a consultant and contributing editor for Dr. Dobb's Journal. He can be contacted at [email protected].


Service-oriented architecture (SOA) and development is a paradigm where software components are created with concise interfaces, and each component performs a discrete set of related functions. With its well-defined interface and contract for usage, each component, provides a service to other software components. This is analogous to an accountant who provides a service to a business, even though that service consists of many related functions—bookkeeping, tax filing, investment management, and so on.

There are no technology requirements or restrictions with SOA. You can build a service in any language with standards such as CORBA, remote procedure calls (RPC), or XML. Although SOA has been around as a concept for years, its vague definition makes it difficult to identify. The client/server development model of the early '90s was a simple example of an SOA-based approach to software development.

A web service is an example of an SOA with a well-defined set of implementation choices. In general, the technology choices are SOAP and the Web Service Definition Language (WSDL); both XML-based. WSDL describes the interface (the "contract"), while SOAP describes the data that is transferred. Because of the platform-neutral nature of XML, SOAP, and WSDL, Java is a popular choice for web-service implementation due to its OS-neutrality.

Web-service systems are an improvement of client/server systems, and proprietary object models such as CORBA or COM, because they're standardized and free of many platform constraints. Additionally, the standards, languages, and protocols typically used to implement web services helps systems built around them to scale better.

Representational State Transfer (REST)

However, there exists an even less restrictive form of SOA than a web service—representational state transfer (REST). Described by Roy Fielding in his doctoral dissertation, REST is a collection of principals that are technology independent, except for the requirement that it be based on HTTP.

A system that conforms to the following set of principals is said to be "RESTful":

These principals map directly to those used in the development of the Web, and according to Fielding, account for much of the Web's success. HTTP protocol, its interface of methods (GET, POST, HEAD, and so on), the use of URLs, HTML, and JavaScript, as well as the clear distinction between what is a web server and web browser, all map directly to the first four principals. The final principal (involving tiers) allows for the common network technology found in most website implementations: load balancers, in-memory caches, firewalls, routers, and so on. These devices are acceptable because they don't affect the interfaces between the components; they merely enhance their performance and communication.

The Web is the premier example of a RESTful system, which makes sense since much of the Web's architecture preceded the definition of REST. What the Web makes clear, however, is that complex remote procedure call protocols are not needed to create successful, scalable, understandable, and reliable distributed software systems. Instead, the principals of REST are all you need.

Overall, REST can be described as a technology and platform-independent architecture where loosely coupled components communicate via interfaces over standard web protocols. Software, hardware, and data-centric designs maximize system efficiency, scalability, and network throughput. The underlying principal is simplicity.

REST differs from other software architecture in that it marries the concepts common to software architecture (interfaces, components, connectors, patterns, and so on) with those of network architecture (portability, bandwidth management, throughput measurement, protocol latencies, and so on). This combination makes REST ideal for distributed software systems where scalability, in terms of both processing power and communication efficiency, are critical.

Figure 1 illustrates the REST architecture, combining both logical software architecture and physical network elements. Communication is performed over HTTP, clients contain optional server caches for efficiency, services employ caches to backend databases, there are no restrictions on maximum clients, or maximum services per client, services can call services, load-balancing hardware is used for scalability, and firewalls can be used for security.

Figure 1: This architectural diagram provides a visual overview of the REST principals.

There are some interesting points on data caching that need to be made. First, data must be marked, either implicitly or explicitly, as cacheable, or noncacheable. Second, although specialized caches may be used (custom, in-memory data structures), general-purpose caches, such as web browser caches or third-party web caches (such as Akamai), can also be used.

Building RESTful Systems

If you eliminate typical web-service protocols (XML-RPC SOAP, WSDL, and so on), then how do you build an SOA-based RESTful system? With REST, you use that same mechanism used to request a web page—the HTTP query URL. For instance, the sample SOAP call in Example 1 makes a request for an employee's benefits information from a human resources web service.


<SOAP-ENV:Envelope xmlns:SOAP
 ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header> 
        some data here...    
    </SOAP-ENV:Header> 
    <SOAP-ENV:Body>
    <GetBenefits>
        <user>123-45-6789</user> 
        <type>full_time_employee</type> 
    </GetBenefits>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Example 1: Sample SOAP call to retrieve employment benefits for an employee.

With REST, you simply replace a SOAP call, such as Example 1, with the URL http://humanresources.com/benefits?user=<USER_SSID>&type=full_time_employee.

The HTTP query URL definition is all you need to know and use to make calls to a RESTful service. The response can be HTML, comma-delimited data, XML, or a more sophisticated document type (such as a spreadsheet). Some claim that the return of anything but hypermedia-based content is not truly RESTful. However, as long as the system stays true to the REST principals for the request and the communication protocol, the response type is unimportant.

When you build a web application with a Java Servlet, for example, it's straightforward to read the data passed through URL query parameters, and to return any text-based response to the caller. The Java Servlet doPost method implementation in Listing One illustrates this. Here, the parameters used in the query in Example 1 are read and used to retrieve a user's employee benefits. The results are encoded as human-readable text. Because this is an example of a RESTful service, the request can be initiated—and the response viewed—by web browsers, or any component in a distributed application.

protected void doPost( HttpServletRequest req, HttpServletResponse resp)
  throws ServletException, IOException
{
    ServletOutputStream out = resp.getOutputStream();
    String response;

    String userSSID = req.getParameter("user");
    String userType = req.getParameter("type");
    if ( userType.equals("full_time_employee")) {
        Employee emp = lookupUser(userSSID);
        String medPlan = emp.getMedicalPlan();
        String dntPlan = emp.getDentalPlan();
        String retPlan = emp.getRetirementPlan();
        Response = "User " + emp.getFullName() +
                   " has medical plan: " + medPlan +
                   ", and dental plan: " + dntPlan +
                   ", and retirement plan: " + retPlan;
    }
    else {
        // ...
    }
    // Output the response from the worker
    out.println(response);
}
Listing One

A REST Service Framework

Although web services tend to be more complex than REST services, there are web-service tools and libraries to help you avoid duplicating code and test effort. However, there aren't many available for REST; at least not for Java developers. To remedy this, I've built a REST service framework (available electronically; see "Resource Center," page 5) that helps avoid writing duplicate code for each service I need. This Java-based framework lets me focus on the interface and functionality I need to implement instead of the skeleton code required to implement a Java Servlet, and map URL query parameters to data.

There are two main components in the REST framework:

The REST worker objects are what you develop to provide service functionality. Every worker must implement the RestWorker interface as in Example 2.


public interface RestWorker 
{
    public String onRequest(Map paramMap);
    public boolean cacheReference();
}

Example 2: The RestWorker interface definition.

The RestWorker interface defines the following methods:

The REST Server defines a common HTTP URL query parameter, request, which is used to determine which REST worker object should be called. The value of this parameter is matched to the name of the class to invoke. For instance, when this request arrives:

http://<rest_server_name>   /restserver?request=EmpBenefits

the REST Server attempts to load a class with the name EmpBenefits, which implements the RestWorker interface, and calls its onRequest method. All of the additional URL query parameters, if there are any, are passed as a java.util.Map of named-value pairs.

Listing Two shows the doPost method for the Rest Server Java Servlet, where this work is done. After the request parameter is retrieved, the entire set of URL query parameters is retrieved via the call to HTTPServletRequest.getParameterMap. The request parameter is removed from this map because it's meaningful to the REST Server only, and not the worker object. Next, a call is made to getWorker, which first attempts to locate a reference to this request's worker object within a cache. If a precached reference is not found, a call is made to createWorker (see Listing Three).

protected void doPost( HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    ServletOutputStream out = resp.getOutputStream();
    String response;

    // Determine which worker this request is for
    String urlKey = req.getParameter("request");
    if ( urlKey == null ) {
        out.println("REST Server Ready");
        return;
    }
    // Get the URL query parameters (remove param "request")
    Map paramMap = req.getParameterMap();
    HashMap params = new HashMap(paramMap);
    params.remove("request");

    // Lookup the correct worker for this request and call it
    RestWorker worker = getWorker(urlKey);
    if ( worker != null )
        response = worker.onRequest(params);
    else
        response = "No REST worker for " + urlKey;
    // Output the response from the worker
    out.println(response);
}
Listing Two



public RestWorker createWorker(String className)
{
    try {
        Class compClass = Class.forName( className );
        if ( compClass != null )
        {            
            Object obj = compClass.newInstance();
            return (RestWorker)obj;
        }
    }
    catch ( Exception e ) {
        log(e.getMessage(), e);
    }
    return null;
}
Listing Three

The method createWorker uses the Class.forName library call to load a reference to the specified class name's java.lang.Class object, using Java Reflection. With a standard Java Servlet container, such as Apache Tomcat, only classes within the WEB-INF/classes directory of the web application will be located by default. Therefore, for this algorithm to work, you must place your RestWorker objects within this directory. Once the class is located, and its java.lang.Class object is loaded, the actual RestWorker object is instantiated via a call to Class.newInstance.

Next, a call is made to the worker object's cacheReference method. If True is returned, a reference to this object is stored in an in-memory cache to make subsequent requests perform a bit faster (the Java reflection code just described will no longer need to be invoked). If this method returns False, then the object reference is used for this call only. This lets you dynamically update your worker object on a running server by simply copying a new class object to the WEB-INF/classes directory. Subsequent requests should use this new class.

Finally, the worker object's onRequest method is called with the Map of parameters from the original HTTP request. The return value from this method (a String) is used as the response, and is returned to the web client. Therefore, your object can return HTML, comma-delimited data, XML, or a human-readable sentence. For instance, the output of a simple "echo" worker object (see Listing Four), as seen from a web browser, is in Figure 2. HTTP Requests made to this RestWorker object are sent back in the form of a paragraph that includes all of the request parameters and values.



public String onRequest(Map params)
{
    String resp = "Thank you for calling EchoWorker. ";
    if ( params.size() == 0 )
        return resp;
    resp += "\n\nHere are the parameters you passed: ";
    Set keys = params.keySet();
    Iterator keyIter = keys.iterator();
    while ( keyIter.hasNext() )
    {
        String key = (String)keyIter.next();
        String[] val = (String[])params.get(key);
        resp += "\n " + key + "=" + val[0];
    }
    return resp;
}
Listing Four

Figure 2: The HTTP Response from an "echo" RestWorker object as seen in a browser.

Conclusion

I've built many RESTful services, as well as web services, in different production systems with great success. In my experience, it's quicker and easier to build, deploy, and consume a REST service than a full-blown web service. If you haven't jumped into the world of SOA-based development because of the complexities of web-service development, give REST and this framework a try.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.