Jersey REST service and Thread Local usage

In this exercise, I am going to provide an example to show how we can use the ThreadLocal with the Jersey REST service.

Consider that it’s a REST application and we need the user details in the application flow many places. The User details will be retrieved with the header information availbale in the HttpServletRequest.
One solution is to pass the HttpServletRequest object whereever its required. But its a tedious job.
So an another way is to use the ContainerRequestFilter and processes the request, then extract out the header and call the back end to get the User details and put into the ThreadLocal storage so that it can be accessible anywhere in that thread flow.

Refer the below sample code to know how to do that. We don’t use the Spring Framework here.

DataHolder:
This class holds the static ThreadLocal variable and it has getter and setter for User object. We will be setting the User Object from the ContainerRequestFilter


package com.utils;

public class DataHolder{

    private static final ThreadLocal userThreadLocal = new ThreadLocal();

    public static void setUser(User user) {
        userThreadLocal.set(user);
    }

    public static User getUser() {
        return userThreadLocal.get();
    }

    public static void remove() {
        userThreadLocal.remove();
    }
}

UserDataCaptureRequestFilter
This filter will intercept the request and extract out the header and retrieve the user details and put it into the ThreadLocal.



package com.filters;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import java.io.IOException;

@Provider
public class UserDataCaptureRequestFilter implements ContainerRequestFilter {
    @Context
    private HttpServletRequest httpRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        //Get the User data from the back end system with the request header/cookie. I have not given the getUser() method but assume it will get the user details 
        User user = getUser(httpRequest);
        DataHolder.setUser(user);
    }
}


Application
The ResourceConfig class where we can specify the packages to look into for the end points and context


public class Application extends ResourceConfig {
    public Application() {
        packages(true, "com.resource");
        packages(true, "com.filters");
    }
}

 
UserResource
This service returns the User details in JSON format. Here We have not retrived the User Details once again since we already retrieved the data in the filter itself. So the data would be available in the Thread scope. So we could use it


package com.resource;

import com.utils.DataHolder;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("user")
public class UserResource{

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getUser() {
       
        return Response.ok().entity(DataHolder.getUser()).type(MediaType.APPLICATION_JSON).build();
    }
}

 

 

Test Secure REST services with Chrome Browser Plugin

Most of us want to test out the REST services via Advanced Rest Client or Postman for some reason or debug an issue.

But if the REST services are secure and protected by Ping Access or SiteMinder or any other tool, then we will get a login page. So we have to hard code the browser cookies to bypass the login page.

There is an another way to do that.

