Index: docs/examples/gettingstarted/sequencers/src/test/java/org/modeshape/example/sequencer/SequencingClientTest.java =================================================================== --- docs/examples/gettingstarted/sequencers/src/test/java/org/modeshape/example/sequencer/SequencingClientTest.java (revision 1855) +++ docs/examples/gettingstarted/sequencers/src/test/java/org/modeshape/example/sequencer/SequencingClientTest.java (working copy) @@ -31,6 +31,9 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.modeshape.common.util.FileUtil; import org.modeshape.graph.connector.inmemory.InMemoryRepositorySource; import org.modeshape.jcr.JcrConfiguration; @@ -40,9 +43,6 @@ import org.modeshape.sequencer.classfile.ClassFileSequencerLexicon; import org.modeshape.sequencer.java.JavaMetadataSequencer; import org.modeshape.sequencer.mp3.Mp3MetadataSequencer; import org.modeshape.sequencer.zip.ZipSequencer; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; /** * @author Randall Hauch Index: extensions/modeshape-connector-infinispan/src/test/java/org/modeshape/connector/infinispan/InfinispanClusterTest.java =================================================================== --- extensions/modeshape-connector-infinispan/src/test/java/org/modeshape/connector/infinispan/InfinispanClusterTest.java (revision 1855) +++ extensions/modeshape-connector-infinispan/src/test/java/org/modeshape/connector/infinispan/InfinispanClusterTest.java (working copy) @@ -5,7 +5,6 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import java.util.UUID; import org.infinispan.Cache; -import org.junit.Ignore; import org.junit.Test; import org.modeshape.graph.ExecutionContext; import org.modeshape.graph.Graph; @@ -21,10 +20,7 @@ import org.modeshape.graph.property.PathNotFoundException; /* * Quick test that two clustered InfinispanSources can share data. - * - * This test is currently ignored. See MODE-764 for details. */ -@Ignore public class InfinispanClusterTest { private static final String CONFIG_FILE = "./src/test/resources/infinispan_clustered_config.xml"; Index: extensions/modeshape-sequencer-images/src/main/java/org/modeshape/sequencer/image/ImageMetadataSequencer.java =================================================================== --- extensions/modeshape-sequencer-images/src/main/java/org/modeshape/sequencer/image/ImageMetadataSequencer.java (revision 1855) +++ extensions/modeshape-sequencer-images/src/main/java/org/modeshape/sequencer/image/ImageMetadataSequencer.java (working copy) @@ -65,23 +65,6 @@ import org.modeshape.graph.sequencer.StreamSequencerContext; *

*/ public class ImageMetadataSequencer implements StreamSequencer { - // - // public static final String IMAGE_PRIMARY_TYPE = "jcr:primaryType"; - // public static final String IMAGE_MIXINS = "jcr:mixinTypes"; - // public static final String IMAGE_MIME_TYPE = "jcr:mimeType"; - // public static final String IMAGE_ENCODING = "jcr:encoding"; - // - // public static final String METADATA_NODE = "image:metadata"; - // public static final String IMAGE_FORMAT_NAME = "image:formatName"; - // public static final String IMAGE_WIDTH = "image:width"; - // public static final String IMAGE_HEIGHT = "image:height"; - // public static final String IMAGE_BITS_PER_PIXEL = "image:bitsPerPixel"; - // public static final String IMAGE_PROGRESSIVE = "image:progressive"; - // public static final String IMAGE_NUMBER_OF_IMAGES = "image:numberOfImages"; - // public static final String IMAGE_PHYSICAL_WIDTH_DPI = "image:physicalWidthDpi"; - // public static final String IMAGE_PHYSICAL_HEIGHT_DPI = "image:physicalHeightDpi"; - // public static final String IMAGE_PHYSICAL_WIDTH_INCHES = "image:physicalWidthInches"; - // public static final String IMAGE_PHYSICAL_HEIGHT_INCHES = "image:physicalHeightInches"; /** * {@inheritDoc} @@ -105,7 +88,7 @@ public class ImageMetadataSequencer implements StreamSequencer { // Generate the output graph if we found useful metadata ... if (metadata != null) { PathFactory pathFactory = context.getValueFactories().getPathFactory(); - Path metadataNode = pathFactory.create(ImageMetadataLexicon.METADATA_NODE); + Path metadataNode = pathFactory.createRelativePath(ImageMetadataLexicon.METADATA_NODE); // Place the image metadata into the output map ... output.setProperty(metadataNode, JcrLexicon.PRIMARY_TYPE, "image:metadata"); Index: modeshape-jcr-api/src/main/java/org/modeshape/jcr/api/Lock.java deleted file mode 100644 =================================================================== --- modeshape-jcr-api/src/main/java/org/modeshape/jcr/api/Lock.java (revision 1855) +++ /dev/null (working copy) @@ -1,54 +0,0 @@ -/* - * ModeShape (http://www.modeshape.org) - * See the COPYRIGHT.txt file distributed with this work for information - * regarding copyright ownership. Some portions may be licensed - * to Red Hat, Inc. under one or more contributor license agreements. - * See the AUTHORS.txt file in the distribution for a full listing of - * individual contributors. - * - * ModeShape is free software. Unless otherwise indicated, all code in ModeShape - * is licensed to you under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * ModeShape is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.modeshape.jcr.api; - -import javax.jcr.RepositoryException; - -/** - * Placeholder for JCR 2.0 Lock interface - * - */ -public interface Lock extends javax.jcr.lock.Lock { - - /** - * Returns the number of seconds remaining until this locks times out. If the lock has already timed out, a negative value is - * returned. If the number of seconds remaining is infinite or unknown, Long.MAX_VALUE is returned. - * - * @return the number of seconds remaining until this lock times out. - * @throws RepositoryException if an error occurs. - * @since JCR 2.0 - */ - public long getSecondsRemaining() throws RepositoryException; - - /** - * Returns true if the current session is the owner of this lock, either because it is session-scoped and bound - * to this session or open-scoped and this session currently holds the token for this lock. Returns false - * otherwise. - * - * @return a boolean. - * @since JCR 2.0 - */ - public boolean isLockOwningSession(); - -} Index: modeshape-jcr-api/src/main/java/org/modeshape/jcr/api/LockManager.java deleted file mode 100644 =================================================================== --- modeshape-jcr-api/src/main/java/org/modeshape/jcr/api/LockManager.java (revision 1855) +++ /dev/null (working copy) @@ -1,184 +0,0 @@ -/* - * ModeShape (http://www.modeshape.org) - * See the COPYRIGHT.txt file distributed with this work for information - * regarding copyright ownership. Some portions may be licensed - * to Red Hat, Inc. under one or more contributor license agreements. - * See the AUTHORS.txt file in the distribution for a full listing of - * individual contributors. - * - * ModeShape is free software. Unless otherwise indicated, all code in ModeShape - * is licensed to you under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * ModeShape is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.modeshape.jcr.api; - -import javax.jcr.AccessDeniedException; -import javax.jcr.InvalidItemStateException; -import javax.jcr.PathNotFoundException; -import javax.jcr.RepositoryException; -import javax.jcr.lock.Lock; -import javax.jcr.lock.LockException; - -/** - * Placeholder for the JCR 2.0 LockManager interface - */ -public interface LockManager { - - /** - * Adds the specified lock token to the current Session. Holding a lock token makes the current - * Session the owner of the lock specified by that particular lock token. - * - * @param lockToken a lock token (a string). - * @throws LockException if the specified lock token is already held by another Session and the implementation - * does not support simultaneous ownership of open-scoped locks. - * @throws RepositoryException if another error occurs. - */ - public void addLockToken( String lockToken ) throws LockException, RepositoryException; - - /** - * Returns the Lock object that applies to the node at the specified absPath. This may be either a - * lock on that node itself or a deep lock on a node above that node. - *

- * - * @param absPath absolute path of node for which to obtain the lock - * @return The applicable Lock object. - * @throws LockException if no lock applies to this node. - * @throws AccessDeniedException if the current session does not have sufficent access to get the lock. - * @throws PathNotFoundException if no node is found at absPath - * @throws RepositoryException if another error occurs. - */ - public Lock getLock( String absPath ) throws PathNotFoundException, LockException, AccessDeniedException, RepositoryException; - - /** - * Returns an array containing all lock tokens currently held by the current Session. Note that any such tokens - * will represent open-scoped locks, since session-scoped locks do not have tokens. - * - * @return an array of lock tokens (strings) - * @throws RepositoryException if an error occurs. - */ - public String[] getLockTokens() throws RepositoryException; - - /** - * Returns true if the node at absPath holds a lock; otherwise returns false. To - * hold a lock means that this node has actually had a lock placed on it specifically, as opposed to just having a lock - * apply to it due to a deep lock held by a node above. - * - * @param absPath absolute path of node - * @return a boolean. - * @throws PathNotFoundException if no node is found at absPath - * @throws RepositoryException if an error occurs. - */ - public boolean holdsLock( String absPath ) throws PathNotFoundException, RepositoryException; - - /** - *

- * Places a lock on the node at absPath. If successful, the node is said to hold the lock. - *

- * If isDeep is true then the lock applies to the specified node and all its descendant nodes; if - * false, the lock applies only to the specified node. On a successful lock, the jcr:lockIsDeep - * property of the locked node is set to this value. - *

- * If isSessionScoped is true then this lock will expire upon the expiration of the current session - * (either through an automatic or explicit Session.logout); if false, this lock does not expire until it is - * explicitly unlocked, it times out, or it is automatically unlocked due to a implementation-specific limitation. - *

- * The timeout parameter specifies the number of seconds until the lock times out (if it is not refreshed with - * Lock.refresh in the meantime). An implementation may use this information as a hint or ignore it altogether. - * Clients can discover the actual timeout by inspecting the returned Lock object. - *

- * The ownerInfo parameter can be used to pass a string holding owner information relevant to the client. An - * implementation may either use or ignore this parameter. If it uses the parameter it must set the jcr:lockOwner - * property of the locked node to this value and return this value on Lock.getLockOwner. If it ignores this - * parameter the jcr:lockOwner property (and the value returned by Lock.getLockOwner) is set to - * either the value returned by Session.getUserID of the owning session or an implementation-specific string - * identifying the owner. - *

- * The method returns a Lock object representing the new lock. If the lock is open-scoped the returned lock will - * include a lock token. The lock token is also automatically added to the set of lock tokens held by the current session. - *

- * The addition or change of the properties jcr:lockIsDeep and jcr:lockOwner are persisted - * immediately; there is no need to call save. - *

- * It is possible to lock a node even if it is checked-in. - * - * @param absPath absolute path of node to be locked - * @param isDeep if true this lock will apply to this node and all its descendants; if false, it - * applies only to this node. - * @param isSessionScoped if true, this lock expires with the current session; if false it expires - * when explicitly or automatically unlocked for some other reason. - * @param timeoutHint desired lock timeout in seconds (servers are free to ignore this value); specify {@link Long#MAX_VALUE} - * for no timeout. - * @param ownerInfo a string containing owner information supplied by the client; servers are free to ignore this value. - * @return A Lock object containing a lock token. - * @throws LockException if this node is not mix:lockable or this node is already locked or isDeep - * is true and a descendant node of this node already holds a lock. - * @throws AccessDeniedException if this session does not have sufficent access to lock this node. - * @throws InvalidItemStateException if this node has pending unsaved changes. - * @throws PathNotFoundException if no node is found at absPath - * @throws RepositoryException if another error occurs. - */ - public Lock lock( String absPath, - boolean isDeep, - boolean isSessionScoped, - long timeoutHint, - String ownerInfo ) - throws LockException, PathNotFoundException, AccessDeniedException, InvalidItemStateException, RepositoryException; - - /** - * Returns true if the node at absPath is locked either as a result of a lock held by that node or - * by a deep lock on a node above that node; otherwise returns false. - * - * @param absPath absolute path of node - * @return a boolean. - * @throws PathNotFoundException if no node is found at absPath - * @throws RepositoryException if an error occurs. - */ - public boolean isLocked( String absPath ) throws PathNotFoundException, RepositoryException; - - /** - * Removes the specified lock token from this Session. - * - * @param lockToken a lock token (a string) - * @throws LockException if the current Session does not hold the specified lock token. - * @throws RepositoryException if another error occurs. - */ - public void removeLockToken( String lockToken ) throws LockException, RepositoryException; - - /** - * Removes the lock on the node at absPath. Also removes the properties jcr:lockOwner and - * jcr:lockIsDeep from that node. As well, the corresponding lock token is removed from the set of lock tokens - * held by the current Session. - *

- * If the node does not currently hold a lock or holds a lock for which this Session is not the owner and is not - * a "lock-superuser", then a LockException is thrown. Note that the system may give permission to a non-owning - * session to unlock a lock. Typically, such "lock-superuser" capability is intended to facilitate administrational clean-up - * of orphaned open-scoped locks. - *

- * Note that it is possible to unlock a node even if it is checked-in (the lock-related properties will be changed despite the - * checked-in status). - *

- * If the current session does not have sufficient privileges to remove the lock, an AccessDeniedException is - * thrown. - * - * @param absPath absolute path of node to be unlocked - * @throws LockException if this node does not currently hold a lock or holds a lock for which this Session does not have the - * correct lock token. - * @throws AccessDeniedException if the current session does not have permission to unlock this node. - * @throws InvalidItemStateException if this node has pending unsaved changes. - * @throws PathNotFoundException if no node is found at absPath - * @throws RepositoryException if another error occurs. - */ - public void unlock( String absPath ) - throws PathNotFoundException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException; -} Index: modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java (revision 1855) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrNode.java (working copy) @@ -24,6 +24,7 @@ package org.modeshape.jcr; import java.io.InputStream; +import java.security.AccessControlException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -51,6 +52,7 @@ import javax.jcr.RepositoryException; import javax.jcr.UnsupportedRepositoryOperationException; import javax.jcr.Value; import javax.jcr.ValueFormatException; +import javax.jcr.lock.Lock; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.nodetype.NoSuchNodeTypeException; @@ -82,7 +84,6 @@ import org.modeshape.graph.session.GraphSession.PropertyInfo; import org.modeshape.jcr.SessionCache.JcrNodePayload; import org.modeshape.jcr.SessionCache.JcrPropertyPayload; import org.modeshape.jcr.SessionCache.NodeEditor; -import org.modeshape.jcr.api.Lock; /** * An abstract implementation of the JCR {@link javax.jcr.Node} interface. Instances of this class are created and managed by the @@ -121,10 +122,6 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node return nodeInfo().getSegment(); } - JcrLockManager lockManager() { - return session().lockManager(); - } - final Node nodeInfo() throws InvalidItemStateException, AccessDeniedException, RepositoryException { try { @@ -527,7 +524,14 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node // Execute a query that will report all nodes referencing this node ... String uuid = getUUID(); QueryBuilder builder = new QueryBuilder(context().getValueFactories().getTypeSystem()); - QueryCommand query = builder.select("jcr:primaryType").fromAllNodesAs("allNodes").where().referenceValue("allNodes").isEqualTo(uuid).end().limit(maxNumberOfNodes).query(); + QueryCommand query = builder.select("jcr:primaryType") + .fromAllNodesAs("allNodes") + .where() + .referenceValue("allNodes") + .isEqualTo(uuid) + .end() + .limit(maxNumberOfNodes) + .query(); Query jcrQuery = session().workspace().queryManager().createQuery(query); QueryResult result = jcrQuery.execute(); return result.getNodes(); @@ -989,7 +993,7 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node JcrNodeType mixinCandidateType = cache.nodeTypes().getNodeType(mixinName); // Check this separately since it throws a different type of exception - if (this.isLocked() && !getLock().isLockOwningSession()) { + if (this.isLocked() && !holdsLock()) { throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location)); } @@ -1025,7 +1029,7 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node public final void removeMixin( String mixinName ) throws RepositoryException { checkSession(); - if (this.isLocked() && !getLock().isLockOwningSession()) { + if (this.isLocked() && !holdsLock()) { throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location)); } @@ -1154,7 +1158,7 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException { checkSession(); - if (this.isLocked() && !getLock().isLockOwningSession()) { + if (this.isLocked() && !holdsLock()) { throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location)); } @@ -1220,7 +1224,7 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node RepositoryException { checkSession(); - if (isLocked() && !getLock().isLockOwningSession()) { + if (isLocked() && !holdsLock()) { throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location)); } @@ -1316,7 +1320,7 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node CheckArg.isNotEmpty(relPath, relPath); checkSession(); - if (isLocked() && !getLock().isLockOwningSession()) { + if (isLocked() && !holdsLock()) { return false; } @@ -1781,7 +1785,9 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node */ public final boolean holdsLock() throws RepositoryException { checkSession(); - return lockManager().holdsLock(this); + WorkspaceLockManager.ModeShapeLock lock = session().workspace().lockManager().lockFor(session(), this.location); + + return lock != null && cache.session().lockTokens().contains(lock.getLockToken()); } /** @@ -1791,7 +1797,7 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node * @see javax.jcr.Node#isLocked() */ public final boolean isLocked() throws LockException, RepositoryException { - return lockManager().isLocked(this); + return lock() != null; } /** @@ -1802,7 +1808,37 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node public final Lock lock( boolean isDeep, boolean isSessionScoped ) throws LockException, RepositoryException { checkSession(); - return lockManager().lock(this, isDeep, isSessionScoped, -1L, null); + if (!isLockable()) { + throw new LockException(JcrI18n.nodeNotLockable.text(getPath())); + } + + if (isLocked()) { + throw new LockException(JcrI18n.alreadyLocked.text(this.location)); + } + + if (isDeep) { + LinkedList> nodesToVisit = new LinkedList>(); + nodesToVisit.add(nodeInfo()); + + while (!nodesToVisit.isEmpty()) { + Node node = nodesToVisit.remove(nodesToVisit.size() - 1); + if (session().workspace().lockManager().lockFor(session(), node.getLocation()) != null) throw new LockException( + JcrI18n.parentAlreadyLocked.text(this.location, + node.getLocation())); + + for (Node child : node.getChildren()) { + nodesToVisit.add(child); + } + } + } + + WorkspaceLockManager.ModeShapeLock lock = session().workspace().lockManager().lock(session(), + this.location, + isDeep, + isSessionScoped); + + cache.session().addLockToken(lock.getLockToken()); + return lock.lockFor(cache); } /** @@ -1812,7 +1848,43 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node */ public final void unlock() throws LockException, RepositoryException { checkSession(); - lockManager().unlock(this); + WorkspaceLockManager.ModeShapeLock lock = session().workspace().lockManager().lockFor(session(), this.location); + + if (lock == null) { + throw new LockException(JcrI18n.notLocked.text(this.location)); + } + + if (!session().lockTokens().contains(lock.getLockToken())) { + try { + // See if the user has the permission to break someone else's lock + session().checkPermission(cache.workspaceName(), null, ModeShapePermissions.UNLOCK_ANY); + } catch (AccessControlException iae) { + throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location)); + } + } + + session().workspace().lockManager().unlock(session().getExecutionContext(), lock); + session().removeLockToken(lock.getLockToken()); + } + + private final WorkspaceLockManager.ModeShapeLock lock() throws RepositoryException { + // This can only happen in mocked testing. + if (session() == null || session().workspace() == null) return null; + + WorkspaceLockManager lockManager = session().workspace().lockManager(); + WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session(), this.location); + if (lock != null) return lock; + + AbstractJcrNode parent = this; + while (!parent.isRoot()) { + parent = parent.getParent(); + + WorkspaceLockManager.ModeShapeLock parentLock = lockManager.lockFor(session(), parent.location); + if (parentLock != null && parentLock.isLive()) { + return parentLock.isDeep() ? parentLock : null; + } + } + return null; } /** @@ -1822,7 +1894,10 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node */ public final Lock getLock() throws LockException, RepositoryException { checkSession(); - return lockManager().getLock(this); + WorkspaceLockManager.ModeShapeLock lock = lock(); + + if (lock == null) throw new LockException(JcrI18n.notLocked.text(this.location)); + return lock.lockFor(cache); } /** @@ -1948,8 +2023,8 @@ abstract class AbstractJcrNode extends AbstractJcrItem implements javax.jcr.Node if (destChildRelPath != null) { Path destPath = pathFactory.create(destChildRelPath); if (destPath.isAbsolute() || destPath.size() != 1) { - throw new ItemNotFoundException( - JcrI18n.pathNotFound.text(destPath.getString(cache.context().getNamespaceRegistry()), + throw new ItemNotFoundException(JcrI18n.pathNotFound.text(destPath.getString(cache.context() + .getNamespaceRegistry()), cache.session().workspace().getName())); } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrProperty.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrProperty.java (revision 1855) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/AbstractJcrProperty.java (working copy) @@ -32,6 +32,7 @@ import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.Property; import javax.jcr.RepositoryException; +import javax.jcr.lock.Lock; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.nodetype.PropertyDefinition; @@ -44,7 +45,6 @@ import org.modeshape.graph.property.ValueFactory; import org.modeshape.graph.session.GraphSession.PropertyInfo; import org.modeshape.jcr.SessionCache.JcrPropertyPayload; import org.modeshape.jcr.SessionCache.NodeEditor; -import org.modeshape.jcr.api.Lock; /** * An abstract {@link Property JCR Property} implementation. @@ -84,7 +84,7 @@ abstract class AbstractJcrProperty extends AbstractJcrItem implements Property, */ protected final void checkForLock() throws LockException, RepositoryException { - if (this.getParent().isLocked() && !getParent().getLock().isLockOwningSession()) { + if (this.getParent().isLocked()) { Lock parentLock = this.getParent().getLock(); if (parentLock != null && parentLock.getLockToken() == null) { throw new LockException(JcrI18n.lockTokenNotHeld.text(this.getParent().location)); @@ -258,10 +258,10 @@ abstract class AbstractJcrProperty extends AbstractJcrItem implements Property, */ public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException { checkSession(); - AbstractJcrNode parentNode = getParent(); + Node parentNode = getParent(); if (parentNode.isLocked()) { - Lock parentLock = parentNode.lockManager().getLock(parentNode); - if (parentLock != null && !parentLock.isLockOwningSession()) { + Lock parentLock = parentNode.getLock(); + if (parentLock != null && parentLock.getLockToken() == null) { throw new LockException(JcrI18n.lockTokenNotHeld.text(getPath())); } } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrI18n.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrI18n.java (revision 1855) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrI18n.java (working copy) @@ -205,7 +205,6 @@ public final class JcrI18n { public static I18n notLocked; public static I18n lockTokenNotHeld; public static I18n lockTokenAlreadyHeld; - public static I18n invalidLockToken; public static I18n uuidRequiredForLock; // JcrObservationManager messages Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLockManager.java deleted file mode 100644 =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrLockManager.java (revision 1855) +++ /dev/null (working copy) @@ -1,278 +0,0 @@ -/* - * ModeShape (http://www.modeshape.org) - * See the COPYRIGHT.txt file distributed with this work for information - * regarding copyright ownership. Some portions may be licensed - * to Red Hat, Inc. under one or more contributor license agreements. - * See the AUTHORS.txt file in the distribution for a full listing of - * individual contributors. - * - * ModeShape is free software. Unless otherwise indicated, all code in ModeShape - * is licensed to you under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * ModeShape is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ -package org.modeshape.jcr; - -import java.security.AccessControlException; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Set; -import java.util.UUID; -import javax.jcr.AccessDeniedException; -import javax.jcr.InvalidItemStateException; -import javax.jcr.PathNotFoundException; -import javax.jcr.RepositoryException; -import javax.jcr.lock.Lock; -import javax.jcr.lock.LockException; -import org.modeshape.common.util.CheckArg; -import org.modeshape.graph.session.GraphSession.Node; -import org.modeshape.jcr.SessionCache.JcrNodePayload; -import org.modeshape.jcr.SessionCache.JcrPropertyPayload; -import org.modeshape.jcr.WorkspaceLockManager.ModeShapeLock; -import org.modeshape.jcr.api.LockManager; - -/** - * A per-session lock manager for a given workspace. This class encapsulates the session-specific locking logic and checks that do - * not occur in @{link WorkspaceLockManager}. - */ -public class JcrLockManager implements LockManager { - - private final JcrSession session; - private final WorkspaceLockManager lockManager; - private final Set lockTokens; - - JcrLockManager( JcrSession session, - WorkspaceLockManager lockManager ) { - this.session = session; - this.lockManager = lockManager; - lockTokens = new HashSet(); - } - - @Override - public void addLockToken( String lockToken ) throws LockException { - CheckArg.isNotNull(lockToken, "lock token"); - - // Trivial case of giving a token back to ourself - if (lockTokens.contains(lockToken)) { - return; - } - - if (lockManager.isHeldBySession(session, lockToken)) { - throw new LockException(JcrI18n.lockTokenAlreadyHeld.text(lockToken)); - } - - lockManager.setHeldBySession(session, lockToken, true); - lockTokens.add(lockToken); - } - - @Override - public Lock getLock( String absPath ) throws PathNotFoundException, LockException, AccessDeniedException, RepositoryException { - AbstractJcrNode node = session.getNode(absPath); - return getLock(node); - } - - org.modeshape.jcr.api.Lock getLock( AbstractJcrNode node ) - throws PathNotFoundException, LockException, AccessDeniedException, RepositoryException { - WorkspaceLockManager.ModeShapeLock lock = lockFor(node); - if (lock != null) return lock.lockFor(node.cache); - throw new LockException(JcrI18n.notLocked.text(node.location)); - } - - @Override - public String[] getLockTokens() { - Set publicTokens = new HashSet(lockTokens); - - for (Iterator iter = publicTokens.iterator(); iter.hasNext();) { - String token = iter.next(); - WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(token); - if (lock.isSessionScoped()) iter.remove(); - } - - return publicTokens.toArray(new String[publicTokens.size()]); - } - - Set lockTokens() { - return this.lockTokens; - } - - @Override - public boolean holdsLock( String absPath ) throws PathNotFoundException, RepositoryException { - AbstractJcrNode node = session.getNode(absPath); - return holdsLock(node); - } - - boolean holdsLock( AbstractJcrNode node ) { - WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session, node.location); - - return lock != null; - - } - - @Override - public boolean isLocked( String absPath ) throws PathNotFoundException, RepositoryException { - AbstractJcrNode node = session.getNode(absPath); - return isLocked(node); - } - - boolean isLocked( AbstractJcrNode node ) throws PathNotFoundException, RepositoryException { - return lockFor(node) != null; - } - - @Override - public Lock lock( String absPath, - boolean isDeep, - boolean isSessionScoped, - long timeoutHint, - String ownerInfo ) - throws LockException, PathNotFoundException, AccessDeniedException, InvalidItemStateException, RepositoryException { - AbstractJcrNode node = session.getNode(absPath); - return lock(node, isDeep, isSessionScoped, timeoutHint, ownerInfo); - } - - org.modeshape.jcr.api.Lock lock( AbstractJcrNode node, - boolean isDeep, - boolean isSessionScoped, - long timeoutHint, - String ownerInfo ) - throws LockException, PathNotFoundException, AccessDeniedException, InvalidItemStateException, RepositoryException { - if (!node.isLockable()) { - throw new LockException(JcrI18n.nodeNotLockable.text(node.getPath())); - } - - if (node.isLocked()) { - throw new LockException(JcrI18n.alreadyLocked.text(node.location)); - } - - if (node.isModified()) { - throw new InvalidItemStateException(); - } - - if (isDeep) { - LinkedList> nodesToVisit = new LinkedList>(); - nodesToVisit.add(node.nodeInfo()); - - while (!nodesToVisit.isEmpty()) { - Node graphNode = nodesToVisit.remove(nodesToVisit.size() - 1); - if (lockManager.lockFor(session, graphNode.getLocation()) != null) throw new LockException( - JcrI18n.parentAlreadyLocked.text(node.location, - graphNode.getLocation())); - - for (Node child : graphNode.getChildren()) { - nodesToVisit.add(child); - } - } - } - - WorkspaceLockManager.ModeShapeLock lock = lockManager.lock(session, node.location, isDeep, isSessionScoped); - - addLockToken(lock.getLockToken()); - return lock.lockFor(session.cache()); - - } - - @Override - public void removeLockToken( String lockToken ) throws LockException { - CheckArg.isNotNull(lockToken, "lockToken"); - // A LockException is thrown if the lock associated with the specified lock token is session-scoped. - - if (!lockTokens.contains(lockToken)) { - throw new LockException(JcrI18n.invalidLockToken.text(lockToken)); - } - - /* - * The JCR API library that we're using diverges from the spec in that it doesn't declare - * this method to throw a LockException. We'll throw a runtime exception for now. - */ - - ModeShapeLock lock = lockManager.lockFor(lockToken); - if (lock == null) { - // The lock is no longer valid - lockTokens.remove(lockToken); - return; - } - - if (lock.isSessionScoped()) { - throw new IllegalStateException(JcrI18n.cannotRemoveLockToken.text(lockToken)); - } - - lockManager.setHeldBySession(session, lockToken, false); - lockTokens.remove(lockToken); - } - - @Override - public void unlock( String absPath ) - throws PathNotFoundException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException { - AbstractJcrNode node = session.getNode(absPath); - unlock(node); - } - - void unlock( AbstractJcrNode node ) - throws PathNotFoundException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException { - WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session, node.location); - - if (lock == null) { - throw new LockException(JcrI18n.notLocked.text(node.location)); - } - - if (lockTokens.contains(lock.getLockToken())) { - lockManager.unlock(session.getExecutionContext(), lock); - removeLockToken(lock.getLockToken()); - } else { - try { - // See if the user has the permission to break someone else's lock - session.checkPermission(session.cache().workspaceName(), null, ModeShapePermissions.UNLOCK_ANY); - - // This user doesn't have the lock token, so don't try to remove it - lockManager.unlock(session.getExecutionContext(), lock); - } catch (AccessControlException iae) { - throw new LockException(JcrI18n.lockTokenNotHeld.text(node.location)); - } - } - - } - - /** - * - */ - final void cleanLocks() { - lockManager.cleanLocks(session); - } - - final WorkspaceLockManager.ModeShapeLock lockFor( AbstractJcrNode node ) throws RepositoryException { - // This can only happen in mocked testing. - if (session == null || session.workspace() == null) return null; - - WorkspaceLockManager.ModeShapeLock lock = lockManager.lockFor(session, node.location); - if (lock != null) return lock; - - AbstractJcrNode parent = node; - while (!parent.isRoot()) { - parent = parent.getParent(); - - WorkspaceLockManager.ModeShapeLock parentLock = lockManager.lockFor(session, parent.location); - if (parentLock != null && parentLock.isLive()) { - return parentLock.isDeep() ? parentLock : null; - } - } - return null; - } - - final WorkspaceLockManager.ModeShapeLock lockFor( UUID nodeUuid ) throws RepositoryException { - // This can only happen in mocked testing. - if (session == null || session.workspace() == null) return null; - - return lockManager.lockFor(nodeUuid); - } - -} Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrNode.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrNode.java (revision 1855) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrNode.java (working copy) @@ -26,13 +26,13 @@ package org.modeshape.jcr; import javax.jcr.ItemNotFoundException; import javax.jcr.Node; import javax.jcr.RepositoryException; +import javax.jcr.lock.Lock; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.version.VersionException; import net.jcip.annotations.NotThreadSafe; import org.modeshape.graph.Location; import org.modeshape.graph.session.GraphSession.NodeId; -import org.modeshape.jcr.api.Lock; /** * A concrete {@link Node JCR Node} implementation. @@ -105,8 +105,8 @@ class JcrNode extends AbstractJcrNode { public void remove() throws RepositoryException, LockException { Node parentNode = getParent(); if (parentNode.isLocked()) { - Lock parentLock = lockManager().getLock(this); - if (parentLock != null && !parentLock.isLockOwningSession()) { + Lock parentLock = parentNode.getLock(); + if (parentLock != null && parentLock.getLockToken() == null) { throw new LockException(JcrI18n.lockTokenNotHeld.text(this.location)); } } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrSession.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrSession.java (revision 1855) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrSession.java (working copy) @@ -29,6 +29,7 @@ import java.io.OutputStream; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -87,6 +88,7 @@ import org.modeshape.jcr.JcrContentHandler.SaveMode; import org.modeshape.jcr.JcrNamespaceRegistry.Behavior; import org.modeshape.jcr.JcrRepository.Option; import org.modeshape.jcr.SessionCache.JcrPropertyPayload; +import org.modeshape.jcr.WorkspaceLockManager.ModeShapeLock; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -135,6 +137,8 @@ class JcrSession implements Session { private final SessionCache cache; + private final Set lockTokens; + /** * A cached instance of the root path. */ @@ -176,8 +180,11 @@ class JcrSession implements Session { this.cache = new SessionCache(this); this.isLive = true; + this.lockTokens = new HashSet(); - this.performReferentialIntegrityChecks = Boolean.valueOf(repository.getOptions().get(Option.PERFORM_REFERENTIAL_INTEGRITY_CHECKS)).booleanValue(); + this.performReferentialIntegrityChecks = Boolean.valueOf(repository.getOptions() + .get(Option.PERFORM_REFERENTIAL_INTEGRITY_CHECKS)) + .booleanValue(); assert this.sessionAttributes != null; assert this.workspace != null; @@ -221,10 +228,6 @@ class JcrSession implements Session { return this.executionContext.getId(); } - JcrLockManager lockManager() { - return workspace.lockManager(); - } - JcrNodeTypeManager nodeTypeManager() { return this.workspace.nodeTypeManager(); } @@ -246,6 +249,10 @@ class JcrSession implements Session { return this.repository; } + final Collection lockTokens() { + return lockTokens; + } + Graph.Batch createBatch() { return graph.batch(); } @@ -350,11 +357,17 @@ class JcrSession implements Session { public void addLockToken( String lt ) throws LockException { CheckArg.isNotNull(lt, "lock token"); - try { - lockManager().addLockToken(lt); - } catch (LockException le) { - // For backwards compatibility (and API compatibility), the LockExceptions from the LockManager need to get swallowed + // Trivial case of giving a token back to ourself + if (lockTokens.contains(lt)) { + return; + } + + if (workspace().lockManager().isHeldBySession(this, lt)) { + throw new LockException(JcrI18n.lockTokenAlreadyHeld.text(lt)); } + + workspace().lockManager().setHeldBySession(this, lt, true); + lockTokens.add(lt); } /** @@ -738,7 +751,7 @@ class JcrSession implements Session { * @see javax.jcr.Session#getLockTokens() */ public String[] getLockTokens() { - return lockManager().getLockTokens(); + return lockTokens.toArray(new String[lockTokens.size()]); } /** @@ -1001,7 +1014,7 @@ class JcrSession implements Session { isLive = false; this.workspace().observationManager().removeAllEventListeners(); - this.lockManager().cleanLocks(); + this.workspace().lockManager().cleanLocks(this); this.repository.sessionLoggedOut(this); this.executionContext.getSecurityContext().logout(); } @@ -1028,14 +1041,14 @@ class JcrSession implements Session { AbstractJcrNode sourceNode = getNode(pathFactory.create(srcAbsPath)); AbstractJcrNode newParentNode = getNode(destPath.getParent()); - if (sourceNode.isLocked() && !sourceNode.getLock().isLockOwningSession()) { + if (sourceNode.isLocked()) { javax.jcr.lock.Lock sourceLock = sourceNode.getLock(); if (sourceLock != null && sourceLock.getLockToken() == null) { throw new LockException(JcrI18n.lockTokenNotHeld.text(srcAbsPath)); } } - if (newParentNode.isLocked() && !newParentNode.getLock().isLockOwningSession()) { + if (newParentNode.isLocked()) { javax.jcr.lock.Lock newParentLock = newParentNode.getLock(); if (newParentLock != null && newParentLock.getLockToken() == null) { throw new LockException(JcrI18n.lockTokenNotHeld.text(destAbsPath)); @@ -1067,14 +1080,27 @@ class JcrSession implements Session { * * @see javax.jcr.Session#removeLockToken(java.lang.String) */ - public void removeLockToken( String lockToken ) { - CheckArg.isNotNull(lockToken, "lock token"); + public void removeLockToken( String lt ) { + CheckArg.isNotNull(lt, "lock token"); // A LockException is thrown if the lock associated with the specified lock token is session-scoped. - try { - lockManager().removeLockToken(lockToken); - } catch (LockException le) { - // For backwards compatibility (and API compatibility), the LockExceptions from the LockManager need to get swallowed + /* + * The JCR API library that we're using diverges from the spec in that it doesn't declare + * this method to throw a LockException. We'll throw a runtime exception for now. + */ + + ModeShapeLock lock = workspace().lockManager().lockFor(lt); + if (lock == null) { + // The lock is no longer valid + lockTokens.remove(lt); + return; + } + + if (lock.isSessionScoped()) { + throw new IllegalStateException(JcrI18n.cannotRemoveLockToken.text(lt)); } + + workspace().lockManager().setHeldBySession(this, lt, false); + lockTokens.remove(lt); } void recordRemoval( Location location ) throws RepositoryException { @@ -1095,8 +1121,13 @@ class JcrSession implements Session { TypeSystem typeSystem = executionContext.getValueFactories().getTypeSystem(); QueryBuilder builder = new QueryBuilder(typeSystem); - QueryCommand query = builder.select("jcr:uuid").from("mix:referenceable AS referenceable").where().path("referenceable").isLike(pathStr - + "%").end().query(); + QueryCommand query = builder.select("jcr:uuid") + .from("mix:referenceable AS referenceable") + .where() + .path("referenceable") + .isLike(pathStr + "%") + .end() + .query(); JcrQueryManager queryManager = workspace().queryManager(); Query jcrQuery = queryManager.createQuery(query); QueryResult result = jcrQuery.execute(); @@ -1189,10 +1220,24 @@ class JcrSession implements Session { QueryBuilder builder = new QueryBuilder(typeSystem); QueryCommand query = null; if (subgraphPath != null) { - query = builder.select("jcr:primaryType").fromAllNodesAs("allNodes").where().referenceValue("allNodes").isIn(someUuidsInBranch).and().path("allNodes").isLike(subgraphPath - + "%").end().query(); + query = builder.select("jcr:primaryType") + .fromAllNodesAs("allNodes") + .where() + .referenceValue("allNodes") + .isIn(someUuidsInBranch) + .and() + .path("allNodes") + .isLike(subgraphPath + "%") + .end() + .query(); } else { - query = builder.select("jcr:primaryType").fromAllNodesAs("allNodes").where().referenceValue("allNodes").isIn(someUuidsInBranch).end().query(); + query = builder.select("jcr:primaryType") + .fromAllNodesAs("allNodes") + .where() + .referenceValue("allNodes") + .isIn(someUuidsInBranch) + .end() + .query(); } Query jcrQuery = workspace().queryManager().createQuery(query); // The nodes that have been (transiently) deleted will not appear in these results ... Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrWorkspace.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrWorkspace.java (revision 1855) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrWorkspace.java (working copy) @@ -78,7 +78,6 @@ import org.modeshape.jcr.JcrContentHandler.SaveMode; import org.modeshape.jcr.SessionCache.JcrNodePayload; import org.modeshape.jcr.SessionCache.JcrPropertyPayload; import org.modeshape.jcr.WorkspaceLockManager.ModeShapeLock; -import org.modeshape.jcr.api.LockManager; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -146,7 +145,7 @@ class JcrWorkspace implements Workspace { */ private final JcrObservationManager observationManager; - private final JcrLockManager lockManager; + private final WorkspaceLockManager lockManager; /** * The {@link Session} instance that this corresponds with this workspace. @@ -164,6 +163,7 @@ class JcrWorkspace implements Workspace { assert repository != null; this.name = workspaceName; this.repository = repository; + this.lockManager = repository.getLockManager(workspaceName); // Create an execution context for this session, which should use the local namespace registry ... NamespaceRegistry globalRegistry = context.getNamespaceRegistry(); @@ -189,7 +189,6 @@ class JcrWorkspace implements Workspace { // // Set up and initialize the persistent JCR namespace registry ... this.workspaceRegistry = new JcrNamespaceRegistry(this.repository.getPersistentRegistry(), this.session); - this.lockManager = new JcrLockManager(session, repository.getLockManager(workspaceName)); } @@ -209,7 +208,7 @@ class JcrWorkspace implements Workspace { return this.context; } - final JcrLockManager lockManager() { + final WorkspaceLockManager lockManager() { return this.lockManager; } @@ -285,13 +284,6 @@ class JcrWorkspace implements Workspace { } /** - * @return the lock manager for this workspace and session - */ - public LockManager getLockManager() { - return lockManager; - } - - /** * {@inheritDoc} */ public final QueryManager getQueryManager() { @@ -357,7 +349,7 @@ class JcrWorkspace implements Workspace { if (uuidProp != null) { UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue()); - ModeShapeLock sourceLock = lockManager().lockFor(sourceUuid); + ModeShapeLock sourceLock = lockManager().lockFor(session, Location.create(sourceUuid)); if (sourceLock != null && sourceLock.getLockToken() == null) { throw new LockException(JcrI18n.lockTokenNotHeld.text(srcAbsPath)); } @@ -517,7 +509,7 @@ class JcrWorkspace implements Workspace { if (uuidProp != null) { UUID sourceUuid = this.context.getValueFactories().getUuidFactory().create(uuidProp.getFirstValue()); - ModeShapeLock sourceLock = lockManager().lockFor(sourceUuid); + ModeShapeLock sourceLock = lockManager().lockFor(session, Location.create(sourceUuid)); if (sourceLock != null && sourceLock.getLockToken() == null) { throw new LockException(srcAbsPath); } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/WorkspaceLockManager.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/WorkspaceLockManager.java (revision 1855) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/WorkspaceLockManager.java (working copy) @@ -1,26 +1,3 @@ -/* - * ModeShape (http://www.modeshape.org) - * See the COPYRIGHT.txt file distributed with this work for information - * regarding copyright ownership. Some portions may be licensed - * to Red Hat, Inc. under one or more contributor license agreements. - * See the AUTHORS.txt file in the distribution for a full listing of - * individual contributors. - * - * ModeShape is free software. Unless otherwise indicated, all code in ModeShape - * is licensed to you under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * ModeShape is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ package org.modeshape.jcr; import java.util.Collection; @@ -29,11 +6,12 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.jcr.Item; import javax.jcr.Node; +import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.lock.Lock; import javax.jcr.lock.LockException; import net.jcip.annotations.ThreadSafe; -import org.modeshape.common.i18n.I18n; import org.modeshape.graph.ExecutionContext; import org.modeshape.graph.Graph; import org.modeshape.graph.Location; @@ -46,6 +24,7 @@ import org.modeshape.graph.property.PathNotFoundException; import org.modeshape.graph.property.Property; import org.modeshape.graph.property.PropertyFactory; import org.modeshape.graph.property.ValueFactory; +import org.modeshape.jcr.SessionCache.NodeEditor; /** * Manages the locks for a particular workspace in a repository. Locks are stored in a {@code Map} while they exist @@ -72,10 +51,7 @@ class WorkspaceLockManager { this.workspaceLocksByNodeUuid = new ConcurrentHashMap(); Property locksPrimaryType = context.getPropertyFactory().create(JcrLexicon.PRIMARY_TYPE, ModeShapeLexicon.LOCKS); - - if (locksPath != null) { - repository.createSystemGraph(context).create(locksPath, locksPrimaryType).ifAbsent().and(); - } + repository.createSystemGraph(context).create(locksPath, locksPrimaryType).ifAbsent().and(); } /** @@ -94,9 +70,9 @@ class WorkspaceLockManager { * @throws RepositoryException if an error occurs updating the graph state */ ModeShapeLock lock( JcrSession session, - Location nodeLocation, - boolean isDeep, - boolean isSessionScoped ) throws RepositoryException { + Location nodeLocation, + boolean isDeep, + boolean isSessionScoped ) throws RepositoryException { assert nodeLocation != null; UUID lockUuid = UUID.randomUUID(); @@ -134,8 +110,17 @@ class WorkspaceLockManager { lockIsDeepProp).ifAbsent().and(); batch.execute(); + SessionCache cache = session.cache(); + AbstractJcrNode lockedNode = cache.findJcrNode(Location.create(nodeUuid)); + NodeEditor editor = cache.getEditorFor(lockedNode.nodeInfo()); + + // Set the properties in the cache... + editor.setProperty(JcrLexicon.LOCK_OWNER, + (JcrValue)cache.session().getValueFactory().createValue(lockOwner, PropertyType.STRING), + false); + editor.setProperty(JcrLexicon.LOCK_IS_DEEP, (JcrValue)cache.session().getValueFactory().createValue(isDeep), false); + lockNodeInRepository(session, nodeUuid, lockOwnerProp, lockIsDeepProp, lock, isDeep); - session.cache().refreshProperties(Location.create(nodeUuid)); workspaceLocksByNodeUuid.put(nodeUuid, lock); return lock; @@ -147,10 +132,10 @@ class WorkspaceLockManager { /* Factory method added to facilitate mocked testing */ ModeShapeLock createLock( String lockOwner, - UUID lockUuid, - UUID nodeUuid, - boolean isDeep, - boolean isSessionScoped ) { + UUID lockUuid, + UUID nodeUuid, + boolean isDeep, + boolean isSessionScoped ) { return new ModeShapeLock(lockOwner, lockUuid, nodeUuid, isDeep, isSessionScoped); } @@ -268,25 +253,20 @@ class WorkspaceLockManager { * @param session the session on behalf of which the lock query is being performed * @param lockToken the lock token to check; may not be null * @return true if a session currently holds the lock token, false otherwise - * @throws LockException if the lock token doesn't exist */ boolean isHeldBySession( JcrSession session, - String lockToken ) throws LockException { + String lockToken ) { assert lockToken != null; ExecutionContext context = session.getExecutionContext(); ValueFactory booleanFactory = context.getValueFactories().getBooleanFactory(); PathFactory pathFactory = context.getValueFactories().getPathFactory(); - try { - org.modeshape.graph.Node lockNode = repository.createSystemGraph(context).getNodeAt(pathFactory.create(locksPath, - pathFactory.createSegment(lockToken))); + org.modeshape.graph.Node lockNode = repository.createSystemGraph(context) + .getNodeAt(pathFactory.create(locksPath, + pathFactory.createSegment(lockToken))); - return booleanFactory.create(lockNode.getProperty(ModeShapeLexicon.IS_HELD_BY_SESSION).getFirstValue()); - } catch (PathNotFoundException pnfe) { - I18n msg = JcrI18n.invalidLockToken; - throw new LockException(msg.text(lockToken)); - } + return booleanFactory.create(lockNode.getProperty(ModeShapeLexicon.IS_HELD_BY_SESSION).getFirstValue()); } @@ -308,8 +288,9 @@ class WorkspaceLockManager { PropertyFactory propFactory = context.getPropertyFactory(); PathFactory pathFactory = context.getValueFactories().getPathFactory(); - repository.createSystemGraph(context).set(propFactory.create(ModeShapeLexicon.IS_HELD_BY_SESSION, value)).on(pathFactory.create(locksPath, - pathFactory.createSegment(lockToken))); + repository.createSystemGraph(context) + .set(propFactory.create(ModeShapeLexicon.IS_HELD_BY_SESSION, value)) + .on(pathFactory.create(locksPath, pathFactory.createSegment(lockToken))); } /** @@ -338,18 +319,8 @@ class WorkspaceLockManager { * @return the corresponding lock, possibly null if there is no such lock */ ModeShapeLock lockFor( JcrSession session, - Location nodeLocation ) { + Location nodeLocation ) { UUID nodeUuid = uuidFor(session, nodeLocation); - return lockFor(nodeUuid); - } - - /** - * Returns the lock that corresponds to the given UUID - * - * @param nodeUuid the node UUID - * @return the corresponding lock, possibly null if there is no such lock - */ - ModeShapeLock lockFor( UUID nodeUuid ) { if (nodeUuid == null) return null; return workspaceLocksByNodeUuid.get(nodeUuid); } @@ -383,7 +354,7 @@ class WorkspaceLockManager { */ void cleanLocks( JcrSession session ) { ExecutionContext context = session.getExecutionContext(); - Collection lockTokens = session.lockManager().lockTokens(); + Collection lockTokens = session.lockTokens(); for (String lockToken : lockTokens) { ModeShapeLock lock = lockFor(lockToken); if (lock != null && lock.isSessionScoped()) { @@ -432,10 +403,10 @@ class WorkspaceLockManager { } ModeShapeLock( String lockOwner, - UUID lockUuid, - UUID nodeUuid, - boolean deep, - boolean sessionScoped ) { + UUID lockUuid, + UUID nodeUuid, + boolean deep, + boolean sessionScoped ) { super(); this.lockOwner = lockOwner; this.lockUuid = lockUuid; @@ -470,18 +441,17 @@ class WorkspaceLockManager { } @SuppressWarnings( "synthetic-access" ) - public org.modeshape.jcr.api.Lock lockFor( SessionCache cache ) throws RepositoryException { + public Lock lockFor( SessionCache cache ) throws RepositoryException { final AbstractJcrNode node = cache.findJcrNode(Location.create(nodeUuid)); final JcrSession session = cache.session(); - return new org.modeshape.jcr.api.Lock() { + return new Lock() { public String getLockOwner() { return lockOwner; } public String getLockToken() { - if (sessionScoped) return null; String uuidString = lockUuid.toString(); - return session.lockManager().lockTokens().contains(uuidString) ? uuidString : null; + return session.lockTokens().contains(uuidString) ? uuidString : null; } public Node getNode() { @@ -501,24 +471,12 @@ class WorkspaceLockManager { } public void refresh() throws LockException { - String uuidString = lockUuid.toString(); - if (!session.lockManager().lockTokens().contains(uuidString)) { + if (getLockToken() == null) { throw new LockException(JcrI18n.notLocked.text(node.location)); } } - - @Override - public long getSecondsRemaining() throws RepositoryException { - return isLockOwningSession() ? Integer.MAX_VALUE : Integer.MIN_VALUE; - } - - @Override - public boolean isLockOwningSession() { - String uuidString = lockUuid.toString(); - return session.lockManager().lockTokens().contains(uuidString); - } - }; } + } } Index: modeshape-jcr/src/main/resources/org/modeshape/jcr/JcrI18n.properties =================================================================== --- modeshape-jcr/src/main/resources/org/modeshape/jcr/JcrI18n.properties (revision 1855) +++ modeshape-jcr/src/main/resources/org/modeshape/jcr/JcrI18n.properties (working copy) @@ -190,7 +190,6 @@ alreadyLocked = The node at location '{0}' is already locked parentAlreadyLocked = The node at location '{0}' cannot be locked because the parent node at location '{1}' is already locked notLocked = The node at location '{0}' is not locked lockTokenNotHeld = The node at location '{0}' is locked and this session does not hold its lock token -invalidLockToken = The lock token '{0}' is not valid lockTokenAlreadyHeld = The lock token '{0}' is already held by another session. It must be removed from that session before it can be added to another session. uuidRequiredForLock = Only referenceable nodes can be locked. The node at location '(0}' is not referenceable. Index: modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java (revision 1855) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrNodeTest.java (working copy) @@ -656,15 +656,11 @@ public class AbstractJcrNodeTest extends AbstractJcrTest { SessionCache cache2 = new SessionCache(jcrSession2, store2.getCurrentWorkspaceName(), context, nodeTypes, store2); Workspace workspace2 = mock(Workspace.class); - JcrRepository repository2 = mock(JcrRepository.class); + Repository repository2 = mock(Repository.class); when(jcrSession2.getWorkspace()).thenReturn(workspace2); when(jcrSession2.getRepository()).thenReturn(repository2); when(workspace2.getName()).thenReturn("workspace2"); - WorkspaceLockManager lockManager = new WorkspaceLockManager(context, repository2, "workspace2", null); - JcrLockManager jcrLockManager = new JcrLockManager(jcrSession2, lockManager); - when(jcrSession2.lockManager()).thenReturn(jcrLockManager); - // Use the same id and location; use 'Toyota Prius' // since the UUID is defined in 'cars.xml' and therefore will be the same javax.jcr.Node prius2 = cache2.findJcrNode(null, path("/Cars/Hybrid/Toyota Prius")); @@ -689,15 +685,11 @@ public class AbstractJcrNodeTest extends AbstractJcrTest { SessionCache cache2 = new SessionCache(jcrSession2, store2.getCurrentWorkspaceName(), context, nodeTypes, store2); Workspace workspace2 = mock(Workspace.class); - JcrRepository repository2 = mock(JcrRepository.class); + Repository repository2 = mock(Repository.class); when(jcrSession2.getWorkspace()).thenReturn(workspace2); when(jcrSession2.getRepository()).thenReturn(repository2); when(workspace2.getName()).thenReturn("workspace1"); - WorkspaceLockManager lockManager = new WorkspaceLockManager(context, repository2, "workspace2", null); - JcrLockManager jcrLockManager = new JcrLockManager(jcrSession2, lockManager); - when(jcrSession2.lockManager()).thenReturn(jcrLockManager); - // Use the same id and location; use 'Nissan Altima' // since the UUIDs will be different (cars.xml doesn't define on this node) ... javax.jcr.Node altima2 = cache2.findJcrNode(null, path("/Cars/Hybrid/Nissan Altima")); @@ -726,10 +718,6 @@ public class AbstractJcrNodeTest extends AbstractJcrTest { when(jcrSession2.getRepository()).thenReturn(repository); when(workspace2.getName()).thenReturn("workspace1"); - WorkspaceLockManager lockManager = new WorkspaceLockManager(context, repository, "workspace1", null); - JcrLockManager jcrLockManager = new JcrLockManager(jcrSession2, lockManager); - when(jcrSession2.lockManager()).thenReturn(jcrLockManager); - // Use the same id and location ... javax.jcr.Node prius2 = cache2.findJcrNode(null, path("/Cars/Hybrid/Toyota Prius")); prius2.addMixin("mix:referenceable"); Index: modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrPropertyTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrPropertyTest.java (revision 1855) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrPropertyTest.java (working copy) @@ -172,15 +172,11 @@ public class AbstractJcrPropertyTest extends AbstractJcrTest { SessionCache cache2 = new SessionCache(jcrSession2, store2.getCurrentWorkspaceName(), context, nodeTypes, store2); Workspace workspace2 = mock(Workspace.class); - JcrRepository repository2 = mock(JcrRepository.class); + Repository repository2 = mock(Repository.class); when(jcrSession2.getWorkspace()).thenReturn(workspace2); when(jcrSession2.getRepository()).thenReturn(repository2); when(workspace2.getName()).thenReturn("workspace2"); - WorkspaceLockManager lockManager = new WorkspaceLockManager(context, repository2, "workspace2", null); - JcrLockManager jcrLockManager = new JcrLockManager(jcrSession2, lockManager); - when(jcrSession2.lockManager()).thenReturn(jcrLockManager); - // Use the same id and location; use 'Toyota Prius' // since the UUID is defined in 'cars.xml' and therefore will be the same javax.jcr.Node prius2 = cache2.findJcrNode(null, path("/Cars/Hybrid/Toyota Prius")); @@ -210,15 +206,11 @@ public class AbstractJcrPropertyTest extends AbstractJcrTest { SessionCache cache2 = new SessionCache(jcrSession2, store2.getCurrentWorkspaceName(), context, nodeTypes, store2); Workspace workspace2 = mock(Workspace.class); - JcrRepository repository2 = mock(JcrRepository.class); + Repository repository2 = mock(Repository.class); when(jcrSession2.getWorkspace()).thenReturn(workspace2); when(jcrSession2.getRepository()).thenReturn(repository2); when(workspace2.getName()).thenReturn("workspace1"); - WorkspaceLockManager lockManager = new WorkspaceLockManager(context, repository2, "workspace2", null); - JcrLockManager jcrLockManager = new JcrLockManager(jcrSession2, lockManager); - when(jcrSession2.lockManager()).thenReturn(jcrLockManager); - // Use the same id and location; use 'Nissan Altima' // since the UUIDs will be different (cars.xml doesn't define on this node) ... javax.jcr.Node altima2 = cache2.findJcrNode(null, path("/Cars/Hybrid/Nissan Altima")); @@ -252,10 +244,6 @@ public class AbstractJcrPropertyTest extends AbstractJcrTest { when(jcrSession2.getRepository()).thenReturn(repository); when(workspace2.getName()).thenReturn("workspace1"); - WorkspaceLockManager lockManager = new WorkspaceLockManager(context, repository, "workspace2", null); - JcrLockManager jcrLockManager = new JcrLockManager(jcrSession2, lockManager); - when(jcrSession2.lockManager()).thenReturn(jcrLockManager); - // Use the same id and location ... javax.jcr.Node prius2 = cache2.findJcrNode(null, path("/Cars/Hybrid/Toyota Prius")); prius2.addMixin("mix:referenceable"); Index: modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrTest.java (revision 1855) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/AbstractJcrTest.java (working copy) @@ -56,8 +56,6 @@ public abstract class AbstractJcrTest { protected SessionCache cache; protected JcrSession jcrSession; protected JcrNodeTypeManager nodeTypes; - protected WorkspaceLockManager lockManager; - protected JcrLockManager jcrLockManager; protected Workspace workspace; /** @@ -119,18 +117,12 @@ public abstract class AbstractJcrTest { // Stub the session, workspace, and repository; then stub some critical methods ... jcrSession = mock(JcrSession.class); workspace = mock(Workspace.class); - String workspaceName = "workspace1"; when(jcrSession.getExecutionContext()).thenReturn(context); when(jcrSession.getWorkspace()).thenReturn(workspace); when(jcrSession.getRepository()).thenReturn(repository); - when(workspace.getName()).thenReturn(workspaceName); + when(workspace.getName()).thenReturn("workspace1"); when(jcrSession.isLive()).thenReturn(true); - lockManager = new WorkspaceLockManager(context, repository, workspaceName, null); - jcrLockManager = new JcrLockManager(jcrSession, lockManager); - - when(jcrSession.lockManager()).thenReturn(jcrLockManager); - // Create the node type manager for the session ... // no need to stub the 'JcrSession.checkPermission' methods, since we're never calling 'register' on the // JcrNodeTypeManager Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrTckTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrTckTest.java (revision 1855) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrTckTest.java (working copy) @@ -309,12 +309,12 @@ public class JcrTckTest { // We currently don't pass the tests in those suites that are commented out // See https://jira.jboss.org/jira/browse/ModeShape-285 - // addTest(new QueryTests()); - // addTest(new ObservationTests()); // remove this and the ObservationTests inner class when all tests pass and + addTest(new QueryTests()); + addTest(new ObservationTests()); // remove this and the ObservationTests inner class when all tests pass and // uncomment observation.TestAll // addTest(org.apache.jackrabbit.test.api.observation.TestAll.suite()); - // addTest(org.apache.jackrabbit.test.api.version.TestAll.suite()); + addTest(org.apache.jackrabbit.test.api.version.TestAll.suite()); addTest(org.apache.jackrabbit.test.api.lock.TestAll.suite()); addTest(org.apache.jackrabbit.test.api.util.TestAll.suite()); // addTest(org.apache.jackrabbit.test.api.query.TestAll.suite());