class ApiTask {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApiTask.class);

    private final Callable<Object> callable;

    enum ApiTaskState {
        INITIAL,
        SUBMITTED,
        COMPLETED,
    }
    private ApiTaskState state;

    private final UUID uuid;
    private final String path;

    private final Response.Status responseStatus;

    private Future<Object> future;

    private final Map<Class<?>, Response.Status> exceptionStatusMap = new HashMap<>();

    /**
     * @param path request path (for identification)
     * @param callable Callable object
     */
    public ApiTask(String path, Callable<Object> callable) {
        this(path, callable, Response.Status.OK);
    }

    /**
     * @param path request path (for identification)
     * @param callable Callable object
     * @param status request status to return after the runnable is done
     */
    public ApiTask(String path, Callable<Object> callable, Response.Status status) {
        this(path, callable, status, null);
    }

   /**
    * @param path request path (for identification)
    * @param callable Callable object
    * @param status request status to return after the runnable is done
    * @param exceptionStatusMap map of {@link Exception} to {@link Response.Status}
    */
    public ApiTask(String path, Callable<Object> callable, Response.Status status,
                   Map<Class<?>, Response.Status> exceptionStatusMap) {
        this.callable = callable;
        this.uuid = UUID.randomUUID();
        this.responseStatus = status;
        this.path = path;
        this.state = ApiTaskState.INITIAL;
        if (exceptionStatusMap != null) {
            this.exceptionStatusMap.putAll(exceptionStatusMap);
        }
    }

    /**
     * The UUID is randomly generated in the constructor.
     * @return UUID
     */
    public UUID getUuid() {
        return uuid;
    }

    /**
     * @return response status to be used when the task was successfully completed
     */
    Response.Status getResponseStatus() {
        return responseStatus;
    }

    /**
     * Set status as submitted.
     */
    void setSubmitted() {
        state = ApiTaskState.SUBMITTED;
    }

    /**
     * @return whether the API task successfully completed
     */
    public boolean isCompleted() {
        return state.equals(ApiTaskState.COMPLETED);
    }

    void setCompleted() {
        state = ApiTaskState.COMPLETED;
    }

    /**
     * @param future Future object used for tracking the progress of the API task
     */
    void setFuture(Future<Object> future) {
        this.future = future;
    }

    /**
     * @return whether the task is finished (normally or with exception)
     */
    public boolean isDone() {
        if (future != null) {
            return future.isDone();
        } else {
            return false;
        }
    }

    /**
     * Provides simple Exception to status code mapping. The Exception match is exact, i.e. exception class hierarchy
     * is not considered.
     * @param exception Exception
     * @return Response status
     */
    private Response.Status mapExceptionToStatus(ExecutionException exception) {
        return exceptionStatusMap.getOrDefault(exception.getCause().getClass(), Response.Status.INTERNAL_SERVER_ERROR);
    }

    /**
     * This method assumes that the API task is finished.
     * @return {@link Response} object corresponding to the state of the API task. If the task returns
     * non-{@code null} object, its JSON representation will be sent in the response payload.
     * @throws IllegalStateException if the API task is not finished
     */
    public Response getResponse() {<FILL_FUNCTION_BODY>}

    /**
     * @return {@link Callable} object that contains the work that needs to be completed for this API request
     */
    public Callable<Object> getCallable() {
        synchronized (this) {
            if (state.equals(ApiTaskState.SUBMITTED)) {
                throw new IllegalStateException(String.format("API task %s already submitted", this));
            }

            return () -> {
                LOGGER.log(Level.FINE, "API task {0} started", this);
                setSubmitted();
                Object ret = callable.call();
                setCompleted();
                LOGGER.log(Level.FINE, "API task {0} done", this);
                return ret;
            };
        }
    }

    @Override
    public String toString() {
        return "{uuid=" + uuid + ",path=" + path + ",state=" + state + ",responseStatus=" + responseStatus + "}";
    }
}

        // Avoid thread being blocked in future.get() below.
        if (!isDone()) {
            throw new IllegalStateException(String.format("task %s not yet done", this));
        }

        Object obj;
        try {
            obj = future.get();
        } catch (InterruptedException ex) {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
        } catch (ExecutionException ex) {
            return Response.status(mapExceptionToStatus(ex)).entity(ex.toString()).build();
        }

        if (obj != null) {
            return Response.status(getResponseStatus()).
                    type(MediaType.APPLICATION_JSON_TYPE).
                    entity(obj).
                    build();
        }

        return Response.status(getResponseStatus()).build();
    
<no_super_class>