Hi Nicolas,
Looks better now, but there are still cases where the doTransaction()
method will not be called, though very unlikely: when all three
tryLock() attempts fail. not very likely but theoretically possible...
regards
marcel
Nicolas Belisle wrote:
Hi,
Thanks again for your comments.
Here's the second version of my template class. It should resolves the
concurrency issues you mentionned :
package app;
import javax.jcr.Credentials;
import javax.jcr.LoginException;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
public abstract class SerializableTemplate {
private Session session;
private Node scope;
private boolean done = false;
private EventListener el;
public SerializableTemplate(Repository repository, Credentials cr,
String scopePath) throws LoginException, RepositoryException {
session = repository.login(cr);
scope = session.getRootNode().getNode(scopePath);
//scope = session.getNodeByUUID(scope.getUUID());
}
public abstract void doInTransaction(Session session) throws
RepositoryException;
public void execute() throws RepositoryException {
if (tryLock()) {
return;
}
this.el = new EventListener() {
public void onEvent(EventIterator events) {
try {
tryLock();
} catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
};
session.getWorkspace().getObservationManager().addEventListener(el,
Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
//Try again, in case the lock is removed before observer could
be put in place
tryLock();
}
private synchronized boolean tryLock() throws RepositoryException {
try {
if (done) {
return false;
}
if (!scope.isLocked()) {
scope.lock(true, true);
try {
if (el != null) {
session.getWorkspace().getObservationManager().removeEventListener(el);
}
doInTransaction(session);
} finally {
done = true;
if (session.isLive()) {
session.logout();
}
}
return true;
}
} catch (LockException e) {
e.printStackTrace();
}
return false;
}
}
Here's how to use it :
SerializableTemplate sTemplate = new SerializableTemplate(repository,
new SimpleCredentials("user", "password".toCharArray()), "node/path") {
//@Override
public void doInTransaction(Session session) throws
RepositoryException {
//Do your favorite transaction...
};
sTemplate.execute();
For the constructor you suggested, I actually came up with a similiar
design at first, but found a problem with it : since the template class
might use an EventListener the class should be responsible for closing
the session (the EventListener can wait a while...). Else, the event
could be removed by the user before being invoked. That's the reason for
my ugly constructor.
I welcome your comments again...
Regards,
Nicolas