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