Science and technology

An instance of very light-weight RESTful internet providers in Java

Web providers, in a single type or one other, have been round for greater than twenty years. For instance, XML-RPC services appeared within the late 1990s, adopted shortly by ones written within the SOAP offshoot. Services within the REST architectural style additionally made the scene about twenty years in the past, quickly after the XML-RPC and SOAP trailblazers. REST-style (hereafter, Restful) providers now dominate in common websites akin to eBay, Facebook, and Twitter. Despite the alternate options to internet providers for distributed computing (e.g., internet sockets, microservices, and new frameworks for remote-procedure calls), Restful internet providers stay enticing for a number of causes:

  • Restful providers construct upon current infrastructure and protocols, particularly, internet servers and the HTTP/HTTPS protocols. An group that has HTML-based web sites can readily add internet providers for purchasers extra within the information and underlying performance than within the HTML presentation. Amazon, for instance, has pioneered making the identical data and performance out there via each web sites and internet providers, both SOAP-based or Restful.

  • Restful providers deal with HTTP as an API, thereby avoiding the difficult software program layering that has come to characterize the SOAP-based method to internet providers. For instance, the Restful API helps the usual CRUD (Create-Read-Update-Delete) operations via the HTTP verbs POST-GET-PUT-DELETE, respectively; HTTP standing codes inform a requester whether or not a request succeeded or why it failed.

  • Restful internet providers could be as easy or difficult as wanted. Restful is a method—certainly, a really versatile one—relatively than a set of prescriptions about how providers needs to be designed and structured. (The attendant draw back is that it might be exhausting to find out what does not rely as a Restful service.)

  • For a client or consumer, Restful internet providers are language- and platform-neutral. The consumer makes requests in HTTP(S) and receives textual content responses in a format appropriate for contemporary information interchange (e.g., JSON).

  • Almost each general-purpose programming language has no less than ample (and infrequently sturdy) assist for HTTP/HTTPS, which signifies that web-service purchasers could be written in these languages.

This article explores light-weight Restful providers in Java via a full code instance.

The Restful novels internet service

The Restful novels internet service consists of three programmer-defined courses:

  • The Novel class represents a novel with simply three properties: a machine-generated ID, an creator, and a title. The properties could possibly be expanded for extra realism, however I wish to maintain this instance easy.
  • The Novels class consists of utilities for varied duties: changing a plain-text encoding of a Novel or an inventory of them into XML or JSON; supporting the CRUD operations on the novels assortment; and initializing the gathering from information saved in a file. The Novels class mediates between Novel situations and the servlet.
  • The NovelsServlet class derives from HttpServlet, a sturdy and versatile piece of software program that has been round for the reason that very early enterprise Java of the late 1990s. The servlet acts as an HTTP endpoint for consumer CRUD requests. The servlet code focuses on processing consumer requests and producing the suitable responses, leaving the devilish particulars to utilities within the Novels class.

Some Java frameworks, akin to Jersey (JAX-RS) and Restlet, are designed for Restful providers. Nonetheless, the HttpServlet by itself offers a light-weight, versatile, highly effective, and well-tested API for delivering such providers. I am going to show this with the novels instance.

Deploy the novels internet service

Deploying the novels internet service requires an internet server, after all. My selection is Tomcat, however the service ought to work (well-known final phrases!) if it is hosted on, for instance, Jetty or perhaps a Java Application Server. The code and a README that summarizes methods to set up Tomcat are available on my website. There can be a documented Apache Ant script that builds the novels service (or some other service or web site) and deploys it below Tomcat or the equal.

