/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.concurrent.TimeUnit; import javax.jcr.InvalidItemStateException; import javax.jcr.Node; import javax.jcr.Session; import javax.jcr.lock.Lock; import javax.jcr.lock.LockManager; import org.junit.Assert; import org.junit.Test; import org.modeshape.common.FixFor; /** * Unit test for {@link JcrLockManager} * * @author Horia Chiorean (hchiorea@redhat.com) */ public class JcrLockManagerTest extends SingleUseAbstractTest { @Test @FixFor( "MODE-2047" ) public void shouldNotAllowLockOnTransientNode() throws Exception { AbstractJcrNode testNode = session.getRootNode().addNode("test"); testNode.addMixin("mix:lockable"); JcrLockManager lockManager = session.lockManager(); try { lockManager.lock(testNode, true, false, Long.MAX_VALUE, null); fail("Transient nodes should not be locked"); } catch (InvalidItemStateException e) { // expected } } @Test @FixFor( "MODE-2342" ) public void lockTokensShouldBeRemovedFromSessionUponLogout() throws Exception { final AbstractJcrNode testNode = session.getRootNode().addNode("test"); final String path = testNode.getPath(); testNode.addMixin("mix:lockable"); session.save(); final Lock lock = session.getWorkspace().getLockManager().lock(path, false, false, Long.MAX_VALUE, session.getUserID()); final String token = lock.getLockToken(); Assert.assertNotNull(token); session.logout(); Session session2 = repository.login(); final LockManager lockManager = session2.getWorkspace().getLockManager(); lockManager.addLockToken(token); Assert.assertTrue("New session should now own the lock.", lockManager.getLock(path).isLockOwningSession()); } @Test @FixFor( "MODE-2424" ) public void shouldAllowAddingMixinOnLockedNodeForLockOwner() throws Exception { final AbstractJcrNode testNode = session.getRootNode().addNode("test"); final String path = testNode.getPath(); testNode.addMixin("mix:lockable"); session.save(); session.getWorkspace().getLockManager().lock(path, false, true, Long.MAX_VALUE, session.getUserID()); testNode.addMixin("mix:created"); session.save(); } @Test @FixFor( "MODE-2450" ) public void shouldCleanupCorruptedLocks() throws Exception { final AbstractJcrNode testNode = session.getRootNode().addNode("test"); final String path = testNode.getPath(); testNode.addMixin("mix:lockable"); session.save(); final org.modeshape.jcr.RepositoryLockManager.Lock lock = (RepositoryLockManager.Lock) session.getWorkspace().getLockManager().lock(path, false, false, Long.MAX_VALUE, session.getUserID()); Assert.assertNotNull(lock); session.logout(); //forcibly remove the lock node from the system area... String lockKey = lock.lockKey().toString(); assertTrue(runInTransaction(() -> repository.documentStore().remove(lockKey))); //and then force a refresh RepositoryLockManager lockManager = repository.lockManager(); lockManager.refreshFromSystem(); //check that the lock has been removed session = repository.login(); assertFalse(session.getWorkspace().getLockManager().isLocked("/test")); // issue another refresh and verify the node is still unlocked lockManager.refreshFromSystem(); assertFalse(session.getWorkspace().getLockManager().isLocked("/test")); } @Test @FixFor( "MODE-2633" ) public void shouldExpireOpenScopedLocks() throws Exception { // Create a new lockable node Node node = session.getRootNode().addNode("test"); node.addMixin("mix:lockable"); session.save(); // Lock the node JcrLockManager lockManager = session.getWorkspace().getLockManager(); lockManager.lock(node.getPath(), false, false, 1, null); assertTrue(node.isLocked()); // Wait enough time for the lock to be expired Thread.sleep(TimeUnit.SECONDS.toMillis(2)); // The old lock should still be there even though it's expired assertFalse(node.isLocked()); assertFalse(lockManager.getLock(node.getPath()).isLive()); // Check that a new lock can be obtained lockManager.lock(node.getPath(), false, false, 10, null); assertTrue(node.isLocked()); } @Test @FixFor( "MODE-2641" ) public void shouldProvideTimeoutForOpenScopedLocks() throws Exception { // Create a new lockable node Node node = session.getRootNode().addNode("test"); node.addMixin("mix:lockable"); session.save(); String path = node.getPath(); // Lock the node with an open scoped lock int timeout = 2; JcrLockManager lockManager = session.getWorkspace().getLockManager(); Lock lock = lockManager.lock(node.getPath(), false, false, timeout, null); assertTrue(node.isLocked()); long secondsRemaining = lock.getSecondsRemaining(); assertTrue("Expected a valid value for seconds remaining", secondsRemaining <= timeout && secondsRemaining > 0); // Look at the same lock from another session session.logout(); session = repository.login(); lockManager = session.getWorkspace().getLockManager(); lock = lockManager.getLock(path); secondsRemaining = lock.getSecondsRemaining(); assertTrue("Expected a valid value for seconds remaining", secondsRemaining <= timeout && secondsRemaining > 0); Thread.sleep(TimeUnit.SECONDS.toMillis(2)); assertEquals("Expected a negative value because the lock should have expired", Long.MIN_VALUE, lock.getSecondsRemaining()); } @Test @FixFor( "MODE-2641" ) public void shouldNotProvideTimeoutForSessionScopedLocks() throws Exception { // Create a new lockable node Node node = session.getRootNode().addNode("test"); node.addMixin("mix:lockable"); session.save(); String path = node.getPath(); // Lock the node with a session scoped lock int timeout = 2; JcrLockManager lockManager = session.getWorkspace().getLockManager(); Lock lock = lockManager.lock(node.getPath(), false, true, timeout, null); assertTrue(node.isLocked()); assertEquals("Session scoped locks should not have timeout information", Long.MAX_VALUE, lock.getSecondsRemaining()); JcrSession otherSession = repository.login(); try { lockManager = otherSession.getWorkspace().getLockManager(); lock = lockManager.getLock(path); assertTrue(otherSession.getNode(path).isLocked()); assertEquals("Session scoped locks should not have timeout information", Long.MAX_VALUE, lock.getSecondsRemaining()); } finally { otherSession.logout(); } } @Test @FixFor("MODE-2654") public void shouldNotCopyLocks()throws Exception { Node parentNode = session.getRootNode().addNode("node"); Node childNode = parentNode.addNode("child"); childNode.addMixin("mix:lockable"); session.save(); JcrWorkspace workspace = session.getWorkspace(); JcrLockManager lockManager = workspace.getLockManager(); lockManager.lock("/node/child", false, false, Long.MAX_VALUE, null); assertTrue(session.getNode("/node/child").isLocked()); workspace.copy("/node", "/newPath"); assertFalse(session.getNode("/newPath").isLocked()); assertFalse(session.getNode("/newPath/child").isLocked()); lockManager.lock("/newPath/child", false, false, Long.MAX_VALUE, null); assertTrue(session.getNode("/newPath/child").isLocked()); } }