Web Services and Java Jersey

Overview

Since 1996 I have developed many distributed systems in C, C++ and Java using middleware technologies like CORBA, Java RMI, COM and DCOM, our home grown middleware technology XORBA, and Java JMS. 

All of them excluding JMS are RPC middleware technologies.  They rely on the developer specifying the interfaces using a specialist language first.  A special tool is then used to generate language bindings from the interface.  The generated code has client and server side code that are compiled into the client and server applications respectively.

On the wire, data is transmitted in a binary format e.g. CORBA using IIOP, Java RMI using JRMP, COM and DCOM using MS Binary Protocol etc.  Using binary ensures low latency and minimal bandwidth.

Unfortunately these binary standards struggle to coexist in this web enabled world of firewalls and fixed ports.

Web Services rely on two simple facts.  1) the service port is exposed on well defined public ports (usually 80), and 2) the transmitted data is usually text based - XML or more commonly now JSON.

Visit https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for detailed description of each HTTP command.

Visit https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm for a detailed understanding of ReSTful services by Roy Thomas Fielding the creator of this architectural style.

With Jersey, the service handler is implemented as a Servlet.  You annotate your service code annotations that direct incoming requests to specific methods within that servlet.  These annotations are responsible for the marshalling and unmarshalling of data.

The annotations are defined in the package javax.ws.rs package contains JAX-RS annotations.

AnnotationDescription
PathIt identifies the URI path. It can be specified on class or method.
PathParamrepresents the parameter of the URI path.
GETspecifies method responds to GET request.
POSTspecifies method responds to POST request.
PUTspecifies method responds to PUT request.
HEADspecifies method responds to HEAD request.
DELETEspecifies method responds to DELETE request.
OPTIONSspecifies method responds to OPTIONS request.
FormParamrepresents the parameter of the form.
QueryParamrepresents the parameter of the query string of an URL.
HeaderParamrepresents the parameter of the header.
CookieParamrepresents the parameter of the cookie.
Producesdefines media type for the response such as XML, PLAIN, JSON etc. It defines the media type that the methods of a resource class or MessageBodyWriter can produce.
ConsumesIt defines the media type that the methods of a resource class or MessageBodyReader can produce.

Let's look at an example using the JAX-RS @Get, @Path, and @PathParam annotations (example is from https://www.javatpoint.com/jax-rs-annotations-example)

@Get annotation

HelloService.java
package com.javatpoint.rest;  
import javax.ws.rs.GET;  
import javax.ws.rs.Path;  
import javax.ws.rs.PathParam;  
import javax.ws.rs.core.Response;  

@Path("/hello")  
public class HelloService
{  
    @GET  
    public Response getMsg() 
    {  
        String output = "Jersey says hello world;  
        return Response.status(200).entity(output).build();  
    }  
}  

To fully understand how these annotations work, you must also look at the web.xml file for the server

web.xml
<?xml version="1.0" encoding="UTF-8"?>  
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
xmlns="http://java.sun.com/xml/ns/javaee"   
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"   
id="WebApp_ID" version="3.0">  
 <servlet>  
    <servlet-name>Jersey REST Service</servlet-name>  
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>  
    <init-param>  
        <param-name>jersey.config.server.provider.packages</param-name>  
        <param-value>com.javatpoint.rest</param-value>  
    </init-param>  
    <load-on-startup>1</load-on-startup>  
  </servlet>  
  <servlet-mapping>  
    <servlet-name>Jersey REST Service</servlet-name>  
    <url-pattern>/rest/*</url-pattern>  
  </servlet-mapping>  
</web-app>   

Let's start by examing the web.xml file.

It specifies that there is a servlet called org.glassfish.jersey.servlet.ServletContainer (line 9) that will handle requests on the url <host>/rest/ (line 18) and route those requests to any one of the classes within the package com.javatpoint.rest (line 12).

Now let's go back to the java file.  Notice the package this class belongs to "com.javatpoint.rest", this correlates to line 12 in the web.xml file.  At line 7 we see @Path ("/hello").  This correlates to the url-pattern at line 18.  So the methods on this class will called if the url-pattern matches <host>/rest/hello.

ReST is an architecural style that is dependant on the open HTTP protocol.  Web Services which are direct implementation of that architectural style will only use HTTP.  Hence the @Get, @Post, and @Put annotations. Marking a method with any of these HTTP commands indicates to the servlet engine to only route messages to that method if it can handle that command.  So in the above example the getMsg() method will be invoked on a GET command for messages to <host>/rest/hello.  

@Path annotation

HelloService.java
package com.javatpoint.rest;  
import javax.ws.rs.GET;  
import javax.ws.rs.Path;  
import javax.ws.rs.PathParam;  
import javax.ws.rs.core.Response;  

@Path("/hello")  
public class HelloService
{  
    @GET  
    @Path("/help")  
    public Response getHelp() 
    {  
        String output = "Jersey is getting you some help ";  
        return Response.status(200).entity(output).build();  
    }  
}  

When the @Path annotation is use to route messages to specific methods depending on the sub-url that is typed.  In the example above messages to <host>/hello/help will cause the servlet engine to invokce the method getHelp().  This is a useful feature for routing based on variations in the sub-urls.

@Path and @PathParam annotations

HelloService.java
package com.javatpoint.rest;  
import javax.ws.rs.GET;  
import javax.ws.rs.Path;  
import javax.ws.rs.PathParam;  
import javax.ws.rs.core.Response;  

@Path("/hello")  
public class HelloService
{  
    @GET  
    @Path("/{param}")  
    public Response getParameterisedMsg(@PathParam("param") String msg) 
    {  
        String output = "Jersey say : " + msg;  
        return Response.status(200).entity(output).build();  
    }  
}  

The Web Services standard also allows parameters to be passed as part of the url.  It is done by appending the parameters to the base url as though they are a sub-url.  In the above example if you enter the base url <host>/hello/help me, the servlet engine will route the message to the method getParameterisedMsg().  The @Path on the method contains /{param} which is a indicator to the engine to interpret the value after the base url <host>/hello as a parameter.  The paramater can then be injected into the method using the @PathParam annotation, the value in the quotes must match the value in the @Path annotation on the method and between the braces.


Working with multiple @PathParam annotations

HelloService.java
package com.javatpoint.rest;  
import javax.ws.rs.GET;  
import javax.ws.rs.Path;  
import javax.ws.rs.PathParam;  
import javax.ws.rs.core.Response;  

@Path("/hello")  
public class HelloService
{  
    @GET  
    @Path("{year}/{month}/{day}")  
    public Response respondWithDOB(  
            @PathParam("year") int year,  
            @PathParam("month") int month,   
            @PathParam("day") int day) {  
   
       String date = year + "/" + month + "/" + day;  
   
       return Response.status(200)  
        .entity("getDate is called, year/month/day : " + date)  
        .build();  
    }    
}  

In this example, notice that the @Path annotation on the method has multiple braced values, each of these is an expected paramter, the delimineter being the "/" character.