Search code examples
javamultithreadingserverjerseyjersey-2.0

How can the server error response be sent when the Jersey ThreadPoolExecutorProvider rejects the request?


So at server end, we have extended the org.glassfish.jersey.spi.ThreadPoolExecutorProvider for having a bounded BlockingQueue of Runnable tasks, overriding the corePoolSize and MaximumPoolSize.

When the capacity of the BlockingQueue exceeds(when there are request which can't be queued anymore), it starts rejecting the tasks(which is as good as rejecting the requests).

But we observed that there's no response getting sent to the client when the task at the server end gets rejected for above case. Initially we didn't even know if the request are getting rejected or not, but later read about java.util.concurrent.RejectedExecutionHandler and we tried implementing a customHandler and passing it while creating an Executor (please refer the code snippet below).

I can now see that the task is rejected but not sure how to deal with sending the response to client saying that the request was rejected. If I don't then the client keeps waiting until maybe it timeouts.

do I need to configure anything at Jersey to send the proper response to client.

    @Override
    protected ThreadPoolExecutor createExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler){
        System.err.println("MANDAR_CREATED_EXECUTOR");
        return super.createExecutor(corePoolSize, maximumPoolSize, keepAliveTime, workQueue, threadFactory, new CustomHandler());
    }

    
    class CustomHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            System.err.println("MANDAR_REJECTED");
            executor.remove(r);
            executor.purge();
        }
    }

PS: the style we're using for implementing the Jersey layer is this


Solution

  • The solution was kinda throwing the RejectedExecutionException. This is done by new ThreadPoolExecutor.AbortPolicy().The thing weird was that AbortPolicy being default policy, still was not triggered earlier before overriding this createExecutor method. Seems Jersey's implementation of RejectionExecutionHandler for Executor is a bit different and not using the AbortPolicy.

    @Override
    protected ThreadPoolExecutor createExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler){
        return super.createExecutor(corePoolSize, maximumPoolSize, keepAliveTime, workQueue, threadFactory, new ThreadPoolExecutor.AbortPolicy());
    }
    

    EDIT: Actually the better is overrding the getRejectedExecutionHandler method which returns the AbortyPolicy based handler.

    /**
    * This method returns the handler that handles the rejected runnable tasks.
    * We have initialized the handler to {@link AbortPolicy} that helps throwing a {@link RejectedExecutionException} which in turn returns 500 statusCode response
    */
    @Override
    protected RejectedExecutionHandler getRejectedExecutionHandler() {
        return this.handler;
    }