Tomcat is obtainable for obtain from its website. Once you put in it domestically, let TOMCAT_HOME be the set up listing. There are two subdirectories of quick curiosity:

  • The TOMCAT_HOME/bin listing incorporates startup and cease scripts for Unix-like techniques (startup.sh and shutdown.sh) and Windows (startup.bat and shutdown.bat). Tomcat runs as a Java utility. The internet server’s servlet container is called Catalina. (In Jetty, the online server and container have the identical title.) Once Tomcat begins, enter http://localhost:8080/ in a browser to see intensive documentation, together with examples.

  • The TOMCAT_HOME/webapps listing is the default for deployed web sites and internet providers. The simple solution to deploy a web site or internet service is to repeat a JAR file with a .struggle extension (therefore, a WAR file) to TOMCAT_HOME/webapps or a subdirectory thereof. Tomcat then unpacks the WAR file into its personal listing. For instance, Tomcat would unpack novels.struggle right into a subdirectory named novels, leaving novels.struggle as-is. A web site or service could be eliminated by deleting the WAR file and up to date by overwriting the WAR file with a brand new model. By the way in which, step one in debugging a web site or service is to verify that Tomcat has unpacked the WAR file; if not, the positioning or service was not revealed due to a deadly error within the code or configuration.

  • Because Tomcat listens by default on port 8080 for HTTP requests, a request URL for Tomcat on the native machine begins:

    http://localhost:8080/

    Access a programmer-deployed WAR file by including the WAR file’s title however with out the .struggle extension:

    http://locahost:8080/novels/

    If the service was deployed in a subdirectory (e.g., myapps) of TOMCAT_HOME, this is able to be mirrored within the URL:

    http://locahost:8080/myapps/novels/

    I am going to provide extra particulars about this within the testing part close to the top of the article.

As famous, the ZIP file on my homepage incorporates an Ant script that compiles and deploys a web site or service. (A duplicate of novels.struggle can be included within the ZIP file.) For the novels instance, a pattern command (with % because the command-line immediate) is:

% ant -Dwar.title=novels deploy

This command compiles Java supply information after which builds a deployable file named novels.struggle, leaves this file within the present listing, and copies it to TOMCAT_HOME/webapps. If all goes nicely, a GET request (utilizing a browser or a command-line utility, akin to curl) serves as a primary take a look at:

% curl http://localhost:8080/novels/

Tomcat is configured, by default, for scorching deploys: the online server doesn’t should be shut all the way down to deploy, replace, or take away an internet utility.

The novels service on the code degree

Let’s get again to the novels instance however on the code degree. Consider the Novel class under:

Example 1. The Novel class

bundle novels;

import java.io.Serializable;

public class Novel implements Serializable, Comparable<Novel>
    static remaining lengthy serialVersionUID = 1L;
    non-public String creator;
    non-public String title;
    non-public int id;

    public Novel()

    public void setAuthor(remaining String creator) this.creator = creator;
    public String getAuthor()
    public void setTitle(remaining String title)
    public String getTitle() return this.title;
    public void setId(remaining int id) this.id = id;
    public int getId() return this.id;

    public int evaluateTo(remaining Novel different) return this.id - different.id;

This class implements the evaluateTo methodology from the Comparable interface as a result of Novel situations are saved in a thread-safe ConcurrentHashMap, which doesn’t implement a sorted order. In responding to requests to view the gathering, the novels service kinds a group (an ArrayList) extracted from the map; the implementation of evaluateTo enforces an ascending sorted order by Novel ID.

The class Novels incorporates varied utility features:

Example 2. The Novels utility class

bundle novels;

import java.io.IOException;
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.nio.file.Files;
import java.util.stream.Stream;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Collections;
import java.beans.XMLEncoder;
import javax.servlet.ServletContext; // not in JavaSE
import org.json.JSONObject;
import org.json.XML;

public class Novels {
    non-public remaining String fileName = "/WEB-INF/data/novels.db";
    non-public ConcurrentMap<Integer, Novel> novels;
    non-public ServletContext sctx;
    non-public AtomicInteger mapKey;

    public Novels()
        novels = new ConcurrentHashMap<Integer, Novel>();
        mapKey = new AtomicInteger();
   

    public void setServletContext(ServletContext sctx) this.sctx = sctx;
    public ServletContext getServletContext()

    public ConcurrentMap<Integer, Novel> getConcurrentMap()
        if (getServletContext() == null) return null; // not initialized
        if (novels.measurement() < 1) populate();
        return this.novels;
   

    public String toXml(Object obj) // default encoding
        String xml = null;
        strive
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            XMLEncoder encoder = new XMLEncoder(out);
            encoder.writeObject(obj);
            encoder.shut();
            xml = out.toString();
       
        catch(Exception e)
        return xml;
   

    public String toJson(String xml)

    public int addNovel(Novel novel)

