Return-Path: Delivered-To: apmail-incubator-jackrabbit-dev-archive@www.apache.org Received: (qmail 90938 invoked from network); 15 Jul 2005 15:47:14 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 15 Jul 2005 15:47:14 -0000 Received: (qmail 37783 invoked by uid 500); 15 Jul 2005 15:47:13 -0000 Mailing-List: contact jackrabbit-dev-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: jackrabbit-dev@incubator.apache.org Delivered-To: mailing list jackrabbit-dev@incubator.apache.org Received: (qmail 37770 invoked by uid 99); 15 Jul 2005 15:47:13 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 15 Jul 2005 08:47:13 -0700 X-ASF-Spam-Status: No, hits=0.1 required=10.0 tests=FORGED_RCVD_HELO X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [132.203.250.27] (HELO clouso.ulaval.ca) (132.203.250.27) by apache.org (qpsmtpd/0.29) with SMTP; Fri, 15 Jul 2005 08:47:10 -0700 Received: from hermes.ulaval.ca(132.203.250.27) by clouso.ulaval.ca via csmap id b61c54d4_f547_11d9_9b61_00304811f440_2637; Fri, 15 Jul 2005 11:47:13 -0400 (EDT) Received: from clouso.ulaval.ca (poste015-130.bibl.ulaval.ca [132.203.130.15]) by hermes.ulaval.ca (8.12.9/8.12.9) with ESMTP id j6FFlASn020569 for ; Fri, 15 Jul 2005 11:47:10 -0400 Received: from poste015-130.bibl.ulaval.ca(132.203.130.15) by clouso.ulaval.ca via csmap id b597acfc_f547_11d9_8738_00304811f440_2596; Fri, 15 Jul 2005 11:47:12 -0400 (EDT) Message-Id: <6.1.1.1.2.20050715112238.01a926e8@hermes.ulaval.ca> X-Sender: biblnbe@hermes.ulaval.ca X-Mailer: QUALCOMM Windows Eudora Version 6.1.1.1 Date: Fri, 15 Jul 2005 11:47:09 -0400 To: jackrabbit-dev@incubator.apache.org From: Nicolas Belisle Subject: Re: Problems with concurrent sessions In-Reply-To: <42D77CC3.4020303@gmx.net> References: <6.1.1.1.2.20050706142809.01afdcd8@hermes.ulaval.ca> <90a8d1c00507070233521f9893@mail.gmail.com> <6.1.1.1.2.20050707112258.01a594b8@hermes.ulaval.ca> <42CE3410.2030407@gmx.net> <6.1.1.1.2.20050708111147.019f7fd8@hermes.ulaval.ca> <42CEAB1A.5040709@gmx.net> <6.1.1.1.2.20050714134841.01a036c8@hermes.ulaval.ca> <42D77CC3.4020303@gmx.net> Mime-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1"; format=flowed Content-Transfer-Encoding: quoted-printable X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Hi, Thanks again for your comments. Here's the second version of my template class. It should resolves the=20 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 =3D false; private EventListener el; public SerializableTemplate(Repository repository, Credentials cr,=20 String scopePath) throws LoginException, RepositoryException { session =3D repository.login(cr); scope =3D session.getRootNode().getNode(scopePath); //scope =3D session.getNodeByUUID(scope.getUUID()); } public abstract void doInTransaction(Session session) throws=20 RepositoryException; public void execute() throws RepositoryException { if (tryLock()) { return; } this.el =3D new EventListener() { public void onEvent(EventIterator events) { try { tryLock(); } catch (RepositoryException e) { throw new RuntimeException(e); } } }; session.getWorkspace().getObservationManager().addEventListener(el,= =20 Event.PROPERTY_REMOVED, scope.getPath(), true, null, null, false); //Try again, in case the lock is removed before observer could be= =20 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 !=3D null) { = session.getWorkspace().getObservationManager().removeEventListener(el); } doInTransaction(session); } finally { done =3D true; if (session.isLive()) { session.logout(); } } return true; } } catch (LockException e) { e.printStackTrace(); } return false; } } Here's how to use it : SerializableTemplate sTemplate =3D new SerializableTemplate(repository, new= =20 SimpleCredentials("user", "password".toCharArray()), "node/path") { //@Override public void doInTransaction(Session session) throws=20 RepositoryException { //Do your favorite transaction... }; sTemplate.execute(); For the constructor you suggested, I actually came up with a similiar=20 design at first, but found a problem with it : since the template class=20 might use an EventListener the class should be responsible for closing the= =20 session (the EventListener can wait a while...). Else, the event could be=20 removed by the user before being invoked. That's the reason for my ugly=20 constructor. I welcome your comments again... Regards, Nicolas Le 05:07 2005-07-15, vous avez =E9crit: >Hi Nicolas, > >I can see where you are heading for ;) > >I think the constructor take the node that acts as the lock scope. It=20 >should provide all that is needed: > >public SerializableTemplate(Node scope) { > session =3D scope.getSession(); > this.scope =3D scope; >} > >you may then also ommit the session parameter in doInTransaction. And just= =20 >as a minor improvement the method should be allowed to throw a=20 >RepositoryException. > >After checking whether a node is locked it is not guaranteed that you can= =20 >then lock the node. >A similar concurrency problem can arise when isLocked() returns true,=20 >between that call and the listener registration the node might get=20 >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= =20 >>tests on that class.) : >>public abstract class SerializableTemplate { >> private Session session; >> private Node scope; >> public SerializableTemplate(Repository repository, Credentials cr,=20 >> String scopePath) throws LoginException, RepositoryException { >> session =3D repository.login(cr); >> scope =3D 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 =3D 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,=20 >>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=20 >> repository. >> > Setting an isolation level as a global property does not seems to be= =20 >> a good idea to me. >>Well, if many applications need control of their isolation level, maybe=20 >>that feature should be implemented in one place or documented in a worked= =20 >>example... >>Regards, >>Nicolas