/*
* 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.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.jcr.AccessDeniedException;
import javax.jcr.Binary;
import javax.jcr.Credentials;
import javax.jcr.ImportUUIDBehavior;
import javax.jcr.InvalidItemStateException;
import javax.jcr.Item;
import javax.jcr.LoginException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.ValueFactory;
import javax.jcr.Workspace;
import javax.jcr.lock.LockException;
import javax.jcr.lock.LockManager;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NodeDefinitionTemplate;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.NodeTypeTemplate;
import javax.jcr.nodetype.PropertyDefinitionTemplate;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import javax.jcr.query.Query;
import javax.jcr.query.QueryResult;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;
import org.apache.jackrabbit.test.AbstractJCRTest;
import org.apache.jackrabbit.test.api.ShareableNodeTest;
import org.modeshape.common.FixFor;
import org.modeshape.jcr.api.JcrTools;
import junit.framework.Test;
/**
* Additional ModeShape tests that check for JCR compliance.
*/
public class ModeShapeTckTest extends AbstractJCRTest {
Session session;
private Map<String, Node> testAreasByWorkspace = new HashMap<String, Node>();
protected boolean print = false;
public ModeShapeTckTest( String testName ) {
super();
this.setName(testName);
this.isReadOnly = true;
this.print = false;
}
public static Test suite() {
return JcrTckSuites.someTestsInline(ModeShapeTckTest.class);
}
@Override
protected void tearDown() throws Exception {
try {
superuser.getRootNode().getNode(this.nodeName1).remove();
superuser.save();
} catch (PathNotFoundException ignore) {
}
if (session != null) {
session.logout();
session = null;
}
super.tearDown();
Credentials creds = getHelper().getSuperuserCredentials();
Repository repository = getHelper().getRepository();
for (Map.Entry<String, Node> entry : testAreasByWorkspace.entrySet()) {
Session session = repository.login(creds, entry.getKey());
try {
Node node = session.getNode(entry.getValue().getPath());
node.remove();
session.save();
} catch (RepositoryException e) {
// ignore ...
} finally {
session.logout();
}
}
}
protected Node getTestRoot( Session session ) throws Exception {
// Create a temporary area for the test ...
String workspaceName = session.getWorkspace().getName();
Node node = testAreasByWorkspace.get(workspaceName);
if (node == null) {
node = session.getRootNode().addNode("tmp");
testAreasByWorkspace.put(workspaceName, node);
} else {
// There is a node, but check which session it came from ...
if (node.getSession() != session) {
// Look it up in the supplied session ...
node = session.getRootNode().getNode("tmp");
}
}
return node;
}
private void testRead( Session session ) throws Exception {
Node rootNode = session.getRootNode();
for (NodeIterator iter = rootNode.getNodes(); iter.hasNext();) {
iter.nextNode();
}
}
private void testAddNode( Session session ) throws Exception {
session.refresh(false);
Node root = getTestRoot(session);
root.addNode(nodeName1, testNodeType);
session.save();
}
private void testRemoveNode( Session session ) throws Exception {
session.refresh(false);
Node root = getTestRoot(session);
Node node = root.getNode(nodeName1);
node.remove();
session.save();
}
private void testSetProperty( Session session ) throws Exception {
session.refresh(false);
Node root = getTestRoot(session);
root.setProperty(this.propertyName1, "test value");
session.save();
}
private void testRemoveProperty( Session session ) throws Exception {
Session localAdmin = getHelper().getRepository().login(getHelper().getSuperuserCredentials(),
session.getWorkspace().getName());
assertEquals(session.getWorkspace().getName(), superuser.getWorkspace().getName());
Node superRoot = localAdmin.getRootNode();
Node superNode;
try {
superNode = superRoot.getNode(this.nodeName1);
} catch (PathNotFoundException pnfe) {
superNode = superRoot.addNode(nodeName1, testNodeType);
}
superNode.setProperty(this.propertyName1, "test value");
localAdmin.save();
localAdmin.logout();
session.refresh(false);
Node root = session.getRootNode();
Node node = root.getNode(nodeName1);
Property property = node.getProperty(this.propertyName1);
property.remove();
session.save();
}
private void testRegisterNamespace( Session session ) throws Exception {
String unusedPrefix = session.getUserID();
session.getWorkspace().getNamespaceRegistry().registerNamespace(unusedPrefix, unusedPrefix);
session.getWorkspace().getNamespaceRegistry().unregisterNamespace(unusedPrefix);
}
private void testRegisterType( Session session ) throws Exception {
JcrNodeTypeManager nodeTypes = (JcrNodeTypeManager)session.getWorkspace().getNodeTypeManager();
NodeTypeTemplate newType = nodeTypes.createNodeTypeTemplate();
String nodeTypeName = session.getUserID() + "Type";
newType.setName(nodeTypeName);
nodeTypes.registerNodeType(newType, false);
nodeTypes.unregisterNodeTypes(Collections.singleton(nodeTypeName));
}
private void testWrite( Session session ) throws Exception {
testAddNode(session);
testSetProperty(session);
testRemoveProperty(session);
testRemoveNode(session);
}
private void testAdmin( Session session ) throws Exception {
testRegisterNamespace(session);
testRegisterType(session);
}
protected boolean useDeprecatedApi() {
return false;
}
@SuppressWarnings( "deprecation" )
protected VersionHistory versionHistory( Node node ) throws RepositoryException {
if (useDeprecatedApi()) return node.getVersionHistory();
return session.getWorkspace().getVersionManager().getVersionHistory(node.getPath());
}
@SuppressWarnings( "deprecation" )
protected Version baseVersion( Node node ) throws RepositoryException {
if (useDeprecatedApi()) return node.getBaseVersion();
return session.getWorkspace().getVersionManager().getBaseVersion(node.getPath());
}
@SuppressWarnings( "deprecation" )
protected Version checkin( Node node ) throws RepositoryException {
if (useDeprecatedApi()) return node.checkin();
return session.getWorkspace().getVersionManager().checkin(node.getPath());
}
@SuppressWarnings( "deprecation" )
protected void checkout( Node node ) throws RepositoryException {
if (useDeprecatedApi()) {
node.checkout();
} else {
session.getWorkspace().getVersionManager().checkout(node.getPath());
}
}
@SuppressWarnings( "deprecation" )
protected void restore( Node node,
Version version,
boolean removeExisting ) throws RepositoryException {
if (useDeprecatedApi()) {
node.restore(version, removeExisting);
} else {
session.getWorkspace().getVersionManager().restore(version, removeExisting);
}
}
@SuppressWarnings( "deprecation" )
protected void lock( Node node,
boolean isDeep,
boolean isSessionScoped ) throws RepositoryException {
if (useDeprecatedApi()) {
node.lock(isDeep, isSessionScoped);
} else {
session.getWorkspace().getLockManager().lock(node.getPath(), isDeep, isSessionScoped, 1L, "owner");
}
}
protected void print( String msg ) {
if (print) {
System.out.println(msg);
}
}
protected void printSubgraph( Node node ) throws RepositoryException {
if (print) {
JcrTools tools = new JcrTools();
tools.printSubgraph(node);
}
}
protected void printVersionHistory( Node node ) throws RepositoryException {
VersionHistory history = session.getWorkspace().getVersionManager().getVersionHistory(node.getPath());
printSubgraph(history);
}
/**
* Tests that read-only sessions can read nodes by loading all of the children of the root node
*
* @throws Exception
*/
public void testShouldAllowReadOnlySessionToRead() throws Exception {
session = getHelper().getReadOnlySession();
testRead(session);
}
/**
* Tests that read-only sessions cannot add nodes, remove nodes, set nodes, or set properties.
*
* @throws Exception
*/
public void testShouldNotAllowReadOnlySessionToWrite() throws Exception {
session = getHelper().getReadOnlySession();
try {
testAddNode(session);
fail("Read-only sessions should not be able to add nodes");
} catch (AccessDeniedException expected) {
}
try {
testSetProperty(session);
fail("Read-only sessions should not be able to set properties");
} catch (AccessDeniedException expected) {
}
try {
testRemoveProperty(session);
fail("Read-only sessions should not be able to remove properties");
} catch (AccessDeniedException expected) {
}
try {
testRemoveNode(session);
fail("Read-only sessions should not be able to remove nodes");
} catch (AccessDeniedException expected) {
}
}
/**
* Tests that read-only sessions cannot register namespaces or types
*
* @throws Exception
*/
public void testShouldNotAllowReadOnlySessionToAdmin() throws Exception {
session = getHelper().getReadOnlySession();
try {
testRegisterNamespace(session);
fail("Read-only sessions should not be able to register namespaces");
} catch (AccessDeniedException expected) {
}
try {
testRegisterType(session);
fail("Read-only sessions should not be able to register types");
} catch (AccessDeniedException expected) {
}
}
/**
* Tests that read-write sessions can read nodes by loading all of the children of the root node
*
* @throws Exception
*/
public void testShouldAllowReadWriteSessionToRead() throws Exception {
session = getHelper().getReadWriteSession();
testRead(session);
}
/**
* Tests that read-write sessions can add nodes, remove nodes, set nodes, and set properties.
*
* @throws Exception
*/
public void testShouldAllowReadWriteSessionToWrite() throws Exception {
session = getHelper().getReadWriteSession();
testWrite(session);
}
/**
* Tests that read-write sessions cannot register namespaces or types
*
* @throws Exception
*/
public void testShouldNotAllowReadWriteSessionToAdmin() throws Exception {
session = getHelper().getReadWriteSession();
try {
testRegisterNamespace(session);
fail("Read-write sessions should not be able to register namespaces");
} catch (AccessDeniedException expected) {
}
try {
testRegisterType(session);
fail("Read-write sessions should not be able to register types");
} catch (AccessDeniedException expected) {
}
}
/**
* Tests that admin sessions can read nodes by loading all of the children of the root node
*
* @throws Exception
*/
public void testShouldAllowAdminSessionToRead() throws Exception {
session = getHelper().getSuperuserSession();
testRead(session);
}
/**
* Tests that admin sessions can add nodes, remove nodes, set nodes, and set properties.
*
* @throws Exception
*/
public void testShouldAllowAdminSessionToWrite() throws Exception {
session = getHelper().getSuperuserSession();
testWrite(session);
}
/**
* Tests that admin sessions can register namespaces and types
*
* @throws Exception
*/
public void testShouldAllowAdminSessionToAdmin() throws Exception {
session = getHelper().getSuperuserSession();
testAdmin(session);
}
/**
* User defaultuser is configured to have readwrite in "otherWorkspace" and readonly in the default workspace. This test makes
* sure both work.
*
* @throws Exception
*/
public void testShouldMapReadRolesToWorkspacesWhenSpecified() throws Exception {
Credentials creds = new SimpleCredentials("defaultonly", "defaultonly".toCharArray());
session = getHelper().getRepository().login(creds);
testRead(session);
session.logout();
// If the repo only supports one workspace, stop here
if ("default".equals(this.workspaceName)) return;
session = getHelper().getRepository().login(creds, this.workspaceName);
testRead(session);
try {
testWrite(session);
fail("User 'defaultuser' should not have write access to '" + this.workspaceName + "'");
} catch (AccessDeniedException expected) {
}
session.logout();
}
/**
* User defaultuser is configured to have readwrite in "otherWorkspace" and readonly in the default workspace. This test makes
* sure both work.
*
* @throws Exception
*/
public void testShouldMapWriteRolesToWorkspacesWhenSpecified() throws Exception {
Credentials creds = new SimpleCredentials("defaultonly", "defaultonly".toCharArray());
session = getHelper().getRepository().login(creds);
testRead(session);
testWrite(session);
session.logout();
session = getHelper().getRepository().login(creds, "otherWorkspace");
testRead(session);
try {
testWrite(session);
fail("User 'defaultuser' should not have write access to 'otherWorkspace'");
} catch (AccessDeniedException expected) {
}
session.logout();
}
/**
* Users should not be able to see workspaces to which they don't at least have read access. User 'noaccess' has no access to
* the default workspace.
*
* @throws Exception
*/
public void testShouldNotSeeWorkspacesWithoutReadPermission() throws Exception {
Credentials creds = new SimpleCredentials("noaccess", "noaccess".toCharArray());
try {
session = getHelper().getRepository().login(creds);
fail("User 'noaccess' with no access to the default workspace should not be able to log into that workspace");
} catch (LoginException le) {
// Expected
}
// If the repo only supports one workspace, stop here
if ("default".equals(this.workspaceName)) return;
session = getHelper().getRepository().login(creds, this.workspaceName);
String[] workspaceNames = session.getWorkspace().getAccessibleWorkspaceNames();
assertThat(workspaceNames.length, is(1));
assertThat(workspaceNames[0], is(this.workspaceName));
session.logout();
}
public void testShouldCopyFromAnotherWorkspace() throws Exception {
session = getHelper().getSuperuserSession("otherWorkspace");
String nodetype1 = this.getProperty("nodetype");
Node node1 = session.getRootNode().addNode(nodeName1, nodetype1);
node1.addNode(nodeName2, nodetype1);
session.save();
session.logout();
superuser.getRootNode().addNode(nodeName4, nodetype1);
superuser.save();
superuser.getWorkspace().copy("otherWorkspace", "/" + nodeName1, "/" + nodeName4 + "/" + nodeName1);
Node node4 = superuser.getRootNode().getNode(nodeName4);
Node node4node1 = node4.getNode(nodeName1);
Node node4node1node2 = node4node1.getNode(nodeName2);
assertNotNull(node4node1node2);
}
/**
* A clone operation with removeExisting = true should fail if it would require removing an existing node that is a mandatory
* child node of some other parent (and not replacing it as part of the clone operation).
*
* @throws Exception if an error occurs
*/
public void testShouldNotCloneIfItWouldViolateTypeSemantics() throws Exception {
session = getHelper().getSuperuserSession("otherWorkspace");
assertThat(session.getWorkspace().getName(), is("otherWorkspace"));
String nodetype1 = this.getProperty("nodetype");
Node node1 = session.getRootNode().addNode("cloneSource", nodetype1);
// This node is not a mandatory child of nodetype1 (modetest:referenceableUnstructured)
node1.addNode("modetest:mandatoryChild", nodetype1);
session.save();
session.logout();
// /cloneTarget in the default workspace is type mode:referenceableUnstructured
superuser.getRootNode().addNode("cloneTarget", nodetype1);
// /node3 in the default workspace is type mode:referenceableUnstructured
superuser.getRootNode().addNode(nodeName3, nodetype1);
superuser.save();
// Throw the cloned items under cloneTarget
superuser.getWorkspace().clone("otherWorkspace", "/cloneSource", "/cloneTarget/cloneSource", false);
superuser.refresh(false);
Node node3 = (Node)superuser.getItem("/node3");
assertThat(node3.getNodes().getSize(), is(0L));
Node node4node1 = (Node)superuser.getItem("/cloneTarget/cloneSource");
assertThat(node4node1.getNodes().getSize(), is(1L));
// Now clone from the same source under node3 and remove the existing records
superuser.getWorkspace().clone("otherWorkspace", "/cloneSource", "/" + nodeName3 + "/cloneSource", true);
superuser.refresh(false);
Node node3node1 = (Node)superuser.getItem("/node3/cloneSource");
assertThat(node3node1.getNodes().getSize(), is(1L));
// Check that the nodes were indeed removed
Node node4 = (Node)superuser.getItem("/cloneTarget");
assertThat(node4.getNodes().getSize(), is(0L));
superuser.getRootNode().addNode("nodeWithMandatoryChild", "modetest:nodeWithMandatoryChild");
try {
superuser.save();
fail("A node with type modetest:nodeWithMandatoryChild should not be savable until the child is added");
} catch (ConstraintViolationException cve) {
// Expected
}
superuser.move("/node3/cloneSource/modetest:mandatoryChild", "/nodeWithMandatoryChild/modetest:mandatoryChild");
superuser.save();
superuser.refresh(false);
// Now clone from the same source under node3 and remove the existing records
try {
superuser.getWorkspace().clone("otherWorkspace", "/cloneSource", "/" + nodeName3 + "/cloneSource", true);
fail("Should not be able to use clone to remove the mandatory child node at /nodeWithMandatoryChild/modetest:mandatoryChild");
} catch (ConstraintViolationException cve) {
// expected
}
}
private void ensureExactlyOneVersionRoot( VersionManager vm,
String absPath ) throws Exception {
boolean foundRoot = false;
VersionHistory vh = vm.getVersionHistory(absPath);
VersionIterator vi = vh.getAllVersions();
while (vi.hasNext()) {
Version v = vi.nextVersion();
if ("jcr:rootVersion".equals(v.getName())) {
if (foundRoot) {
fail("Found multiple root versions of versionable node at " + absPath);
}
foundRoot = true;
}
}
if (!foundRoot) fail("No root version found for versionable node at " + absPath);
}
@FixFor( "MODE-1017" )
public void testShouldNotHaveTwoRootVersions() throws Exception {
Session session = superuser;
ValueFactory vf = session.getValueFactory();
Node root = session.getRootNode();
Node file = root.addNode("createfile.mode", "nt:file");
Node content = file.addNode("jcr:content", "nt:resource");
content.setProperty("jcr:data", vf.createBinary(new ByteArrayInputStream("Write 1".getBytes())));
session.save();
file.addMixin("mix:versionable");
// Per Section 15.1:
// "Under both simple and full versioning, on persist of a new versionable node N that neither corresponds
// nor shares with an existing node:
// - The jcr:isCheckedOut property of N is set to true and
// - A new VersionHistory (H) is created for N. H contains one Version, the root version (V0)
// (see §3.13.5.2 Root Version)."
//
// This means that the version history should not be created until save is performed. This makes sense,
// because otherwise the version history would be persisted for a newly-created node, even though that node
// is not yet persisted. Tests with the reference implementation (see sandbox) verified this behavior.
try {
session.getWorkspace().getVersionManager().getVersionHistory(file.getPath());
fail("It should not be possible to obtain the version history for a transient node.");
} catch (UnsupportedRepositoryOperationException e) {
// this exception is expected
}
session.save();
ensureExactlyOneVersionRoot(session.getWorkspace().getVersionManager(), file.getPath());
session.refresh(false);
ensureExactlyOneVersionRoot(session.getWorkspace().getVersionManager(), file.getPath());
}
public void testShouldFailToGetVersionHistoryOfTransientNode() throws Exception {
Session session = superuser;
VersionManager vm = session.getWorkspace().getVersionManager();
Node root = session.getRootNode();
Node file = root.addNode("createfile2.mode", "nt:file");
Node content = file.addNode("jcr:content", "nt:resource");
content.setProperty("jcr:data", vf.createBinary(new ByteArrayInputStream("Write 1".getBytes())));
// Do not save the node ...
// session.save();
// Now add the mixin transiently ...
file.addMixin("mix:versionable");
try {
vm.getVersionHistory(file.getPath());
fail("It should not be possible to obtain the version history for a transient node.");
} catch (InvalidItemStateException e) {
// expected
}
session.save();
ensureExactlyOneVersionRoot(vm, file.getPath());
}
public void testShouldFailToGetVersionHistoryOfPersistentNonVersionableNodeThatWasTransientlyMadeVersionable()
throws Exception {
Session session = superuser;
VersionManager vm = session.getWorkspace().getVersionManager();
Node root = session.getRootNode();
Node file = root.addNode("createfile3.mode", "nt:file");
Node content = file.addNode("jcr:content", "nt:resource");
content.setProperty("jcr:data", vf.createBinary(new ByteArrayInputStream("Write 1".getBytes())));
// Save the node ...
session.save();
// Now add the mixin transiently ...
file.addMixin("mix:versionable");
try {
vm.getVersionHistory(file.getPath());
fail("It should not be possible to obtain the version history for a persistent node that was newly-made versionable.");
} catch (UnsupportedRepositoryOperationException e) {
// expected
}
session.save();
ensureExactlyOneVersionRoot(vm, file.getPath());
}
public void testShouldNotHaveVersionHistoryForTransientVersionableNode() throws Exception {
Session session = superuser;
ValueFactory vf = session.getValueFactory();
Node root = session.getRootNode();
Node file = root.addNode("createfile.mode", "nt:file");
Node content = file.addNode("jcr:content", "nt:resource");
content.setProperty("jcr:data", vf.createBinary(new ByteArrayInputStream("Write 1".getBytes())));
file.addMixin("mix:versionable");
// Per Section 15.1:
// "Under both simple and full versioning, on persist of a new versionable node N that neither corresponds
// nor shares with an existing node:
// - The jcr:isCheckedOut property of N is set to true and
// - A new VersionHistory (H) is created for N. H contains one Version, the root version (V0)
// (see §3.13.5.2 Root Version)."
//
// This means that the version history should not be created until save is performed. This makes sense,
// because otherwise the version history would be persisted for a newly-created node, even though that node
// is not yet persisted. Tests with the reference implementation (see sandbox) verified this behavior.
try {
session.getWorkspace().getVersionManager().getVersionHistory(file.getPath());
fail("It should not be possible to obtain the version history for a transient node.");
} catch (RepositoryException e) {
// some repository exception is expected; the ref impl throws a RepositoryException
}
session.save();
ensureExactlyOneVersionRoot(session.getWorkspace().getVersionManager(), "/createfile.mode");
session.refresh(false);
ensureExactlyOneVersionRoot(session.getWorkspace().getVersionManager(), "/createfile.mode");
}
@FixFor( "MODE-1089" )
public void testShouldNotFailGettingVersionHistoryForNodeMadeVersionableSinceLastSave() throws Exception {
Session session1 = getHelper().getSuperuserSession();
VersionManager vm = session1.getWorkspace().getVersionManager();
// Create node structure
Node root = session1.getRootNode();
Node area = root.addNode("tmp2", "nt:unstructured");
Node outer = area.addNode("outerFolder");
Node inner = outer.addNode("innerFolder");
Node file = inner.addNode("testFile.dat");
file.setProperty("jcr:mimeType", "text/plain");
file.setProperty("jcr:data", "Original content");
session1.save();
file.addMixin("mix:versionable");
// session.save();
isVersionable(vm, file); // here's the problem
session1.save();
Version v1 = vm.checkin(file.getPath());
assertThat(v1, is(notNullValue()));
// System.out.println("Created version: " + v1);
// ubgraph(root.getNode("jcr:system/jcr:versionStorage"));
}
@SuppressWarnings( "deprecation" )
public void testShouldCreateProperVersionHistoryWhenSavingVersionedNode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = session.getRootNode().addNode("test", "nt:unstructured");
node.addMixin("mix:versionable");
session.save();
assertThat(node.hasProperty("jcr:isCheckedOut"), is(true));
assertThat(node.getProperty("jcr:isCheckedOut").getBoolean(), is(true));
assertThat(node.hasProperty("jcr:versionHistory"), is(true));
Node history = node.getProperty("jcr:versionHistory").getNode();
assertThat(history, is(notNullValue()));
assertThat(node.hasProperty("jcr:baseVersion"), is(true));
Node version = node.getProperty("jcr:baseVersion").getNode();
assertThat(version, is(notNullValue()));
assertThat(version.getParent(), is(history));
assertThat(node.hasProperty("jcr:uuid"), is(true));
assertThat(node.getProperty("jcr:uuid").getString(), is(history.getProperty("jcr:versionableUuid").getString()));
assertThat(versionHistory(node).getUUID(), is(history.getUUID()));
assertThat(versionHistory(node).getIdentifier(), is(history.getIdentifier()));
assertThat(versionHistory(node).getPath(), is(history.getPath()));
assertThat(baseVersion(node).getUUID(), is(version.getUUID()));
assertThat(baseVersion(node).getIdentifier(), is(version.getIdentifier()));
assertThat(baseVersion(node).getPath(), is(version.getPath()));
}
public void testShouldCreateProperStructureForPropertiesOnTheFirstCheckInOfANode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = session.getRootNode().addNode("/checkInTest", "modetest:versionTest");
session.save();
node.addMixin("mix:versionable");
session.save();
node.setProperty("abortProp", "abortPropValue");
node.setProperty("copyProp", "copyPropValue");
node.setProperty("ignoreProp", "ignorePropValue");
node.setProperty("versionProp", "versionPropValue");
session.save();
try {
checkin(node);
fail("Should not be able to checkin a node with a property that has an OnParentVersionAction of ABORT");
} catch (VersionException ve) {
assertThat(node.getProperty("jcr:isCheckedOut").getBoolean(), is(true));
}
node.setProperty("abortProp", (String)null);
session.save();
checkin(node);
assertThat(node.getProperty("jcr:isCheckedOut").getBoolean(), is(false));
Version version = baseVersion(node);
assertThat(version, is(notNullValue()));
assertThat(version.getProperty("jcr:frozenNode/copyProp").getString(), is("copyPropValue"));
assertThat(version.getProperty("jcr:frozenNode/versionProp").getString(), is("versionPropValue"));
try {
version.getProperty("jcr:frozenNode/ignoreProp");
fail("Frozen version should not record a property that has an OnParentVersionAction of IGNORE");
} catch (PathNotFoundException pnfe) {
// Expected
}
checkout(node);
node.setProperty("abortProp", "abortPropValueNew");
node.setProperty("copyProp", "copyPropValueNew");
node.setProperty("ignoreProp", "ignorePropValueNew");
node.setProperty("versionProp", "versionPropValueNew");
version = baseVersion(node);
assertThat(version, is(notNullValue()));
assertThat(version.getProperty("jcr:frozenNode/copyProp").getString(), is("copyPropValue"));
assertThat(version.getProperty("jcr:frozenNode/versionProp").getString(), is("versionPropValue"));
try {
version.getProperty("ignoreProp");
fail("Frozen version should not record a property that has an OnParentVersionAction of IGNORE");
} catch (PathNotFoundException pnfe) {
// Expected
}
session.save();
}
public void testShouldCreateProperHistoryForNodeWithCopySemantics() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/copyNode/copyNode/AbortNode with the first copyNode being versionable. This should be able
* to be checked in, as the ABORT status of abortNode is ignored when copyNode is checked in.
*/
Node copyNode = node.addNode("copyNode", "modetest:versionTest");
copyNode.addMixin("mix:versionable");
Node midLevel = copyNode.addNode("copyNode", "modetest:versionTest");
midLevel.setProperty("ignoreProp", "ignorePropValue");
midLevel.setProperty("copyProp", "copyPropValue");
Node abortNode = midLevel.addNode("abortNode", "modetest:versionTest");
abortNode.setProperty("ignoreProp", "ignorePropValue");
abortNode.setProperty("copyProp", "copyPropValue");
/*
* Create /checkinTest/copyNode/versionNode with versionNode being versionable as well. This should
* create a copy of versionNode in the version history, due to copyNode (the root of the checkin) having
* COPY semantics for the OnParentVersionAction.
*/
Node versionNode = copyNode.addNode("versionNode", "modetest:versionTest");
versionNode.addMixin("mix:versionable");
session.save();
Version version = checkin(copyNode);
Version version2 = checkin(versionNode);
assertThat(version.getProperty("jcr:frozenNode/versionNode/jcr:primaryType").getString(), is("nt:versionedChild"));
assertThat(version.getProperty("jcr:frozenNode/copyNode/abortNode/copyProp").getString(), is("copyPropValue"));
try {
version.getProperty("jcr:frozenNode/abortNode/ignoreProp");
fail("Property with OnParentVersionAction of IGNORE should not have been copied");
} catch (PathNotFoundException pnfe) {
// Expected
}
assertThat(version2.getProperty("jcr:frozenNode/jcr:primaryType").getString(), is("nt:frozenNode"));
assertThat(version2.getProperty("jcr:frozenNode/jcr:frozenPrimaryType").getString(), is("modetest:versionTest"));
assertThat(version2.getProperty("jcr:frozenNode/jcr:frozenUuid").getString(), is(versionNode.getIdentifier()));
}
public void testShouldThrowExceptionWhenVersioningChildNodeWithOnParentVersionSemanticsOfAbort() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/versionNode/abortNode with versionNode being versionable. This should fail
* when versionNode is checked in, as the OnParentVersionAction semantics come from the OPV of the child.
*/
Node versionNode = node.addNode("versionNode", "modetest:versionTest");
versionNode.addMixin("mix:versionable");
Node abortNode = versionNode.addNode("abortNode", "modetest:versionTest");
abortNode.setProperty("ignoreProp", "ignorePropValue");
abortNode.setProperty("copyProp", "copyPropValue");
session.save();
try {
checkin(versionNode);
fail("Child node with OnParentVersionAction of ABORT should have resulted in exception.");
} catch (VersionException ve) {
// Expected
}
}
public void testShouldCreateProperHistoryForVersionableChildOfNodeWithVersionSemantics() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/versionNode/copyNode with versionNode and copyNode being versionable. This should
* create a child of type nt:childVersionedNode under the frozen node.
*/
Node versionNode = node.addNode("versionNode", "modetest:versionTest");
versionNode.addMixin("mix:versionable");
Node copyNode = versionNode.addNode("copyNode", "modetest:versionTest");
copyNode.addMixin("mix:versionable");
copyNode.setProperty("ignoreProp", "ignorePropValue");
copyNode.setProperty("copyProp", "copyPropValue");
session.save();
Version version = checkin(versionNode);
assertThat(version.getProperty("jcr:frozenNode/copyNode/jcr:primaryType").getString(), is("nt:frozenNode"));
assertThat(version.getProperty("jcr:frozenNode/copyNode/jcr:frozenPrimaryType").getString(), is("modetest:versionTest"));
try {
version.getProperty("jcr:frozenNode/copyNode/copyProp");
} catch (PathNotFoundException pnfe) {
fail("Property should be copied to versionable child of versioned node, because copyNode was copied (see Section 3.13.9 Item 5 of JSR-283)");
}
try {
version.getProperty("jcr:frozenNode/copyNode/ignoreProp");
} catch (PathNotFoundException pnfe) {
fail("Ignored property should be copied to versionable child of versioned node when in a COPY subgraph");
}
}
public void testShouldRestorePropertiesOnVersionableNode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/copyNode with copyNode being versionable. This should be able
* to be checked in, as the ABORT status of abortNode is ignored when copyNode is checked in.
*/
Node copyNode = node.addNode("copyNode", "modetest:versionTest");
copyNode.addMixin("mix:versionable");
copyNode.setProperty("copyProp", "copyPropValue");
copyNode.setProperty("ignoreProp", "ignorePropValue");
copyNode.setProperty("computeProp", "computePropValue");
session.save();
Version version = checkin(copyNode);
/*
* Make some changes
*/
checkout(copyNode);
copyNode.addMixin("mix:lockable");
copyNode.setProperty("copyProp", "copyPropValueNew");
copyNode.setProperty("ignoreProp", "ignorePropValueNew");
copyNode.setProperty("versionProp", "versionPropValueNew");
copyNode.setProperty("computeProp", "computePropValueNew");
session.save();
checkin(copyNode);
restore(copyNode, version, false);
assertThat(copyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(copyNode.getProperty("ignoreProp").getString(), is("ignorePropValueNew"));
assertThat(copyNode.getProperty("computeProp").getString(), is("computePropValueNew"));
try {
copyNode.getProperty("versionProp");
fail("Property with OnParentVersionAction of VERSION added after version should be removed during restore");
} catch (PathNotFoundException pnfe) {
// Expected
}
}
@FixFor( "MODE-1228" )
public void testShouldRestoreWithNoReplaceTheNonReferenceableCopiedChildNode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/copyNode with copyNode being versionable. This should be able
* to be checked in, as the ABORT status of abortNode is ignored when copyNode is checked in.
*/
Node copyNode = node.addNode("copyNode", "modetest:versionTest");
copyNode.addMixin("mix:versionable");
copyNode.setProperty("copyProp", "copyPropValue");
copyNode.setProperty("ignoreProp", "ignorePropValue");
copyNode.setProperty("computeProp", "computePropValue");
Node belowCopyNode = copyNode.addNode("copyNode", "nt:unstructured");
belowCopyNode.addMixin("mix:title");
belowCopyNode.setProperty("copyProp", "copyPropValue");
belowCopyNode.setProperty("ignoreProp", "ignorePropValue");
belowCopyNode.setProperty("computeProp", "computePropValue");
belowCopyNode.setProperty("versionProp", "versionPropValue");
session.save();
assertThat(belowCopyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode.getMixinNodeTypes()[0].getName(), is("mix:title"));
Version version = checkin(copyNode);
/*
* Make some changes
*/
checkout(copyNode);
copyNode.addMixin("mix:lockable");
copyNode.setProperty("copyProp", "copyPropValueNew");
copyNode.setProperty("ignoreProp", "ignorePropValueNew");
copyNode.setProperty("versionProp", "versionPropValueNew");
copyNode.setProperty("computeProp", "computePropValueNew");
belowCopyNode.setProperty("versionProp", "versionPropValueNew");
belowCopyNode.setProperty("computeProp", "computePropValueNew");
session.save();
checkin(copyNode);
// printSubgraph(copyNode);
// printVersionHistory(copyNode);
restore(copyNode, version, false);
assertThat(copyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(copyNode.getProperty("ignoreProp").getString(), is("ignorePropValueNew"));
assertThat(copyNode.getProperty("computeProp").getString(), is("computePropValueNew"));
try {
copyNode.getProperty("versionProp");
fail("Property with OnParentVersionAction of VERSION added after version should be removed during restore");
} catch (PathNotFoundException pnfe) {
// Expected
}
// Note that in the case of properties on copied subnodes of a restored node, they replace the nodes
// in the workspace unless there is a node with the same identifier. See Section 15.7.6 for details.
Node belowCopyNode2 = copyNode.getNode("copyNode");
assertThat(belowCopyNode2.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode2.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode2.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode2.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode2.getMixinNodeTypes()[0].getName(), is("mix:title"));
}
@FixFor( "MODE-1228" )
public void testShouldRestoreWithReplaceTheNonReferenceableCopiedChildNode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/copyNode with copyNode being versionable. This should be able
* to be checked in, as the ABORT status of abortNode is ignored when copyNode is checked in.
*/
Node copyNode = node.addNode("copyNode", "modetest:versionTest");
copyNode.addMixin("mix:versionable");
copyNode.setProperty("copyProp", "copyPropValue");
copyNode.setProperty("ignoreProp", "ignorePropValue");
copyNode.setProperty("computeProp", "computePropValue");
Node belowCopyNode = copyNode.addNode("copyNode", "nt:unstructured");
belowCopyNode.addMixin("mix:title");
belowCopyNode.setProperty("copyProp", "copyPropValue");
belowCopyNode.setProperty("ignoreProp", "ignorePropValue");
belowCopyNode.setProperty("computeProp", "computePropValue");
belowCopyNode.setProperty("versionProp", "versionPropValue");
session.save();
assertThat(belowCopyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode.getMixinNodeTypes()[0].getName(), is("mix:title"));
Version version = checkin(copyNode);
/*
* Make some changes
*/
checkout(copyNode);
copyNode.addMixin("mix:lockable");
copyNode.setProperty("copyProp", "copyPropValueNew");
copyNode.setProperty("ignoreProp", "ignorePropValueNew");
copyNode.setProperty("versionProp", "versionPropValueNew");
copyNode.setProperty("computeProp", "computePropValueNew");
belowCopyNode.setProperty("versionProp", "versionPropValueNew");
belowCopyNode.setProperty("computeProp", "computePropValueNew");
session.save();
checkin(copyNode);
restore(copyNode, version, true);
assertThat(copyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(copyNode.getProperty("ignoreProp").getString(), is("ignorePropValueNew"));
assertThat(copyNode.getProperty("computeProp").getString(), is("computePropValueNew"));
try {
copyNode.getProperty("versionProp");
fail("Property with OnParentVersionAction of VERSION added after version should be removed during restore");
} catch (PathNotFoundException pnfe) {
// Expected
}
// Note that in the case of properties on copied subnodes of a restored node, they replace the nodes
// in the workspace unless there is a node with the same identifier. See Section 15.7.6 for details.
Node belowCopyNode2 = copyNode.getNode("copyNode");
assertThat(belowCopyNode2.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode2.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode2.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode2.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode2.getMixinNodeTypes()[0].getName(), is("mix:title"));
}
@FixFor( "MODE-1228" )
public void testShouldRestoreWithNoReplaceTheReferenceableCopiedChildNode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/copyNode with copyNode being versionable. This should be able
* to be checked in, as the ABORT status of abortNode is ignored when copyNode is checked in.
*/
Node copyNode = node.addNode("copyNode", "modetest:versionTest");
copyNode.addMixin("mix:versionable");
copyNode.setProperty("copyProp", "copyPropValue");
copyNode.setProperty("ignoreProp", "ignorePropValue");
copyNode.setProperty("computeProp", "computePropValue");
Node belowCopyNode = copyNode.addNode("copyNode", "nt:unstructured");
belowCopyNode.addMixin("mix:referenceable");
belowCopyNode.setProperty("copyProp", "copyPropValue");
belowCopyNode.setProperty("ignoreProp", "ignorePropValue");
belowCopyNode.setProperty("computeProp", "computePropValue");
belowCopyNode.setProperty("versionProp", "versionPropValue");
session.save();
assertThat(belowCopyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode.getMixinNodeTypes()[0].getName(), is("mix:referenceable"));
String belowCopyNodeId = belowCopyNode.getIdentifier();
Version version = checkin(copyNode);
// printSubgraph(version);
/*
* Make some changes
*/
checkout(copyNode);
copyNode.addMixin("mix:lockable");
copyNode.setProperty("copyProp", "copyPropValueNew");
copyNode.setProperty("ignoreProp", "ignorePropValueNew");
copyNode.setProperty("versionProp", "versionPropValueNew");
copyNode.setProperty("computeProp", "computePropValueNew");
belowCopyNode.setProperty("versionProp", "versionPropValueNew");
belowCopyNode.setProperty("computeProp", "computePropValueNew");
session.save();
checkin(copyNode);
// printVersionHistory(copyNode);
restore(copyNode, version, false);
assertThat(copyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(copyNode.getProperty("ignoreProp").getString(), is("ignorePropValueNew"));
assertThat(copyNode.getProperty("computeProp").getString(), is("computePropValueNew"));
try {
copyNode.getProperty("versionProp");
fail("Property with OnParentVersionAction of VERSION added after version should be removed during restore");
} catch (PathNotFoundException pnfe) {
// Expected
}
// Note that in the case of properties on copied subnodes of a restored node, they do NOT replace the
// referenceable nodes (with the same identifier) in the workspace. See Section 15.7.6 for details.
// Therefore, the properties should match the updated values...
Node belowCopyNode2 = copyNode.getNode("copyNode");
String belowCopyNode2Id = belowCopyNode.getIdentifier();
assertThat(belowCopyNodeId, is(belowCopyNode2Id));
assertThat(belowCopyNode2.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode2.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode2.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode2.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode2.getMixinNodeTypes()[0].getName(), is("mix:referenceable"));
}
@FixFor( "MODE-1228" )
public void testShouldRestoreWithReplaceTheReferenceableCopiedChildNode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "modetest:versionTest");
session.save();
/*
* Create /checkinTest/copyNode with copyNode being versionable. This should be able
* to be checked in, as the ABORT status of abortNode is ignored when copyNode is checked in.
*/
Node copyNode = node.addNode("copyNode", "modetest:versionTest");
copyNode.addMixin("mix:versionable");
copyNode.setProperty("copyProp", "copyPropValue");
copyNode.setProperty("ignoreProp", "ignorePropValue");
copyNode.setProperty("computeProp", "computePropValue");
Node belowCopyNode = copyNode.addNode("copyNode", "nt:unstructured");
belowCopyNode.addMixin("mix:referenceable");
belowCopyNode.setProperty("copyProp", "copyPropValue");
belowCopyNode.setProperty("ignoreProp", "ignorePropValue");
belowCopyNode.setProperty("computeProp", "computePropValue");
belowCopyNode.setProperty("versionProp", "versionPropValue");
session.save();
assertThat(belowCopyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode.getMixinNodeTypes()[0].getName(), is("mix:referenceable"));
String belowCopyNodeId = belowCopyNode.getIdentifier();
Version version = checkin(copyNode);
// printSubgraph(version);
/*
* Make some changes
*/
checkout(copyNode);
copyNode.addMixin("mix:lockable");
copyNode.setProperty("copyProp", "copyPropValueNew");
copyNode.setProperty("ignoreProp", "ignorePropValueNew");
copyNode.setProperty("versionProp", "versionPropValueNew");
copyNode.setProperty("computeProp", "computePropValueNew");
belowCopyNode.setProperty("versionProp", "versionPropValueNew");
belowCopyNode.setProperty("computeProp", "computePropValueNew");
session.save();
checkin(copyNode);
// printVersionHistory(copyNode);
restore(copyNode, version, true);
assertThat(copyNode.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(copyNode.getProperty("ignoreProp").getString(), is("ignorePropValueNew"));
assertThat(copyNode.getProperty("computeProp").getString(), is("computePropValueNew"));
try {
copyNode.getProperty("versionProp");
fail("Property with OnParentVersionAction of VERSION added after version should be removed during restore");
} catch (PathNotFoundException pnfe) {
// Expected
}
// Note that in the case of properties on copied subnodes of a restored node, they replace the nodes
// in the workspace unless there is a node with the same identifier. See Section 15.7.6 for details.
Node belowCopyNode2 = copyNode.getNode("copyNode");
String belowCopyNode2Id = belowCopyNode.getIdentifier();
assertThat(belowCopyNodeId, is(belowCopyNode2Id));
assertThat(belowCopyNode2.getProperty("copyProp").getString(), is("copyPropValue"));
assertThat(belowCopyNode2.getProperty("ignoreProp").getString(), is("ignorePropValue"));
assertThat(belowCopyNode2.getProperty("computeProp").getString(), is("computePropValue"));
assertThat(belowCopyNode2.getProperty("versionProp").getString(), is("versionPropValue"));
assertThat(belowCopyNode2.getMixinNodeTypes()[0].getName(), is("mix:referenceable"));
}
@FixFor( "MODE-693" )
public void testShouldAllowDeletingNodesWhenLargePropertyIsPresent() throws Exception {
session = getHelper().getReadWriteSession();
Node root = session.getRootNode();
final int SIZE = 2048;
byte[] largeArray = new byte[SIZE];
for (int i = 0; i < SIZE; i++) {
largeArray[i] = (byte)'x';
}
Node projectNode = root.addNode("mode693", "nt:unstructured");
Node fileNode = projectNode.addNode("fileNode", "nt:file");
Node contentNode = fileNode.addNode("jcr:content", "nt:resource");
Binary binaryValue = session.getValueFactory().createBinary(new ByteArrayInputStream(largeArray));
contentNode.setProperty("jcr:data", binaryValue);
contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
contentNode.setProperty("jcr:mimeType", "application/octet-stream");
Node otherNode = projectNode.addNode("otherNode", "nt:unstructured");
session.save();
String pathToNode = projectNode.getName() + "/" + otherNode.getName();
if (!root.hasNode(pathToNode)) {
throw new RepositoryException("Cannot delete node at path=" + pathToNode + " since no node exists at this path");
}
Node nodeToDelete = root.getNode(pathToNode);
nodeToDelete.remove();
session.save();
// Make sure that only one node was deleted
assertThat(root.hasNode(projectNode.getName()), is(true));
assertThat(projectNode.hasNode(fileNode.getName()), is(true));
assertThat(fileNode.hasNode(contentNode.getName()), is(true));
assertThat(root.hasNode(pathToNode), is(false));
}
@FixFor( "MODE-704" )
public void testShouldReturnSilentlyWhenCheckingOutACheckedOutNode() throws Exception {
session = getHelper().getReadWriteSession();
Node root = session.getRootNode();
Node testNode = root.addNode("checkedOutNodeTest", "nt:unstructured");
session.save();
// Add the mixin, but don't save it
testNode.addMixin("mix:versionable");
checkout(testNode);
session.save();
// Now check that it still returns silently on a saved node that was never checked in.
checkout(testNode);
}
@FixFor( "MODE-709" )
public void testShouldCreateVersionStorageForWhenVersionableNodesCopied() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node parentNode = root.addNode("versionableNodeForCopy", "nt:unstructured");
parentNode.addMixin("mix:versionable");
Node childNode = parentNode.addNode("versionableChild", "nt:unstructured");
childNode.addMixin("mix:versionable");
Node targetNode = root.addNode("destForCopy", "nt:unstructured");
session.save();
String newParentPath = targetNode.getPath() + "/" + parentNode.getName();
session.getWorkspace().copy(parentNode.getPath(), newParentPath);
parentNode = (Node)session.getItem(newParentPath);
childNode = parentNode.getNode("versionableChild");
checkout(parentNode);
checkin(parentNode);
checkout(childNode);
checkin(childNode);
}
@FixFor( "MODE-1822" )
public void testShouldBeAbleToVersionWithinUserTransactionAndJBossTransactionManager() throws Exception {
session = getHelper().getReadWriteSession();
VersionManager vm = session.getWorkspace().getVersionManager();
Node node = session.getRootNode().addNode("Test3");
node.addMixin("mix:versionable");
node.setProperty("name", "lalalal");
node.setProperty("code", "lalalal");
session.save();
vm.checkin(node.getPath());
print("Checked in " + node.getPath());
for (int i = 0; i != 2; ++i) {
// Check it back out before we commit ...
node = session.getRootNode().getNode("Test3");
print("Checking out " + node.getPath());
vm.checkout(node.getPath());
// Make some more changes ...
node.setProperty("code", "fa-lalalal");
print("Saving changes to " + node.getPath());
session.save();
// Check it back in ...
print("Checking in " + node.getPath());
vm.checkin(node.getPath());
}
}
@FixFor( "MODE-720" )
@SuppressWarnings( "unchecked" )
public void testShouldBeAbleToReferToUnsavedReferenceNode() throws Exception {
session = getHelper().getSuperuserSession();
JcrNodeTypeManager nodeTypes = (JcrNodeTypeManager)session.getWorkspace().getNodeTypeManager();
/*
* Register a one-off node type with a reference property that has a constraint on it
*/
NodeTypeTemplate ntt = nodeTypes.createNodeTypeTemplate();
ntt.setName("modetest:constrainedPropType");
PropertyDefinitionTemplate pdt = nodeTypes.createPropertyDefinitionTemplate();
pdt.setName("modetest:constrainedProp");
pdt.setRequiredType(PropertyType.REFERENCE);
pdt.setValueConstraints(new String[] {"nt:unstructured"});
ntt.getPropertyDefinitionTemplates().add(pdt);
nodeTypes.registerNodeType(ntt, false);
/*
* Add a node that would satisfy the constraint
*/
Node root = session.getRootNode();
Node parentNode = root.addNode("constrainedNodeTest", "nt:unstructured");
Node targetNode = parentNode.addNode("target", "nt:unstructured");
targetNode.addMixin("mix:referenceable");
/*
* Now add a node with the one-off type.
*/
Node referringNode = parentNode.addNode("referer", "modetest:constrainedPropType");
referringNode.setProperty("modetest:constrainedProp", targetNode);
session.save();
}
@FixFor( "MODE-1092" )
@SuppressWarnings( "unchecked" )
public void testShouldBeAbleToSetWeakReferences() throws Exception {
session = getHelper().getSuperuserSession();
JcrNodeTypeManager nodeTypes = (JcrNodeTypeManager)session.getWorkspace().getNodeTypeManager();
/*
* Register a one-off node type with a reference property that has a constraint on it
*/
NodeTypeTemplate ntt = nodeTypes.createNodeTypeTemplate();
ntt.setName("modetest:typeWithWeakReference");
PropertyDefinitionTemplate pdt = nodeTypes.createPropertyDefinitionTemplate();
pdt.setName("modetest:weakRefProp");
pdt.setRequiredType(PropertyType.WEAKREFERENCE);
pdt.setValueConstraints(new String[] {"nt:unstructured"});
ntt.getPropertyDefinitionTemplates().add(pdt);
nodeTypes.registerNodeType(ntt, false);
/*
* Add a node that would satisfy the constraint
*/
Node root = session.getRootNode();
Node parentNode = root.addNode("weakReferenceTest", "nt:unstructured");
Node targetNode = parentNode.addNode("target", "nt:unstructured");
targetNode.addMixin("mix:referenceable");
/*
* Now add a node with the one-off type.
*/
Node referringNode = parentNode.addNode("referer", "modetest:typeWithWeakReference");
referringNode.setProperty("modetest:weakRefProp", targetNode);
session.save();
// Now fetch the referring node again, and verify that the reference is still a weak reference
Node referringNode2 = session.getNode("/weakReferenceTest/referer");
Property property = referringNode2.getProperty("modetest:weakRefProp");
assertThat(property.getType(), is(PropertyType.WEAKREFERENCE));
Node referredNode = property.getNode();
assertThat(referredNode, is(targetNode));
}
@FixFor( "MODE-701" )
public void testShouldBeAbleToImportAutocreatedChildNodeWithoutDuplication() throws Exception {
session = getHelper().getSuperuserSession();
/*
* Add a node that would satisfy the constraint
*/
Node root = getTestRoot(session);
Node parentNode = root.addNode("autocreatedChildRoot", "nt:unstructured");
session.save();
Node targetNode = parentNode.addNode("nodeWithAutocreatedChild", "modetest:nodeWithAutocreatedChild");
assertThat(targetNode.getNode("modetest:autocreatedChild"), is(notNullValue()));
// Don't save this yet
session.refresh(false);
InputStream in = getClass().getResourceAsStream("/io/autocreated-node-test.xml");
session.importXML(root.getPath() + "/autocreatedChildRoot", in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
}
public void testShouldAllowCheckoutAfterMove() throws Exception {
// q.v., MODE-???
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node sourceNode = root.addNode("versionableSource", "nt:unstructured");
sourceNode.addMixin("mix:versionable");
Node targetNode = root.addNode("versionableTarget", "nt:unstructured");
session.save();
String sourceName = sourceNode.getName();
session.move(sourceNode.getPath(), targetNode.getPath() + "/" + sourceName);
sourceNode = targetNode.getNode(sourceName);
checkout(sourceNode);
}
@SuppressWarnings( "deprecation" )
public void testAdminUserCanBreakOthersLocksUsingDeprecatedNodeLockAndUnlockMethods() throws Exception {
String lockNodeName = "lockTestNode";
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node lockNode = root.addNode(lockNodeName);
lockNode.addMixin("mix:lockable");
session.save();
lockNode.lock(false, false);
assertThat(lockNode.isLocked(), is(true));
Session superuser = getHelper().getSuperuserSession();
root = getTestRoot(superuser);
lockNode = root.getNode(lockNodeName);
assertThat(lockNode.isLocked(), is(true));
lockNode.unlock();
assertThat(lockNode.isLocked(), is(false));
superuser.logout();
}
public void testAdminUserCanBreakOthersLocks() throws Exception {
String lockNodeName = "lockTestNode2";
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node lockNode = root.addNode(lockNodeName);
lockNode.addMixin("mix:lockable");
session.save();
session.getWorkspace().getLockManager().lock(lockNode.getPath(), false, false, 1L, "me");
assertThat(lockNode.isLocked(), is(true));
Session superuser = getHelper().getSuperuserSession();
root = getTestRoot(superuser);
lockNode = root.getNode(lockNodeName);
assertThat(lockNode.isLocked(), is(true));
session.getWorkspace().getLockManager().unlock(lockNode.getPath());
assertThat(lockNode.isLocked(), is(false));
superuser.logout();
}
public void testShouldNotAllowLockedNodeToBeRemoved() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node parentNode = root.addNode("lockedParent");
parentNode.addMixin("mix:lockable");
Node targetNode = parentNode.addNode("lockedTarget");
session.save();
lock(parentNode, true, true);
Session session2 = getHelper().getReadWriteSession();
Node targetNode2 = (Node)session2.getItem(root.getPath() + "/lockedParent/lockedTarget");
try {
targetNode2.remove();
fail("Locked nodes should not be able to be removed");
} catch (LockException le) {
// Success
}
targetNode.remove();
session.save();
}
public void testShouldNotAllowPropertyOfLockedNodeToBeRemoved() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node parentNode = root.addNode("lockedPropParent");
parentNode.addMixin("mix:lockable");
Node targetNode = parentNode.addNode("lockedTarget");
targetNode.setProperty("foo", "bar");
session.save();
lock(parentNode, true, true);
Session session2 = getHelper().getReadWriteSession();
Property targetProp2 = (Property)session2.getItem(root.getPath() + "/lockedPropParent/lockedTarget/foo");
try {
targetProp2.remove();
fail("Properties of locked nodes should not be able to be removed");
} catch (LockException le) {
// Success
}
targetNode.getProperty("foo").remove();
session.save();
}
public void testShouldNotAllowCheckedInNodeToBeRemoved() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node parentNode = root.addNode("checkedInParent");
parentNode.addMixin("mix:versionable");
Node targetNode = parentNode.addNode("checkedInTarget");
session.save();
checkin(parentNode);
try {
targetNode.remove();
fail("Checked in nodes should not be able to be removed");
} catch (VersionException ve) {
// Success
}
checkout(parentNode);
targetNode.remove();
session.save();
}
public void testShouldNotAllowPropertyOfCheckedInNodeToBeRemoved() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node parentNode = root.addNode("checkedInPropParent");
parentNode.addMixin("mix:versionable");
Node targetNode = parentNode.addNode("checkedInTarget");
Property targetProp = targetNode.setProperty("foo", "bar");
session.save();
checkin(parentNode);
try {
targetProp.remove();
fail("Properties of checked in nodes should not be able to be removed");
} catch (VersionException ve) {
// Success
}
checkout(parentNode);
targetProp.remove();
session.save();
}
public void testGetPathOnRemovedNodeShouldThrowException() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node parentNode = root.addNode("invalidItemStateTest");
session.save();
parentNode.remove();
try {
parentNode.getPath();
fail("getPath on removed node should throw InvalidItemStateException per section 7.1.3.3 of 1.0.1 spec");
} catch (InvalidItemStateException iise) {
// Success
}
}
@FixFor( "MODE-792" )
public void testCheckingOutAnAlreadyCheckedOutNodeShouldHaveNoEffect() throws Exception {
session = getHelper().getReadWriteSession();
VersionManager versionManager = session.getWorkspace().getVersionManager();
Node root = getTestRoot(session);
Node parentNode = root.addNode("checkedOutNodeNopTest");
parentNode.addMixin("mix:versionable");
session.save();
versionManager.checkin(parentNode.getPath());
versionManager.checkout(parentNode.getPath());
parentNode.setProperty("foo", "bar");
versionManager.checkout(parentNode.getPath());
assertEquals(parentNode.getProperty("foo").getString(), "bar");
}
@FixFor( "MODE-793" )
public void testPropertyCardinalityShouldPropagateToFrozenNode() throws Exception {
session = getHelper().getReadWriteSession();
VersionManager versionManager = session.getWorkspace().getVersionManager();
Node root = getTestRoot(session);
Node parentNode = root.addNode("checkedOutNodeNopTest");
parentNode.addMixin("mix:versionable");
parentNode.setProperty("foo", new String[] {"bar", "baz"});
session.save();
assertEquals(true, parentNode.getProperty("foo").getDefinition().isMultiple());
Version version = versionManager.checkin(parentNode.getPath());
Node frozenNode = version.getFrozenNode();
assertEquals(true, frozenNode.getProperty("foo").getDefinition().isMultiple());
}
@FixFor( "MODE-799" )
public void testNodeWithoutETagMixinShouldNotHaveETagProperty() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
root.addNode("someNewNode");
session.save();
assertThat(root.getNode("someNewNode").hasProperty("jcr:etag"), is(false));
}
@FixFor( "MODE-799" )
public void testAutomaticCreationUponSaveOfETagPropertyWhenETagMixinIsAddedToNodeWithoutBinaryProperties() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node newNode = root.addNode("someNewNode4");
newNode.addMixin("mix:etag");
session.save();
String etagValue = root.getNode("someNewNode4").getProperty("jcr:etag").getString();
assertThat(etagValue, is(""));
}
@FixFor( "MODE-799" )
public void testAutomaticCreationUponSaveOfETagPropertyWhenETagMixinIsAddedToNodeWithExistingBinaryProperties()
throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node newNode = root.addNode("someNewNode2");
Binary binary = session.getValueFactory().createBinary(new ByteArrayInputStream("This is the value".getBytes()));
newNode.setProperty("binaryProperty", binary);
session.save();
root.getNode("someNewNode2").addMixin("mix:etag");
session.save();
String etagValue = root.getNode("someNewNode2").getProperty("jcr:etag").getString();
assertThat(etagValue.trim().length() != 0, is(true));
}
@FixFor( "MODE-799" )
public void testAutomaticCreationUponSaveOfETagPropertyWhenNodeWithETagMixinHasNewBinaryProperty() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node newNode = root.addNode("someNewNode3");
newNode.addMixin("mix:etag");
session.save();
Binary binary = session.getValueFactory().createBinary(new ByteArrayInputStream("This is the value".getBytes()));
root.getNode("someNewNode3").setProperty("binaryProperty", binary);
session.save();
String etagValue = root.getNode("someNewNode3").getProperty("jcr:etag").getString();
assertThat(etagValue.trim().length() != 0, is(true));
}
@FixFor( "MODE-796" )
public void testNodeReferenceRemainsValidAfterSave() throws Exception {
session = getHelper().getReadWriteSession();
Node root = getTestRoot(session);
Node nodeA = root.addNode("nodeA", "nt:unstructured");
nodeA.setProperty("foo", "bar A");
Node nodeB = root.addNode("nodeB", "nt:unstructured");
nodeB.setProperty("foo", "bar B");
session.save();
// Verify that the node references still have a path ...
assertThat(nodeA.getPath(), is(root.getPath() + "/nodeA"));
assertThat(nodeB.getPath(), is(root.getPath() + "/nodeB"));
// Move 'nodeA' under 'nodeB' ...
session.move(nodeA.getPath(), root.getPath() + "/nodeB/nodeA");
assertThat(nodeA.getPath(), is(root.getPath() + "/nodeB/nodeA"));
assertThat(nodeB.getPath(), is(root.getPath() + "/nodeB"));
session.save();
assertThat(nodeA.getPath(), is(root.getPath() + "/nodeB/nodeA"));
assertThat(nodeB.getPath(), is(root.getPath() + "/nodeB"));
}
@FixFor( "MODE-956" )
public void testShouldBeAbleToSetNonexistingPropertyToNull() throws Exception {
Node rootNode = getTestRoot(superuser);
Node child = rootNode.addNode("child", "nt:unstructured");
rootNode.getSession().save();
child.setProperty("foo", (Calendar)null);
}
@FixFor( "MODE-1005" )
public void testShouldThrowRepositoryExceptionForRelativePathsInSessionGetNode() throws Exception {
try {
Node root = getTestRoot(superuser);
root.addNode("nodeForRelativePathTest", "nt:unstructured");
superuser.getNode("nodeForRelativePathTest");
fail("Should throw RepositoryException when attempting to call Session.getNode(String) with a relative path");
} catch (RepositoryException re) {
// Expected
}
}
@FixFor( "MODE-1005" )
public void testShouldThrowRepositoryExceptionForRelativePathsInSessionGetProperty() throws Exception {
try {
Node root = getTestRoot(superuser);
root.addNode("propertyNodeForRelativePathTest", "nt:unstructured");
superuser.getProperty("propertyNodeForRelativePathTest/jcr:primaryType");
fail("Should throw RepositoryException when attempting to call Session.getProperty(String) with a relative path");
} catch (RepositoryException re) {
// Expected
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyShallowLockPreventsOtherSessionsFromChangingPropertiesOnLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// subgraph(root1);
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), false, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toChange = session2.getNode(node2.getPath());
try {
toChange.addMixin("mix:versionable");
fail("Expected to see LockException");
} catch (LockException e) {
// expected
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyShallowLockAllowsOtherSessionsToDeleteLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), false, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toDelete = session2.getNode(node2.getPath());
try {
// This is possible because removing node2 is an alteration of node1, upon which there is no lock
toDelete.remove();
session2.save();
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyShallowLockAllowsSameSessionToDeleteLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), false, false, 10000, "Locked");
session1.save();
// Attempt to a child node of node2
try {
node3.remove();
} finally {
tmp.remove();
session1.save();
session1.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyShallowLockPreventsOtherSessionsFromDeletingChildOfLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), false, false, 10000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toDelete = session2.getNode(node3.getPath());
try {
toDelete.remove();
fail("Expected to see LockException");
} catch (LockException e) {
// expected
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyShallowLockAllowsSameSessionToDeleteChildOfLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), false, false, 10000, "Locked");
session1.save();
// Attempt to a child node of node2
try {
node3.remove();
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyShallowLockAllowOtherSessionsToChangePropertiesOfChildOfLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), false, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toChange = session2.getNode(node3.getPath());
try {
toChange.addMixin("mix:referenceable");
session2.save();
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyShallowLockAllowsSameSessionToChangeChildProperties() throws Exception {
Session session1 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:unstructured");
Node node1 = tmp.addNode("node1", "nt:unstructured");
Node node2 = tmp.addNode("node1/node2", "nt:unstructured");
Node node3 = tmp.addNode("node1/node2/node3", "nt:unstructured");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), false, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
Node toChange = node2;
try {
toChange.setProperty("newProp", "newValue");
session1.save();
} finally {
tmp.remove();
session1.save();
session1.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyAddingMixinAndSavingRequiredBeforeLockingNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
try {
node2.addMixin("mix:lockable");
lm1.lock(node2.getPath(), false, false, 10000, "Locked");
session1.save();
} catch (InvalidItemStateException e) {
// expected??, since the lock manager is owned by the workspace
} finally {
tmp.remove();
session1.save();
session1.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyDeepLockPreventsOtherSessionsFromChangingPropertiesOnLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), true, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toChange = session2.getNode(node2.getPath());
try {
toChange.addMixin("mix:versionable");
fail("Expected to see LockException");
} catch (LockException e) {
// expected
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyDeepLockAllowsOtherSessionsToDeleteLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), true, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toDelete = session2.getNode(node2.getPath());
try {
// This is possible because removing node2 is an alteration of node1, upon which there is no lock
toDelete.remove();
session2.save();
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyDeepLockAllowsSameSessionToDeleteLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), true, false, 10000, "Locked");
session1.save();
// Attempt to a child node of node2
try {
node3.remove();
} finally {
tmp.remove();
session1.save();
session1.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyDeepLockPreventsOtherSessionsFromDeletingChildOfLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), true, false, 10000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toDelete = session2.getNode(node3.getPath());
try {
toDelete.remove();
fail("Expected to see LockException");
} catch (LockException e) {
// expected
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyDeepLockAllowsSameSessionToDeleteChildOfLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), true, false, 10000, "Locked");
session1.save();
// Attempt to a child node of node2
try {
node3.remove();
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyDeepLockPreventsOtherSessionsFromChangePropertiesOfChildOfLockedNode() throws Exception {
Session session1 = getHelper().getSuperuserSession();
Session session2 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:folder");
Node node1 = tmp.addNode("node1", "nt:folder");
Node node2 = tmp.addNode("node1/node2", "nt:folder");
Node node3 = tmp.addNode("node1/node2/node3", "nt:folder");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), true, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
session2.refresh(true);
Node toChange = session2.getNode(node3.getPath());
try {
toChange.addMixin("mix:referenceable");
session2.save();
fail("Expected to see LockException");
} catch (LockException e) {
// expected
} finally {
tmp.remove();
session1.save();
session1.logout();
session2.logout();
}
}
@FixFor( "MODE-1040" )
@SuppressWarnings( "unused" )
public void testShouldVerifyDeepLockAllowsSameSessionToChangeChildProperties() throws Exception {
Session session1 = getHelper().getSuperuserSession();
LockManager lm1 = session1.getWorkspace().getLockManager();
// Create node structure
Node root1 = getTestRoot(session1);
Node tmp = root1.addNode("tmp", "nt:unstructured");
Node node1 = tmp.addNode("node1", "nt:unstructured");
Node node2 = tmp.addNode("node1/node2", "nt:unstructured");
Node node3 = tmp.addNode("node1/node2/node3", "nt:unstructured");
session1.save();
// Create an open-scoped shallow lock on node2
node2.addMixin("mix:lockable");
session1.save();
lm1.lock(node2.getPath(), true, false, 100000, "Locked");
session1.save();
// Attempt to a child node of node2
Node toChange = node2;
try {
toChange.setProperty("newProp", "newValue");
session1.save();
} finally {
tmp.remove();
session1.save();
session1.logout();
}
}
public void testShouldVerifyNtFileNodesHavePrimaryItem() throws Exception {
Session session1 = getHelper().getSuperuserSession();
// Create node structure
Node root1 = getTestRoot(session1);
Node folder1 = root1.addNode("folder1", "nt:folder");
String fileName = "simple.json";
String filePath = folder1.getPath() + "/" + fileName;
new JcrTools().uploadFile(session1, filePath, getClass().getResourceAsStream("/data/" + fileName));
session1.save();
// Find the primary item ...
Node file1 = folder1.getNode(fileName);
Node content = file1.getNode("jcr:content");
assertNotNull(file1);
Item primary = file1.getPrimaryItem();
assertThat(primary, is(sameInstance((Item)content)));
// Change the primary type of the "jcr:content" node to "nt:unstructured" ...
content.setPrimaryType("nt:unstructured");
session1.save();
// Find the primary item (again) ...
Node content2 = file1.getNode("jcr:content");
assertNotNull(file1);
Item primary2 = file1.getPrimaryItem();
assertThat(primary2, is(sameInstance((Item)content2)));
}
@FixFor( "MODE-1696" )
public void testShouldVerifyNtResourceNodesHavePrimaryItem() throws Exception {
Session session1 = getHelper().getSuperuserSession();
// Create node structure
Node root1 = getTestRoot(session1);
Node resource = root1.addNode("resource", "nt:resource");
Binary binary = session1.getValueFactory().createBinary(getClass().getResourceAsStream("/data/simple.json"));
resource.setProperty("jcr:data", binary);
session1.save();
// Find the primary item ...
resource = root1.getNode("resource");
Item primary = resource.getPrimaryItem();
assertNotNull(primary);
assertThat(resource.getProperty("jcr:data"), is(sameInstance(primary)));
}
boolean isVersionable( VersionManager vm,
Node node ) throws RepositoryException {
try {
vm.getVersionHistory(node.getPath());
return true;
} catch (UnsupportedRepositoryOperationException e) {
return false;
}
}
protected void checkinRecursively( Node node,
VersionManager vm ) throws RepositoryException {
String path = node.getPath();
if (node.isNodeType("mix:versionable") && vm.isCheckedOut(path)) {
try {
vm.checkin(path);
} catch (VersionException e) {
// Something on this node can't be checked in, so remove it ..
node.remove();
return;
}
}
NodeIterator iter = node.getNodes();
while (iter.hasNext()) {
checkinRecursively(iter.nextNode(), vm);
}
}
@FixFor( "MODE-1234" )
public void testShouldFindCheckedOutNodesByQuerying() throws Exception {
Session session1 = getHelper().getSuperuserSession();
VersionManager vm = session1.getWorkspace().getVersionManager();
// Check in all nodes that are still checked out ...
Node root = session1.getRootNode();
checkinRecursively(root, vm);
// Create node structure
Node area = root.addNode("tmp2", "nt:unstructured");
Node outer = area.addNode("outerFolder");
Node inner = outer.addNode("innerFolder");
Node file = inner.addNode("testFile.dat");
file.setProperty("jcr:mimeType", "text/plain");
file.setProperty("jcr:data", "Original content");
session1.save();
file.addMixin("mix:versionable");
session1.save();
assertTrue(isVersionable(vm, file));
Version v1 = vm.checkin(file.getPath());
assertThat(v1, is(notNullValue()));
// Register a listener so that we only have to wait until the change is made ...
CountDownListener listener = registerCountDownListener(session1, file.getPath(), 1);
// Query for versionables ...
queryForCheckedOutVersionables(session1, 0, false);
// Now check out a node ...
vm.checkout(file.getPath());
// and wait a bit ...
listener.await(2, TimeUnit.SECONDS);
listener.unregister();
// And re-query ...
queryForCheckedOutVersionables(session1, 1, false);
// Register a listener so that we only have to wait until the change is made ...
listener = registerCountDownListener(session1, file.getPath(), 1);
// Check it back in ...
vm.checkin(file.getPath());
// and wait a bit ...
listener.await(2, TimeUnit.SECONDS);
listener.unregister();
// And re-query ...
queryForCheckedOutVersionables(session1, 0, false);
}
protected int queryForCheckedOutVersionables( Session session,
int expected,
boolean print ) throws RepositoryException {
String queryStr = "SELECT * FROM [mix:versionable] AS versionable WHERE versionable.[jcr:isCheckedOut] = true";
QueryResult result = query(session, queryStr);
int actual = (int)result.getRows().getSize();
if (print) {
System.out.println("Result = \n" + result);
}
assertThat(actual, is(expected));
return actual;
}
protected QueryResult query( Session session,
String queryStr ) throws RepositoryException {
Query query = session.getWorkspace().getQueryManager().createQuery(queryStr, Query.JCR_SQL2);
QueryResult result = query.execute();
return result;
}
protected CountDownListener registerCountDownListener( Session session,
String path,
int counts ) throws RepositoryException {
CountDownListener listener = new CountDownListener(session, counts);
session.getWorkspace().getObservationManager()
.addEventListener(listener, Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, path, true, null, null, false);
return listener;
}
protected static class CountDownListener implements EventListener {
final CountDownLatch latch;
final Session session;
public CountDownListener( Session session,
int counts ) {
this.session = session;
this.latch = new CountDownLatch(counts);
}
@Override
public void onEvent( EventIterator events ) {
while (events.hasNext()) {
try {
latch.countDown();
events.nextEvent();
} catch (Throwable e) {
latch.countDown();
fail(e.getMessage());
}
}
}
public void await( int time,
TimeUnit unit ) {
try {
latch.await(time, unit);
} catch (InterruptedException e) {
fail(e.getMessage());
}
}
public void unregister() throws RepositoryException {
this.session.getWorkspace().getObservationManager().removeEventListener(this);
}
}
@SuppressWarnings( "unchecked" )
@FixFor( "MODE-1050" )
public void testShouldSuccessfullyRegisterChildAndParentSameTypes() throws RepositoryException {
Session session = getHelper().getSuperuserSession();
session.getWorkspace().getNamespaceRegistry().registerNamespace("mx", "urn:test");
final NodeTypeManager nodeTypeMgr = session.getWorkspace().getNodeTypeManager();
final NodeTypeTemplate nodeType = nodeTypeMgr.createNodeTypeTemplate();
nodeType.setName("mx:type");
nodeType.setDeclaredSuperTypeNames(new String[] {"nt:folder"});
final NodeDefinitionTemplate subTypes = nodeTypeMgr.createNodeDefinitionTemplate();
subTypes.setRequiredPrimaryTypeNames(new String[] {"mx:type"});
nodeType.getNodeDefinitionTemplates().add(subTypes);
nodeTypeMgr.registerNodeType(nodeType, false);
}
/**
* Restore a shareable node that automatically removes an existing shareable node (6.13.19). In this case the particular
* shared node is removed but its descendants continue to exist below the remaining members of the shared set.
* <p>
* NOTE: This is a copy of the {@link ShareableNodeTest#testRestoreRemoveExisting()} method in the TCK test, useful for
* debugging.
* </p>
*
* @throws Exception
* @see ShareableNodeTest#testRestoreRemoveExisting()
*/
@FixFor( "MODE-1458" )
@SuppressWarnings( "deprecation" )
public void testRestoreRemoveExisting() throws Exception {
testRootNode = testRootNode.addNode("restoreTest");
int eventTypes = Event.NODE_ADDED | Event.NODE_MOVED | Event.NODE_REMOVED | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED
| Event.PROPERTY_REMOVED;
EventListener listener = new EventListener() {
@Override
public void onEvent( EventIterator events ) {
while (events.hasNext()) {
Event event = events.nextEvent();
if (print) {
System.out.println(event);
System.out.flush();
}
}
}
};
testRootNode.getSession().getWorkspace().getObservationManager()
.addEventListener(listener, eventTypes, testRootNode.getPath(), true, null, null, false);
// setup parent nodes and first child
Node a1 = testRootNode.addNode("a1");
Node a2 = testRootNode.addNode("a2");
Node b1 = a1.addNode("b1");
testRootNode.getSession().save();
// make b1 shareable
ensureMixinType(b1, mixShareable);
b1.save();
// clone
Workspace workspace = b1.getSession().getWorkspace();
workspace.clone(workspace.getName(), b1.getPath(), a2.getPath() + "/b2", false);
// add child c
b1.addNode("c");
b1.save();
// make a2 versionable
ensureMixinType(a2, mixVersionable);
a2.save();
// check in version and check out again
Version v = a2.checkin();
a2.checkout();
print("After checkout: ");
printSubgraph(testRootNode);
// delete b2 and save
a2.getNode("b2").remove();
a2.save();
print("After remove/save: ");
printSubgraph(testRootNode);
// verify shareable set contains one elements only
Node[] shared = getSharedSet(b1);
assertEquals(1, shared.length);
// restore version and remove existing (i.e. b1)
a2.restore(v, true);
print("After restore: ");
printSubgraph(testRootNode);
// verify shareable set contains still one element
shared = getSharedSet(a2.getNode("b2"));
assertEquals(1, shared.length);
// verify child c still exists
Node[] children = toArray(a2.getNode("b2").getNodes());
assertEquals(1, children.length);
}
@FixFor( "MODE-1469" )
public void testShouldReturnAllNodesUnderRootUsingPathCriteria() throws Exception {
Session session = testRootNode.getSession();
List<String> rootNodesPaths = new ArrayList<String>();
rootNodesPaths.add(session.getRootNode().getPath());
for (NodeIterator it = session.getRootNode().getNodes(); it.hasNext();) {
rootNodesPaths.add(it.nextNode().getPath());
}
String query = "SELECT * FROM [nt:base] WHERE [jcr:path] LIKE '/%' AND NOT [jcr:path] LIKE '/%/%'";
javax.jcr.query.QueryManager queryManager = session.getWorkspace().getQueryManager();
QueryResult result = queryManager.createQuery(query, Query.JCR_SQL2).execute();
for (NodeIterator it = result.getNodes(); it.hasNext();) {
Node queryNode = it.nextNode();
String queryNodePath = queryNode.getPath();
assertTrue(queryNodePath + " not part of the original nodes list", rootNodesPaths.remove(queryNodePath));
}
if (!rootNodesPaths.isEmpty()) {
StringBuilder nodesNotFound = new StringBuilder("Nodes: ");
for (String nodePath : rootNodesPaths) {
nodesNotFound.append(nodePath).append(" ");
}
nodesNotFound.append(" not found by query");
fail(nodesNotFound.toString());
}
}
@FixFor( "MODE-1883" )
public void testShouldRestoreDeletedNode() throws Exception {
session = getHelper().getReadWriteSession();
Node node = getTestRoot(session).addNode("checkInTest", "nt:folder");
session.save();
Node n = node.addNode("removeNode", "nt:folder");
n.addMixin("mix:versionable");
session.save();
VersionManager vm = session.getWorkspace().getVersionManager();
// Version 1.0
vm.checkout(n.getPath());
Version version1 = vm.checkin(n.getPath());
assertEquals("1.0", version1.getName());
n.remove();
session.save();
try {
vm.restore(version1, false);
fail("An exception should be thrown, because a removed not cannot be restored");
} catch (VersionException e) {
// expected
}
}
/**
* Return a shared set as an array of nodes.
*
* @param n node
* @return array of nodes in shared set
* @throws RepositoryException
*/
private static Node[] getSharedSet( Node n ) throws RepositoryException {
return toArray(n.getSharedSet());
}
/**
* Return an array of nodes given a <code>NodeIterator</code>.
*
* @param iter node iterator
* @return node array
*/
private static Node[] toArray( NodeIterator iter ) {
List<Node> list = new ArrayList<Node>();
while (iter.hasNext()) {
list.add(iter.nextNode());
}
Node[] result = new Node[list.size()];
list.toArray(result);
return result;
}
}