    non-public void populate() {
        InputStream in = sctx.getResourceAsStream(this.fileName);
        // Convert novel.db string information into novels.
        if (in != null) {
            strive
                InputStreamReader isr = new InputStreamReader(in);
                BufferedReader reader = new BufferedReader(isr);

                String report = null;
                whereas ((report = reader.readLine()) != null)
                in.shut();
           
            catch (IOException e)
        }
    }
}

The most complex methodology is populate, which reads from a textual content file contained within the deployed WAR file. The textual content file incorporates the preliminary assortment of novels. To open the textual content file, the populate methodology wants the ServletContext, a Java map that incorporates all the important details about the servlet embedded within the servlet container. The textual content file, in flip, incorporates data akin to this:

Jane Austen!Persuasion

The line is parsed into two components (creator and title) separated by the bang image (!). The methodology then builds a Novel occasion, units the creator and title properties, and provides the novel to the gathering, which acts as an in-memory information retailer.

The Novels class additionally has utilities to encode the novels assortment into XML or JSON, relying upon the format that the requester prefers. XML is the default, however JSON is obtainable upon request. A light-weight XML-to-JSON bundle offers the JSON. Further particulars on encoding are under.

Example three. The NovelsServlet class

bundle novels;

import java.util.concurrent.ConcurrentMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.beans.XMLEncoder;
import org.json.JSONObject;
import org.json.XML;

public class NovelsServlet extends HttpServlet {
    static remaining lengthy serialVersionUID = 1L;
    non-public Novels novels; // back-end bean

    // Executed when servlet is first loaded into container.
    @Override
    public void init()

    // GET /novels
    // GET /novels?id=1
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)

    // POST /novels
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)

    // PUT /novels
    @Override
    public void doPut(HttpServletRequest request, HttpServletResponse response)

    // DELETE /novels?id=1
    @Override
    public void doDelete(HttpServletRequest request, HttpServletResponse response)

    // Methods Not Allowed
    @Override
    public void doTrace(HttpServletRequest request, HttpServletResponse response)

    @Override
    public void doHead(HttpServletRequest request, HttpServletResponse response)

    @Override
    public void doOptions(HttpServletRequest request, HttpServletResponse response)

    // Send the response payload (Xml or Json) to the consumer.
    non-public void sendResponse(HttpServletResponse response, String payload)
        strive
        catch(Exception e)
   
}

Recall that the NovelsServlet class above extends the HttpServlet class, which in flip extends the GenericServlet class, which implements the Servlet interface:

NovelsServlet extends HttpServlet extends GenericServlet implements Servlet

As the title makes clear, the HttpServlet is designed for servlets delivered over HTTP(S). The class offers empty strategies named after the usual HTTP request verbs (formally, strategies):

  • doPost (Post = Create)
  • doGet (Get = Read)
  • doPut (Put = Update)
  • doDelete (Delete = Delete)

Some extra HTTP verbs are lined as nicely. An extension of the HttpServlet, such because the NovelsServlet, overrides any do methodology of curiosity, leaving the others as no-ops. The NovelsServlet overrides seven of the do strategies.

Each of the HttpServlet CRUD strategies takes the identical two arguments. Here is doPost for instance:

