/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.jcr2spi.lock;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import org.apache.jackrabbit.test.AbstractJCRTest;
import org.apache.jackrabbit.test.NotExecutableException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <code>AbstractLockTest</code>...
*/
public abstract class AbstractLockTest extends AbstractJCRTest {
private static Logger log = LoggerFactory.getLogger(AbstractLockTest.class);
Node lockedNode;
Node childNode;
Lock lock;
Session otherSession;
abstract boolean isSessionScoped();
@Override
protected void setUp() throws Exception {
super.setUp();
otherSession = getHelper().getSuperuserSession();
lockedNode = testRootNode.addNode(nodeName1, testNodeType);
lockedNode.addMixin(mixLockable);
childNode = lockedNode.addNode(nodeName2, testNodeType);
testRootNode.save();
lock = lockedNode.lock(false, isSessionScoped());
}
@Override
protected void tearDown() throws Exception {
// make sure all locks are removed
try {
lockedNode.unlock();
} catch (RepositoryException e) {
// ignore
}
if (otherSession.isLive()) {
otherSession.logout();
otherSession = null;
}
lockedNode = null;
childNode = null;
lock = null;
super.tearDown();
}
public void testParentChildLock() throws Exception {
childNode.addMixin(mixLockable);
testRootNode.save();
// lock child node
try {
childNode.lock(false, isSessionScoped());
// unlock parent node
lockedNode.unlock();
// child node must still hold lock
assertTrue("child node must still hold lock", childNode.isLocked() && childNode.holdsLock());
} finally {
childNode.unlock();
}
}
public void testParentChildLock2() throws Exception {
childNode.addMixin(mixLockable);
testRootNode.save();
try {
Lock l = childNode.lock(false, isSessionScoped());
assertTrue("child node must still hold lock", l.getNode().isSame(childNode));
} finally {
childNode.unlock();
}
}
/**
* Tests if a locked, checked-in node can be unlocked
*/
public void testCheckedInUnlock() throws Exception {
if (!isSupported(Repository.OPTION_VERSIONING_SUPPORTED)) {
throw new NotExecutableException("Repository does not support versioning.");
}
lockedNode.addMixin(mixVersionable);
lockedNode.save();
// lock and check-in
lockedNode.checkin();
// do the unlock
lockedNode.unlock();
assertFalse("Could not unlock a locked, checked-in node", lockedNode.holdsLock());
}
public void testReorder() throws Exception {
testRootNode.addNode(nodeName2);
testRootNode.addNode(nodeName3);
testRootNode.save();
// move last node in front of first
testRootNode.orderBefore(lockedNode.getName(), nodeName3);
testRootNode.save();
assertTrue("Node must remain locked upon reordering", testRootNode.getNode(lockedNode.getName()).isLocked());
}
public void testReorderSNS() throws Exception {
// create 2 additional nodes with same name
testRootNode.addNode(nodeName1);
testRootNode.addNode(nodeName1);
testRootNode.save();
// assert: first node locked
assertTrue("First child node locked", testRootNode.getNode(nodeName1 + "[1]").isLocked());
// move first node to last
testRootNode.orderBefore(nodeName1 + "[1]", null);
testRootNode.save();
// assert: third node locked
assertTrue("Third child node locked", testRootNode.getNode(nodeName1 + "[3]").isLocked());
}
/**
* Tests if move preserves lock state (JIRA issue JCR-207). A node that has
* been locked must still appear locked when it has been moved or renamed,
* regardless whether the changes have already been made persistent.
*/
public void testMoveLocked() throws Exception {
Session session = testRootNode.getSession();
childNode.addMixin(mixLockable);
childNode.save();
try {
// lock child node
childNode.lock(false, isSessionScoped());
// assert: child node locked
assertTrue("Child node locked", childNode.isLocked());
// move child node up
String newPath = testRootNode.getPath() + "/" + childNode.getName();
session.move(childNode.getPath(), newPath);
// assert: child node locked, before save
assertTrue("Child node locked before save", childNode.isLocked());
session.save();
// assert: child node locked, after save
assertTrue("Child node locked after save", childNode.isLocked());
} finally {
session.refresh(false);
childNode.unlock();
}
}
/**
* Tests if unlocking the first of two locked same-name sibling nodes does
* not unlock the second (JIRA issue JCR-284).
*/
public void testUnlockSameNameSibling() throws RepositoryException {
Session session = testRootNode.getSession();
// create two same-name sibling nodes
Node lockedNode2 = testRootNode.addNode(nodeName1);
lockedNode2.addMixin("mix:lockable");
session.save();
// lock both nodes
lockedNode2.lock(false, isSessionScoped());
try {
// assert: both nodes are locked
assertTrue("First node locked: ", lockedNode.isLocked());
assertTrue("Second node locked: ", lockedNode2.isLocked());
} catch (RepositoryException e) {
// make sure all locks are release again
lockedNode.unlock();
lockedNode2.unlock();
throw new RepositoryException(e);
}
try {
// unlock first sibling
lockedNode.unlock();
// assert: first node unlocked, second node still locked
assertFalse("First node unlocked: ", lockedNode.isLocked());
assertTrue("Second node locked: ", lockedNode2.isLocked());
} finally {
// make sure all locks are release again
lockedNode2.unlock();
}
}
/**
* If a locked nodes is unlocked again, any Lock instance retrieved by
* another session must change the lock-status. Similarly, the previously
* locked node must not be marked locked any more.
*/
public void testUnlockByOtherSession() throws RepositoryException {
Node ln2 = (Node) otherSession.getItem(lockedNode.getPath());
Lock l2 = ln2.getLock();
lockedNode.unlock();
assertFalse("Lock must be informed if Node is unlocked.", l2.isLive());
}
/**
* If a locked nodes is unlocked again, any Lock instance retrieved by
* another session must change the lock-status. Similarly, the previously
* locked node must not be marked locked any more.
*/
public void testUnlockByOtherSession2() throws RepositoryException {
Node ln2 = (Node) otherSession.getItem(lockedNode.getPath());
lockedNode.unlock();
assertFalse("Node is not locked any more", ln2.isLocked());
assertFalse("Node is not locked any more", ln2.holdsLock());
try {
ln2.getLock();
fail("Node is not locked any more");
} catch (LockException e) {
// OK
}
}
public void testRemoveLockedNode() throws RepositoryException {
Node n = (Node) otherSession.getItem(lockedNode.getPath());
// since removing a node is a modification of the non-locked parent
// the removal must succeed.
n.remove();
otherSession.save();
}
}