If you are using Advanced Rest Client(https://advancedrestclient.com), then you can use ARC cookie exchange plugin.
So this plugin helps ARC plugin to retrieve the browser cookies and send it in the request.

If you are using Postman(https://www.getpostman.com), then you can use Postman interceptor. So the Postman interceptor plugin helps the Postman plugin to use the browser cookies for each service call.

HTTP Caching in REST API

In this post, I am going to show an example which explains the implementation of HTTP caching in REST API.

As we know that REST API services can be cached in the browser whereas SOAP based services are not.

We have two kinds of cache control mechanism

1. Time based cache header
2. Conditional cache header

Time based cache header

Assume that we have a web service and you want to cache the response of that service in client’s browser for 5 min. So to achieve the same, we should have to set the cache control HTTP header appropriately


Cache-Control:private, max-age=300

Here “private” denotes that the response will be cached only in the client mostly by browser and not any intermediate proxy servers

max-age denotes that how long the resource is valid. The value should be given in seconds

Conditional cache header

With the conditional cache header approach, the browser will ask the server whether the response/content has been changed or not. Here the Browser sends out ETag and If-Modified-Since headers[Need to set If-Modified-Since at the client API] to the server and at the server side we should use these headers and implement our logic and send out the response only if its changed or the client side content has been expired.

You can note for the first time, we get the HTTP status 200 and for the subsequent request, we get the HTTP status 304 Not Modified

Please refer the below sample code which shows both approaches



import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;

@Path("/greeter")
public class GreetingResource {

    private static final String HELLO_MESSAGE= "Welcome %s";

    private static final String BIRTHDAY_MESSAGE= "Happy Birthday %s";

    @Context
    private Request request;

    @GET
    @Path("welcome")
    @Produces(MediaType.TEXT_HTML)
    public Response sayWelcomeMessage(@QueryParam("name") String name) {
        System.out.println("sayWelcomeMessage:::::"+ System.currentTimeMillis());
        CacheControl cacheControl = new CacheControl();
        cacheControl.setMaxAge(60);
        cacheControl.setPrivate(true);
        Response.ResponseBuilder builder = Response.ok(String.format(HELLO_MESSAGE, name));
        builder.cacheControl(cacheControl);
        return builder.build();
    }

    @GET
    @Path("bday")
    @Produces(MediaType.TEXT_HTML)
    public Response sayBdayWishesMessage(@QueryParam("name") String name) {

        System.out.println("sayGreetingMessage:::::"+ System.currentTimeMillis());
        CacheControl cacheControl = new CacheControl();
        //60 seconds
        cacheControl.setMaxAge(60);

        String message = String.format(BIRTHDAY_MESSAGE, name);
        EntityTag entityTag = new EntityTag(Integer.toString(message.hashCode()));
        //Browser sends ETag. So we can use this and
        // find out whether the response is expired or not
        Response.ResponseBuilder builder = request.evaluatePreconditions(entityTag);
        // If the build is null, then the content dont match, so the response should be sent
        if (builder == null) {
            builder = Response.ok(message);
            builder.tag(entityTag);
        } else {
            builder.cacheControl(cacheControl).tag(entityTag);
        }
        return builder.build();
    }
}

Rest API to produce message to Kafka using Docker Maven Plugin

I have developed a simple REST API to send the incoming message to Apache Kafka.

I have used Docker Kafka (https://github.com/spotify/docker-kafka) and the Docker Maven Plugin(https://github.com/fabric8io/docker-maven-plugin) to do this.

So before going through this post be familiarize yourself with Docker and Docker Compose

Docker Maven Plugin[Docker Maven Plugin] provides us a nice way to specify multiple images in POM.xml and link it as necessary. We can also use Docker compose for doing this. But I have used this plugin here.

    1. Clone the project (https://github.com/dkbalachandar/kafka-message-sender)
    2. Then go into kafka-message-sender folder
    3. Then enter ‘mvn clean install’
    4. Then enter  ‘mvn docker:start’. Then enter ‘docker ps’ and make sure that there are two containers are running. The name of those containers are kafka, kafka-rest
    5. Then access http://localhost:8080/api/kafka/send/test (POST) and confirm that you see message has been sent on the browser
    6. Then enter the below command and make sure that whatever message which you sent is available at Kafka[Kafka Command Line Consumer] or you can also consume via a Flume agent[Kafka Flume Agent Consumer]
docker exec -it kafka /opt/kafka_2.11-0.8.2.1/bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning

Transfer Encoding and Content Length

Chunked transfer encoding is a data transfer mechanism which enables the response from the server will be sent as a series of chunks to the client. The servlet container will decide whether to use Content Length or Chunked based on the response content size. If the response content size is  more than the actual buffer size, then it will use Chunked Encoding otherwise it will calculate the content length and add it in the response header. Usually if the response content is large data, then it will use Chunked transfer encoding and you would not see the content-length header

Restful web services: Handling exception with WebApplicationException

Most of us use the below approach in our Rest services for handling the error response.


@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/contacts")
public Response getContactsById(@QueryParam("userId") String userId) {
        try {
            Contact contact = new ContactService().getContactsById(userId);
         return Response.ok().entity(contact).type(MediaType.APPLICATION_JSON).build();
        } catch (Exception ex) {
            LOGGER.error("Exception happened while getting contact data", ex);
            Throwable rootCause = Throwables.getRootCause(ex);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).
                    entity(rootCause.getMessage()).type(MediaType.APPLICATION_JSON).build();
        }
    }

So in the above code, if there is any issue/exception, then we catch that exception and get the root cause of it and create a new Response object with that and send it as JSON.

We can use the WebApplicationException instead of catching that error and creating a response object.

Below is the example for handling the above with WebApplicationException.


public class ContactNotFoundException extends WebApplicationException {

    public ContactNotFoundException(String message) {
        super(Response.status(Status.NOT_FOUND).entity(message)
                .type(MediaType.APPLICATION_JSON).build());
    }
}


@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/contacts")
public Response getContactsById(@QueryParam("userId") String userId) throws ContactNotFoundException {
        try {
            Contact contact = new ContactService().getContactsById(userId);
            return Response.ok().entity(contact).type(MediaType.APPLICATION_JSON).build();
        } catch (Exception ex) {
            LOGGER.error("Exception happened while getting contact data", ex);
            throw new ContactNotFoundException(ex.getMessage());
        }
    }

So in the above example, we create a custom exception ContactNotFoundException which extends the WebApplicationException. Inside the constructor, we create a response with proper status code and error message.

So in our Resource class, we use handle the exception and instead of creating a Response object, just throw the ContactNotFoundException with the message in it. Once we do this, then Rest of things are handled by Jersey framework itself.