jackrabbit-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Nicolas Belisle <Nicolas.Beli...@bibl.ulaval.ca>
Subject Re: Problems with concurrent sessions
Date Fri, 15 Jul 2005 15:47:09 GMT
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


Le 05:07 2005-07-15, vous avez écrit:
>Hi Nicolas,
>
>I can see where you are heading for ;)
>
>I think the constructor take the node that acts as the lock scope. It 
>should provide all that is needed:
>
>public SerializableTemplate(Node scope) {
>         session = scope.getSession();
>         this.scope = scope;
>}
>
>you may then also ommit the session parameter in doInTransaction. And just 
>as a minor improvement the method should be allowed to throw a 
>RepositoryException.
>
>After checking whether a node is locked it is not guaranteed that you can 
>then lock the node.
>A similar concurrency problem can arise when isLocked() returns true, 
>between that call and the listener registration the node might get 
>unlocked. so, you don't get an event for that and keep waiting.
>
>regards
>  marcel
>
>Nicolas Belisle wrote:
>>I just thought about something like this (Note that I've only done a few 
>>tests on that class.)  :
>>public abstract class SerializableTemplate {
>>     private Session session;
>>     private Node scope;
>>     public SerializableTemplate(Repository repository, Credentials cr, 
>> String scopePath) throws LoginException, RepositoryException {
>>         session = repository.login(cr);
>>         scope = session.getRootNode().getNode(scopePath);
>>     }
>>     public abstract void doInTransaction(Session session);
>>     public void execute() {
>>         try {
>>             if (!scope.isLocked()) {
>>                 scope.lock(true, true);
>>                 doInTransaction(session);
>>                 if (session.isLive()) {
>>                     session.logout();
>>                 }
>>             } else {
>>                 EventListener el = new EventListener() {
>>                     public void onEvent(EventIterator events) {
>>                         try {
>>                             if (!scope.isLocked()) {
>>                                 scope.lock(true, true);
>>                                 doInTransaction(session);
>>                                 if (session.isLive()) {
>>                                     session.logout();
>>                                 }
>>                             }
>>                         } catch (Exception e) {
>>                             throw new RuntimeException(e);
>>                         }
>>                     }
>>                 };
>>
>>session.getWorkspace().getObservationManager().addEventListener(el, 
>>Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false);
>>             }
>>         } catch (Exception e) {
>>             throw new RuntimeException(e);
>>         }
>>     }
>>}
>>I don't like a few things about that class, especially the constructor...
>>What do you think overall ?
>>  > Again I think this always depends on the application on top of the 
>> repository.
>>  > Setting an isolation level as a global property does not seems to be 
>> a good idea to me.
>>Well, if many applications need control of their isolation level, maybe 
>>that feature should be implemented in one place or documented in a worked 
>>example...
>>Regards,
>>Nicolas


Mime
View raw message