loveMeansNothing
loveMeansNothing

Reputation: 361

Async in Java Jersey

I have some code that:

1- Received some data through a REST call (POST);

2- Performed some logic according to that data;

3- Returned the result.

For the sake of this question let's pretend it was a simple calculator webapi that allowed its clients to perform additions and subtractions. It looked like this:

@Path("/calculator")
public class Calculator {

    @POST
    @Path("addition")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response add(String request){
        //Getting A and B from request
        ...
        //Calculating result
        int res = a + b;
        //Creating response payload
        JSONObject res = new JSONObject.put("result",res);

        return Response.status(Response.Status.OK).entity(res.toString()).build();
    }

    @POST
    @Path("subtract")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response sub(String request){
        //Getting A and B from request
        ...
        //Calculating result
        int res = a - b;
        //Creating response payload
        JSONObject res = new JSONObject.put("result",res);

        return Response.status(Response.Status.OK).entity(res.toString()).build();
    }
}

Everything was fine until i realized that i couldn't perform more that one calculation in parallel because all the requests accessed a unique resource that can only be used by one of them at a time.

So, for the sake of this example, let's pretend we have a single calculator and that all requests' computations must be performed by that same calculator processor.

In my mind i think i would need something like a "CalculatorProcessor" that received requests from all the calculator webapi clients that:

1- Receives request;

2- Queues request;

3- Dequeues request;

4- Performs calculation;

5- Returns result using callback.

This is something that is kinda trivial for me in native Java, but i don't have a single clue on how i should this in a Java Jersey's context. For instance... How can i get back to the Calculator.add() and Calculator.sub() methods so i can send the http request response? Can someone please enlighten me please?

Here's my java implementation for such a component:

import java.util.concurrent.ConcurrentLinkedQueue;

//IMPLEMENTS SINGLETON PATTERN
public class Calculator {

private static Calculator instance = null;
private ConcurrentLinkedQueue<Request> queue = null;
private Runnable processor = null;

//PRIVATE CONSTRUCTOR
private Calculator() {
    queue = new ConcurrentLinkedQueue<>();
}

//GET CALCULATOR INSTANCE
static public Calculator getInstance() {
    if (instance == null) {
        instance = new Calculator();
    }
    return instance;
}

//REQUEST COMPUTATION
public synchronized void requestComputation(CalculatorCallback c, SupportedOperations o, int a, int b) {
    //Adds request to queue
    queue.add(new Request(c, o, a, b));

    //Checks if there's an active processor
    if (processor == null) {
        //Launches a new processor if there isn't
        Runnable p = new CalculatorProcessor(queue);
        new Thread(p).start();
    }
}

//CALLBACK INTERFACE
public interface CalculatorCallback {
    void computationReady(int result);
}


//SUPPORTED OPERATIONS ENUMERATION
protected enum SupportedOperations {
    ADDITION, SUBTRACTION;
}

//CLASS THAT REPRESENTS A REQUEST
private class Request {

    final SupportedOperations operation;
    final CalculatorCallback callback;
    final int a;
    final int b;

    public Request(CalculatorCallback c, SupportedOperations operation, int a, int b) {
        this.callback = c;
        this.operation = operation;
        this.a = a;
        this.b = b;
    }
}

//CALCULATOR PROCESSOR THREAD
class CalculatorProcessor implements Runnable {

    final private ConcurrentLinkedQueue<Calculator.Request> queue;

    public CalculatorProcessor(ConcurrentLinkedQueue<Calculator.Request> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        Calculator.Request current;
        int result;
        while (!queue.isEmpty()) {
            //Gets head
            current = queue.poll();

            if (current.operation == Calculator.SupportedOperations.ADDITION) {
                result = current.a + current.b;
            } else if (current.operation == Calculator.SupportedOperations.SUBTRACTION) {
                result = current.a - current.b;
            } else {
                throw new UnsupportedOperationException();
            }
            //Calls back the requester
            current.callback.computationReady(result);
        }
    }
}
}

Here's the CalculatorClient code:

public class CalculatorClient implements Calculator.CalculatorCallback {

public static void main(String[] args) {

    CalculatorClient client = new CalculatorClient();
    Random random = new Random();
    int a, b;

    for (int i = 0; i < 1000; i++) {
        a = random.nextInt(Integer.MAX_VALUE/2);
        b = random.nextInt(Integer.MAX_VALUE/2);
        System.out.println("Requesting "+a+" + "+b+"...");
        Calculator.getInstance().requestComputation(client, Calculator.SupportedOperations.ADDITION,a,b);
    }
}

@Override
public void computationReady(int result) {
    System.out.println("Result is: "+result);
}

}

Upvotes: 1

Views: 2554

Answers (1)

Paul Samsotha
Paul Samsotha

Reputation: 208984

If you are using Jersey 2, you can use its Asynchronous processing feature. You can just pass the AsyncResponse to the calculating task, and the task will just resume the response when it is finished with the processing.

@POST
public void add(@Suspended AysncResponse response, String body) {
    Calculator.getInstance().requestComputation(
            client, 
            Calculator.SupportedOperations.ADDITION,
            a,b,
            response);

    // you don't need to return anything from the resource method
    // calling `response.resume(someResponse)` (from inside the task)
    // is enough. That is why this method just returns `void`
}

The good thing about using the async feature is that if the processing takes a long time, you wouldn't be blocking the server threads as you would be if you were to try using some block mechanism like a CountDownLatch or blocking queue, or something to that effect. The server threads are immediately returned to the server so that it can handle more requests.

Upvotes: 2

Related Questions