### Eclipse Workspace Patch 1.0 #P jackrabbit-core Index: src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java (revision 816206) +++ src/test/java/org/apache/jackrabbit/core/UserTransactionImpl.java (working copy) @@ -16,6 +16,10 @@ */ package org.apache.jackrabbit.core; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import javax.transaction.xa.XAException; @@ -41,16 +45,11 @@ private static byte counter = 0; /** - * XAResource + * The XAResources map */ - private final XAResource xares; + private Map xaResources = new HashMap(); /** - * Xid - */ - private Xid xid; - - /** * Status */ private int status = Status.STATUS_NO_TRANSACTION; @@ -75,7 +74,8 @@ */ public UserTransactionImpl(Session session, boolean distributedThreadAccess) { if (session instanceof XASession) { - xares = ((XASession) session).getXAResource(); + counter++; + xaResources.put(((XASession) session).getXAResource(), new XidImpl(counter)); this.distributedThreadAccess = distributedThreadAccess; } else { throw new IllegalArgumentException("Session not of type XASession"); @@ -83,6 +83,14 @@ } /** + * Enlists the given Session to this UserTransaction + * @param session + */ + public void enlistXAResource(Session session) { + xaResources.put(session, new XidImpl(counter)); + } + + /** * @see javax.transaction.UserTransaction#begin */ public void begin() throws NotSupportedException, SystemException { @@ -91,8 +99,11 @@ } try { - xid = new XidImpl(counter++); - xares.start(xid, XAResource.TMNOFLAGS); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + XAResource resource = (XAResource) it.next(); + XidImpl xid = (XidImpl) xaResources.get(resource); + resource.start(xid, XAResource.TMNOFLAGS); + } status = Status.STATUS_ACTIVE; } catch (XAException e) { @@ -114,10 +125,18 @@ } try { - xares.end(xid, XAResource.TMSUCCESS); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + XAResource resource = (XAResource) it.next(); + XidImpl xid = (XidImpl) xaResources.get(resource); + resource.end(xid, XAResource.TMSUCCESS); + } status = Status.STATUS_PREPARING; - xares.prepare(xid); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + XAResource resource = (XAResource) it.next(); + XidImpl xid = (XidImpl) xaResources.get(resource); + resource.prepare(xid); + } status = Status.STATUS_PREPARED; status = Status.STATUS_COMMITTING; @@ -125,7 +144,11 @@ Thread distributedThread = new Thread() { public void run() { try { - xares.commit(xid, false); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + XAResource resource = (XAResource) it.next(); + XidImpl xid = (XidImpl) xaResources.get(resource); + resource.commit(xid, false); + } } catch (Exception e) { throw new RuntimeException(e.getMessage()); } @@ -138,7 +161,11 @@ "Commit from different thread but same XID must not block"); } } else { - xares.commit(xid, false); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + XAResource resource = (XAResource) it.next(); + XidImpl xid = (XidImpl) xaResources.get(resource); + resource.commit(xid, false); + } } status = Status.STATUS_COMMITTED; @@ -182,10 +209,18 @@ } try { - xares.end(xid, XAResource.TMFAIL); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + XAResource resource = (XAResource) it.next(); + XidImpl xid = (XidImpl) xaResources.get(resource); + resource.end(xid, XAResource.TMFAIL); + } status = Status.STATUS_ROLLING_BACK; - xares.rollback(xid); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + XAResource resource = (XAResource) it.next(); + XidImpl xid = (XidImpl) xaResources.get(resource); + resource.rollback(xid); + } status = Status.STATUS_ROLLEDBACK; } catch (XAException e) { @@ -211,7 +246,9 @@ */ public void setTransactionTimeout(int seconds) throws SystemException { try { - xares.setTransactionTimeout(seconds); + for (Iterator it = xaResources.keySet().iterator(); it.hasNext(); ) { + ((XAResource) it.next()).setTransactionTimeout(seconds); + } } catch (XAException e) { SystemException se = new SystemException( "Unable to set the TransactionTiomeout: XA_ERR=" + e.errorCode); Index: src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java =================================================================== --- src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java (revision 815651) +++ src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java (working copy) @@ -16,10 +16,14 @@ */ package org.apache.jackrabbit.core.state; +import java.util.Arrays; + import javax.transaction.xa.Xid; import org.apache.jackrabbit.core.id.ItemId; import org.apache.jackrabbit.core.TransactionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock; import EDU.oswego.cs.dl.util.concurrent.Sync; @@ -32,6 +36,11 @@ public class DefaultISMLocking implements ISMLocking { /** + * Logger instance + */ + private static final Logger log = LoggerFactory.getLogger(DefaultISMLocking.class); + + /** * The internal read-write lock. */ private final RWLock rwLock = new RWLock(); @@ -60,7 +69,6 @@ * {@inheritDoc} */ public void release() { - rwLock.setActiveXid(null); rwLock.writeLock().release(); } @@ -109,8 +117,36 @@ * @param xid */ synchronized void setActiveXid(Xid xid) { + if (activeXid != null && xid != null) { + boolean sameGTI = Arrays.equals(activeXid.getGlobalTransactionId(), xid.getGlobalTransactionId()); + if (!sameGTI) { + log.warn("Unable to set the ActiveXid while a other one is associated with a different GloalTransactionId with this RWLock."); + return; + } + } activeXid = xid; } - + + /** + * {@inheritDoc} + * + * If there are no more writeHolds the activeXid will be set to null + */ + protected synchronized Signaller endWrite() { + --writeHolds_; + if (writeHolds_ > 0) { // still being held + return null; + } else { + activeXid = null; + activeWriter_ = null; + if (waitingReaders_ > 0 && allowReader()) { + return readerLock_; + } else if (waitingWriters_ > 0) { + return writerLock_; + } else { + return null; + } + } + } } } Index: src/test/java/org/apache/jackrabbit/core/XATest.java =================================================================== --- src/test/java/org/apache/jackrabbit/core/XATest.java (revision 815651) +++ src/test/java/org/apache/jackrabbit/core/XATest.java (working copy) @@ -1643,7 +1643,100 @@ fail("Committed node not visible in this session"); } } + + /** + * Tests two different Sessions in one Transaction + * (see JCR-769) + */ + public void testTwoSessionsInOneTransaction() throws Exception { + Session otherSuperuser = getHelper().getSuperuserSession(); + + // get user transaction object + UserTransactionImpl utx = new UserTransactionImpl(superuser, true); + utx.enlistXAResource(otherSuperuser); + + // start transaction + utx.begin(); + Node rootNode = superuser.getRootNode(); + // add node and save + Node n = rootNode.addNode(nodeName1, testNodeType); + n.addMixin(mixReferenceable); + rootNode.save(); + + // assertion: node exists in this session + try { + superuser.getNodeByUUID(n.getUUID()); + } catch (ItemNotFoundException e) { + fail("New node not visible after save()"); + } + + // assertion: node does exist in other session + try { + otherSuperuser.getNodeByUUID(n.getUUID()); + fail("Uncommitted node visible for other session"); + } catch (ItemNotFoundException e) { + /* expected */ + } + + // add node with other session and save + rootNode = otherSuperuser.getRootNode(); + Node n1 = rootNode.addNode(nodeName2, testNodeType); + n1.addMixin(mixReferenceable); + rootNode.save(); + + // assertion: node exists in this session + try { + otherSuperuser.getNodeByUUID(n1.getUUID()); + } catch (ItemNotFoundException e) { + fail("New node not visible after save()"); + } + + // assertion: node does exist in other session + try { + superuser.getNodeByUUID(n1.getUUID()); + fail("Uncommitted node visible for other session"); + } catch (ItemNotFoundException e) { + /* expected */ + } + + + // commit + utx.commit(); + + // assertion: node exists in this session + try { + superuser.getNodeByUUID(n.getUUID()); + } catch (ItemNotFoundException e) { + fail("Committed node not visible in this session"); + } + + // assertion: node also exists in other session + try { + otherSuperuser.getNodeByUUID(n.getUUID()); + } catch (ItemNotFoundException e) { + fail("Committed node not visible in the other session"); + } + + // assertion: node1 exists in this session + try { + superuser.getNodeByUUID(n1.getUUID()); + } catch (ItemNotFoundException e) { + fail("Committed node not visible in this session"); + } + + // assertion: node1 also exists in other session + try { + otherSuperuser.getNodeByUUID(n1.getUUID()); + } catch (ItemNotFoundException e) { + fail("Committed node not visible in this session"); + } + + // logout + superuser.logout(); + otherSuperuser.logout(); + } + /** * Test setting the same property multiple times. Exposes an issue where * the same property instance got reused in subsequent transactions