public void doPost(HttpServletRequest request, HttpServletResponse response) {

The request argument is a map of the HTTP request data, and the response offers an output stream again to the requester. A way akin to doPost is structured as follows:

  • Read the request data, taking no matter motion is acceptable to generate a response. If data is lacking or in any other case poor, generate an error.
  • Use the extracted request data to carry out the suitable CRUD operation (on this case, create a Novel) after which encode an applicable response to the requester utilizing the response output stream to take action. In the case of doPost, the response is a affirmation new novel has been created and added to the gathering. Once the response is shipped, the output stream is closed, which closes the connection as nicely.

More on the do methodology overrides

An HTTP request has a comparatively easy construction. Here is a sketch within the acquainted HTTP 1.1 format, with feedback launched by double sharp indicators:

GET /novels              ## begin line
Host: localhost:8080     ## header factor
Accept-type: textual content/plain  ## ditto
...
[body]                   ## POST and PUT solely

The begin line begins with the HTTP verb (on this case, GET) and the URI (Uniform Resource Identifier), which is the noun (on this case, novels) that names the focused useful resource. The headers encompass key-value pairs, with a colon separating the important thing on the left from the worth(s) on the proper. The header with key Host (case insensitive) is required; the hostname localhost is the symbolic handle of the native machine on the native machine, and the port quantity 8080 is the default for the Tomcat internet server awaiting HTTP requests. (By default, Tomcat listens on port 8443 for HTTPS requests.) The header components can happen in arbitrary order. In this instance, the Accept-type header’s worth is the MIME sort textual content/plain.

Some requests (particularly, POST and PUT) have our bodies, whereas others (particularly, GET and DELETE) don’t. If there’s a physique (maybe empty), two newlines separate the headers from the physique; the HTTP physique consists of key-value pairs. For bodyless requests, header components, such because the question string, can be utilized to ship data. Here is a request to GET the /novels useful resource with the ID of two:

GET /novels?id=2

The question string begins with the query mark and, typically, consists of key-value pairs, though a key and not using a worth is feasible.

The HttpServlet, with strategies akin to getParameter and getParameterMap, properly hides the excellence between HTTP requests with and and not using a physique. In the novels instance, the getParameter methodology is used to extract the required data from the GET, POST, and DELETE requests. (Handling a PUT request requires lower-level code as a result of Tomcat doesn’t present a workable parameter map for PUT requests.) Here, for illustration, is a slice of the doPost methodology within the NovelsServlet override:

@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) {
   String creator = request.getParameter("author");
   String title = request.getParameter("title");
   ...

For a bodyless DELETE request, the method is actually the identical:

@Override
public void doDelete(HttpServletRequest request, HttpServletResponse response) {
   String param = request.getParameter("id"); // id of novel to be eliminated
   ...

The doGet methodology wants to tell apart between two flavors of a GET request: one taste means “get all, whereas the opposite means get a specified one. If the GET request URL incorporates a question string whose secret’s an ID, then the request is interpreted as “get a specified one”:

http://localhost:8080/novels?id=2  ## GET specified

If there is no such thing as a question string, the GET request is interpreted as “get all”:

http://localhost:8080/novels       ## GET all

Some devilish particulars

The novels service design displays how a Java-based internet server akin to Tomcat works. At startup, Tomcat builds a thread pool from which request handlers are drawn, an method generally known as the one thread per request mannequin. Modern variations of Tomcat additionally use non-blocking I/O to spice up efficiency.

The novels service executes as a single occasion of the NovelsServlet class, which in flip maintains a single assortment of novels. Accordingly, a race situation would come up, for instance, if these two requests have been processed concurrently:

  • One request adjustments the gathering by including a brand new novel.
  • The different request will get all of the novels within the assortment.

The consequence is indeterminate, relying on precisely how the learn and write operations overlap. To keep away from this drawback, the novels service makes use of a thread-safe ConcurrentMap. Keys for this map are generated with a thread-safe AtomicInteger. Here is the related code phase:

public class Novels {
    non-public ConcurrentMap<Integer, Novel> novels;
    non-public AtomicInteger mapKey;
    ...

By default, a response to a consumer request is encoded as XML. The novels program makes use of the old-time XMLEncoder class for simplicity; a far richer possibility is the JAX-B library. The code is easy:

public String toXml(Object obj)

The Object parameter is both a sorted ArrayList of novels (in response to a “get all” request); or a single Novel occasion (in response to a get one request); or a String (a affirmation message).

If an HTTP request header refers to JSON as a desired sort, then the XML is transformed to JSON. Here is the verify within the doGet methodology of the NovelsServlet:

String settle for = request.getHeader("accept"); // "accept" is case insensitive
if (settle for != null && settle for.incorporates("json")) json = true;

The Novels class homes the toJson methodology, which converts XML to JSON:

public String toJson(String xml)

The NovelsServlet checks for errors of assorted sorts. For instance, a POST request ought to embody an creator and a title for the brand new novel. If both is lacking, the doPost methodology throws an exception:

if (creator == null || title == null)
   throw new RuntimeException(Integer.toString(HttpServletResponse.SC_BAD_REQUEST));

The SC in SC_BAD_REQUEST stands for standing code, and the BAD_REQUEST has the usual HTTP numeric worth of 400. If the HTTP verb in a request is TRACE, a unique standing code is returned:

public void doTrace(HttpServletRequest request, HttpServletResponse response)
   throw new RuntimeException(Integer.toString(HttpServletResponse.SC_METHOD_NOT_ALLOWED));

Testing the novels service

Testing an internet service with a browser is difficult. Among the CRUD verbs, fashionable browsers generate solely POST (Create) and GET (Read) requests. Even a POST request is difficult from a browser, because the key-values for the physique should be included; that is sometimes completed via an HTML type. A command-line utility akin to curl is a greater solution to go, as this part illustrates with some curl instructions, that are included within the ZIP on my web site.

Here are some pattern checks with out the corresponding output:

% curl localhost:8080/novels/
% curl localhost:8080/novels?id=1
% curl --header "Accept: application/json" localhost:8080/novels/

The first command requests all of the novels, that are encoded by default in XML. The second command requests the novel with an ID of 1, which is encoded in XML. The final command provides an Accept header factor with utility/json because the MIME sort desired. The get one command might additionally use this header factor. Such requests have JSON relatively than the XML responses.

The subsequent two instructions create a brand new novel within the assortment and make sure the addition:

% curl --request POST --data "author=Tolstoy&title=War and Peace" localhost:8080/novels/
% curl localhost:8080/novels?id=four

A PUT command in curl resembles a POST command besides that the PUT physique doesn’t use commonplace syntax. The documentation for the doPut methodology within the NovelsServlet goes into element, however the quick model is that Tomcat doesn’t generate a correct map on PUT requests. Here is the pattern PUT command and a affirmation command:

% curl --request PUT --data "id=3#title=This is an UPDATE" localhost:8080/novels/
% curl localhost:8080/novels?id=three

The second command confirms the replace.

Finally, the DELETE command works as anticipated:

% curl --request DELETE localhost:8080/novels?id=2
% curl localhost:8080/novels/

The request is for the novel with the ID of two to be deleted. The second command reveals the remaining novels.

The internet.xml configuration file

Although it is formally optionally available, a internet.xml configuration file is a mainstay in a production-grade web site or service. The configuration file permits routing, safety, and different options of a web site or service to be specified independently of the implementation code. The configuration for the novels service handles routing by offering a URL sample for requests dispatched to this service:

<?xml model = "1.0" encoding = "UTF-8"?>
<web-app>
  <servlet>
    <servlet-name>novels</servlet-name>
    <servlet-class>novels.NovelsServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>novels</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

The servlet-name factor offers an abbreviation (novels) for the servlet’s absolutely certified class title (novels.NovelsServlet), and this title is used within the servlet-mapping factor under.

Recall URL for a deployed service has the WAR file title proper after the port quantity:

http://localhost:8080/novels/

The slash instantly after the port quantity begins the URI generally known as the path to the requested useful resource, on this case, the novels service; therefore, the time period novels happens after the primary single slash.

In the internet.xml file, the url-pattern is specified as /*, which implies any path that begins with /novels. Suppose Tomcat encounters a contrived request URL, akin to this:

http://localhost:8080/novels/foobar/

The internet.xml configuration specifies that this request, too, needs to be dispatched to the novels servlet as a result of the /* sample covers /foobar. The contrived URL thus has the identical consequence because the authentic one proven above it.

A production-grade configuration file may embody data on safety, each wire-level and users-roles. Even on this case, the configuration file could be solely two or thrice the scale of the pattern one.

Wrapping up

The HttpServlet is on the middle of Java’s internet applied sciences. A web site or internet service, such because the novels service, extends this class, overriding the do verbs of curiosity. A Restful framework akin to Jersey (JAX-RS) or Restlet does primarily the identical by offering a personalized servlet, which then acts because the HTTP(S) endpoint for requests towards an internet utility written within the framework.

A servlet-based utility has entry, after all, to any Java library required within the internet utility. If the appliance follows the separation-of-concerns precept, then the servlet code stays attractively easy: the code checks a request, issuing the suitable error if there are deficiencies; in any other case, the code calls out for no matter performance could also be required (e.g., querying a database, encoding a response in a specified format), after which sends the response to the requester. The HttpServletRequest and HttpServletResponse sorts make it straightforward to carry out the servlet-specific work of studying the request and writing the response.

Java has APIs that vary from the quite simple to the extremely difficult. If it’s essential to ship some Restful providers utilizing Java, my recommendation is to provide the low-fuss HttpServlet a strive earlier than anything.

Most Popular

To Top