Hi Marcel,

If all three tryLock() fail, the event is certainly active. The event is then waiting for an ACTIVE lock to be released (the last tryLock() confirms that fact). The event is not cancelled/removed until the lock is obtained. Then doInTransaction() is called.

The only way I see to stop the transaction from executing is to stop the server...

Am I missing something ?

Regards,

Nicolas


Le 05:54 2005-07-19, vous avez écrit:
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

Reply via email to