MoneerOmar
MoneerOmar

Reputation: 225

REST service: prevent concurrent invocations per user

I'm developing a REST service (using RESTeasy) that performs heavy calculations and produces large results (that will be downloaded to a file). In order protect the system, we decided to limit the execution by the same user by preventing concurrent invocations.

I thought to put a variable on the session, like this:

//check if service is running before execution
if (Boolean.parseBoolean((String) session.getAttribute("isRunning"))) {
        throw new WebApplicationException(Response.status(
        Response.Status.FORBIDDEN).entity("Service is currently running!").build());
}

//on execution
session.setAttribute("isRunning", "true");

//after execution
finally {
    session.setAttribute("isRunning", "false");
}

but this limits usage per session, not per user.

Upvotes: 1

Views: 1062

Answers (2)

MoneerOmar
MoneerOmar

Reputation: 225

Building on the approach suggested by @dwb, here is my answer:

//globally defined set holding ids of users currently executing the service
private static Set<String> currentUsers = Collections.synchronizedSortedSet(new TreeSet<String>());


//check if service is running before execution
if (isCurrentlyRunningByUser(request.getRemoteUser())) {
        throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN).entity("Service is currently running!").build());
}


//after execution
finally {
    currentUsers.remove(user);
}



/**
 * checks if user is currently running the service. if yes, returns true.
 * if no, add user to current users set, and returns false
 * @param remoteUser user asking to execute the service
 * @return true if service is being run by the user
 */
private boolean isCurrentlyRunningByUser(String user) {
    synchronized (currentUsers) {
        Iterator it = currentUsers.iterator();
        while (it.hasNext()) {
            if (user.equals(it.next())) {
                return true;
            }
        }
        currentUsers.add(user);
    }
    return false;
}

Upvotes: 0

DwB
DwB

Reputation: 38300

You are on the right track.

Store a set in the session (I'll call this the UserSet).

When you get a request, do the following:

  1. Identify the user.
  2. Get the UserSet from the application scope.
  3. Synchronize on the UserSet.
  4. Check if the user is already in UserSet.
  5. If yes, short circuit the request and exit the web service call.
  6. If no, add the user to the UserSet.
  7. exit the synchronization block.
  8. Perform the long running operation.
  9. Synchronize on the UserSet.
  10. Remove the user from the set.

Edit: corrected scope based on John Wu comment.

Upvotes: 1

Related Questions