Synchronous access to REST web service

java, jersey, jpa, persistence, rest

Solution

The life-cycle of a (root) JAX-RS resource is per request, so the (otherwise correct) `synchronized` keyword on the `nextLink` method is sadly ineffectual.

What you need is a mean to synchronize the access/update. This could be done in many ways:

I) You could synchronize on an external object, injected by a framework (example: a CDI injected @ApplicationScoped) as in:

@ApplicationScoped
public class SyncLink{
    private ReentrantLock lock = new ReentrantLock();
    public Lock getLock(){
       return lock;
    }
}
....
public class MyResource{
  @Inject SyncLink sync;

  @GET
  @Path("next/{uuid}")
  @Produces({"application/xml", "application/json"})
  public Links nextLink(@PathParam("uuid") String uuid) {
    sync.getLock().lock();
    try{
      Links link = null;
      try {
        link = super.next();
        if (link != null) {
            link.setStatusCode(5);
            link.setProcessUUID(uuid);
            getEntityManager().flush(); 
            Logger.getLogger("Glassfish Rest Service").log(Level.INFO, "Process {0} request url : {1} #id  {2} at {3} #", new Object[]{uuid, link.getLinkTxt(), link.getLinkID(), Calendar.getInstance().getTimeInMillis()});
        }
      } catch (NoResultException ex) {
      } catch (IllegalArgumentException ex) {
      }
      return link;
    }finally{
       sync.getLock().unlock();
    }
  }
}

II) You could be lazy and synchronize on the class

public class MyResource{
  @Inject SyncLink sync;

  @GET
  @Path("next/{uuid}")
  @Produces({"application/xml", "application/json"})
  public Links nextLink(@PathParam("uuid") String uuid) {
     Links link = null;
    synchronized(MyResource.class){
      try {
        link = super.next();
        if (link != null) {
            link.setStatusCode(5);
            link.setProcessUUID(uuid);
            getEntityManager().flush(); 
            Logger.getLogger("Glassfish Rest Service").log(Level.INFO, "Process {0} request url : {1} #id  {2} at {3} #", new Object[]{uuid, link.getLinkTxt(), link.getLinkID(), Calendar.getInstance().getTimeInMillis()});
        }
      } catch (NoResultException ex) {
      } catch (IllegalArgumentException ex) {
      }

    }
    return link;
  }
}

III) You could synchronize using the database. In that case you would investigate the pessimistic locking available in JPA2.

Problem

i m in trouble with a simple REST service using this code : ``` @GET @Path("next/{uuid}") @Produces({"application/xml", "application/json"}) public synchronized Links nextLink(@PathParam("uuid") String uuid) { Links link = null; try { link = super.next(); if (link != null) { link.setStatusCode(5); link.setProcessUUID(uuid); getEntityManager().flush(); Logger.getLogger("Glassfish Rest Service").log(Level.INFO, "Process {0} request url : {1} #id {2} at {3} #", new Object[]{uuid, link.getLinkTxt(), link.getLinkID(), Calendar.getInstance().getTimeInMillis()}); } } catch (NoResultException ex) { } catch (IllegalArgumentException ex) { } return link; } ``` this should provide a link object, and mark it as used (setStatusCode(5)) to prevent next access to service to send the same object. the probleme, is that when there s a lot of fast clients accessing to the web service, this one provides 2 or 3 times the same link object to different clients. how can i solve this ?? here is the resquest using to : @NamedQuery(name = "Links.getNext", query = "SELECT l FROM Links l WHERE l.statusCode = 2") and the super.next() methode : ``` public T next() { javax.persistence.Query q = getEntityManager().createNamedQuery("Links.getNext"); q.setMaxResults(1); T res = (T) q.getSingleResult(); return res; } ``` thx

Original source