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