/*
* 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.IsNot.not;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Arrays;
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.ImportUUIDBehavior;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockException;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.security.AccessControlList;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import org.junit.Before;
import org.junit.Test;
import org.modeshape.common.FixFor;
import org.modeshape.common.junit.SkipLongRunning;
import org.modeshape.jcr.api.JcrTools;
import org.modeshape.jcr.cache.NodeKey;
import org.modeshape.jcr.cache.change.Change;
import org.modeshape.jcr.cache.change.WorkspaceRemoved;
import org.modeshape.jcr.security.SimplePrincipal;
/**
* @author jverhaeg
*/
public class JcrWorkspaceTest extends SingleUseAbstractTest {
private JcrWorkspace workspace;
private String workspaceName;
private JcrSession otherSession;
private JcrWorkspace otherWorkspace;
private String otherWorkspaceName;
@Override
@Before
public void beforeEach() throws Exception {
super.beforeEach();
Node root = session.getRootNode();
Node a = root.addNode("a");
Node ab = a.addNode("b", "nt:unstructured");
Node abc = ab.addNode("c");
Node b = root.addNode("b");
abc.setProperty("stringProperty", "value");
session.save();
assertThat(b, is(notNullValue()));
workspace = session.getWorkspace();
workspaceName = workspace.getName();
otherWorkspaceName = "anotherWs";
workspace.createWorkspace(otherWorkspaceName);
otherSession = repository.login(otherWorkspaceName);
otherWorkspace = otherSession.getWorkspace();
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowCloneWithNullWorkspaceName() throws Exception {
workspace.clone(null, "/src", "/dest", false);
}
@Test
@FixFor( "MODE-1972" )
public void shouldCloneEntireWorkspaces() throws Exception {
otherWorkspace.clone(workspaceName, "/", "/", true);
assertEquals(session.getNode("/a").getIdentifier(), otherSession.getNode("/a").getIdentifier());
assertEquals(session.getNode("/a/b").getIdentifier(), otherSession.getNode("/a/b").getIdentifier());
assertEquals(session.getNode("/a/b/c").getIdentifier(), otherSession.getNode("/a/b/c").getIdentifier());
assertEquals(session.getNode("/b").getIdentifier(), otherSession.getNode("/b").getIdentifier());
}
@Test( expected = RepositoryException.class )
@FixFor( "MODE-1972" )
public void shouldNotClonePartialWorkspaceIntoWorkspaceRoot() throws Exception {
otherWorkspace.clone(workspaceName, "/a/b", "/", false);
}
@Test
@FixFor( "MODE-2007" )
public void shouldCloneChildrenOfRoot() throws Exception {
otherWorkspace.clone(workspaceName, "/a", "/a", false);
otherWorkspace.clone(workspaceName, "/b", "/b", false);
assertEquals(session.getNode("/a").getIdentifier(), otherSession.getNode("/a").getIdentifier());
assertEquals(session.getNode("/a/b").getIdentifier(), otherSession.getNode("/a/b").getIdentifier());
assertEquals(session.getNode("/a/b/c").getIdentifier(), otherSession.getNode("/a/b/c").getIdentifier());
assertEquals(session.getNode("/b").getIdentifier(), otherSession.getNode("/b").getIdentifier());
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowCopyFromNullPathToNullPath() throws Exception {
workspace.copy(null, null);
}
@Test
public void shouldCopyFromPathToAnotherPathInSameWorkspace() throws Exception {
workspace.copy("/a/b", "/b/b-copy");
}
@Test
@FixFor( "MODE-1972" )
public void shouldCopyEntireWorkspaces() throws Exception {
otherWorkspace.copy(workspaceName, "/", "/");
assertNotNull(otherSession.getNode("/a"));
assertNotNull(otherSession.getNode("/a/b"));
assertNotNull(otherSession.getNode("/a/b/c"));
assertNotNull(otherSession.getNode("/b"));
}
@Test( expected = RepositoryException.class )
@FixFor( "MODE-1972" )
public void shouldNotCopyPartialWorkspaceIntoWorkspaceRoot() throws Exception {
otherWorkspace.copy(workspaceName, "/a/b", "/");
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowCopyFromOtherWorkspaceWithNullWorkspace() throws Exception {
workspace.copy(null, null, null);
}
@Test
public void shouldAllowGetAccessibleWorkspaceNames() throws Exception {
List<String> names = Arrays.asList(workspace.getAccessibleWorkspaceNames());
assertThat(names.size(), is(2));
assertThat(names.contains(workspaceName), is(true));
assertThat(names.contains(otherWorkspaceName), is(true));
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowImportContentHandlerWithNullPath() throws Exception {
workspace.getImportContentHandler(null, 0);
}
@Test
public void shouldGetImportContentHandlerWithValidPath() throws Exception {
assertThat(workspace.getImportContentHandler("/b", 0), is(notNullValue()));
}
@Test
public void shouldProvideName() throws Exception {
assertThat(workspace.getName(), is(workspaceName));
}
@Test
public void shouldHaveSameContextIdAsSession() {
assertThat(workspace.context().getId(), is(session.context().getId()));
}
@Test
public void shouldProvideNamespaceRegistry() throws Exception {
NamespaceRegistry registry = workspace.getNamespaceRegistry();
assertThat(registry, is(notNullValue()));
assertThat(registry.getURI(JcrLexicon.Namespace.PREFIX), is(JcrLexicon.Namespace.URI));
}
@Test
public void shouldGetNodeTypeManager() throws Exception {
assertThat(workspace.getNodeTypeManager(), is(notNullValue()));
}
@Test
public void shouldGetObservationManager() throws Exception {
assertThat(workspace.getObservationManager(), is(notNullValue()));
}
@Test
public void shouldProvideQueryManager() throws Exception {
assertThat(workspace.getQueryManager(), notNullValue());
}
@Test
public void shouldCreateQuery() throws Exception {
String statement = "SELECT * FROM [nt:unstructured]";
QueryManager queryManager = workspace.getQueryManager();
Query query = queryManager.createQuery(statement, Query.JCR_SQL2);
assertThat(query, is(notNullValue()));
assertThat(query.getLanguage(), is(Query.JCR_SQL2));
assertThat(query.getStatement(), is(statement));
}
@Test
public void shouldStoreQueryAsNode() throws Exception {
String statement = "SELECT * FROM [nt:unstructured]";
QueryManager queryManager = workspace.getQueryManager();
Query query = queryManager.createQuery(statement, Query.JCR_SQL2);
Node node = query.storeAsNode("/storedQuery");
assertThat(node, is(notNullValue()));
assertThat(node.getPrimaryNodeType().getName(), is("nt:query"));
assertThat(node.getProperty("jcr:language").getString(), is(Query.JCR_SQL2));
assertThat(node.getProperty("jcr:statement").getString(), is(statement));
}
@Test
public void shouldLoadStoredQuery() throws Exception {
String statement = "SELECT * FROM [nt:unstructured]";
QueryManager queryManager = workspace.getQueryManager();
Query query = queryManager.createQuery(statement, Query.JCR_SQL2);
Node node = query.storeAsNode("/storedQuery");
Query loaded = queryManager.getQuery(node);
assertThat(loaded, is(notNullValue()));
assertThat(loaded.getLanguage(), is(Query.JCR_SQL2));
assertThat(loaded.getStatement(), is(statement));
assertThat(loaded.getStoredQueryPath(), is(node.getPath()));
}
@Test
public void shouldProvideSession() throws Exception {
assertThat(workspace.getSession(), is(notNullValue()));
}
@Test
public void shouldAllowImportXml() throws Exception {
String inputData = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<sv:node xmlns:jcr=\"http://www.jcp.org/jcr/1.0\" xmlns:nt=\"http://www.jcp.org/jcr/nt/1.0\" "
+ "xmlns:sv=\"http://www.jcp.org/jcr/sv/1.0\" sv:name=\"workspaceTestNode\">"
+ "<sv:property sv:name=\"jcr:primaryType\" sv:type=\"Name\">"
+ "<sv:value>nt:unstructured</sv:value></sv:property></sv:node>";
workspace.importXML("/", new ByteArrayInputStream(inputData.getBytes()), 0);
}
@Test( expected = IllegalArgumentException.class )
public void shouldNotAllowMoveFromNullPath() throws Exception {
workspace.move(null, null);
}
@Test
public void shouldAllowMoveFromPathToAnotherPathInSameWorkspace() throws Exception {
workspace.move("/a/b", "/b/b-copy");
}
@Test
@FixFor( "MODE-2009" )
public void shouldRemoveAllNodesWhenRemovingWorkspace() throws Exception {
final String wsName = "testWS";
workspace.createWorkspace(wsName);
JcrSession testWsSession = repository.login(wsName);
testWsSession.getRootNode().addNode("testRoot").addNode("subNode");
assertNotNull(testWsSession.getNode("/jcr:system"));
testWsSession.save();
testWsSession.logout();
// workspace deletion clears the cache asynchronously so we need to wait until that completes
final CountDownLatch workspaceDeletedLatch = new CountDownLatch(1);
repository.changeBus().register(changeSet -> {
for (Change change : changeSet) {
if (change instanceof WorkspaceRemoved && ((WorkspaceRemoved)change).getWorkspaceName().equals(wsName)) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
workspaceDeletedLatch.countDown();
}
}
});
workspace.deleteWorkspace(wsName);
workspaceDeletedLatch.await(10, TimeUnit.SECONDS);
workspace.createWorkspace(wsName);
testWsSession = repository.login(wsName);
assertNotNull(testWsSession.getNode("/jcr:system"));
assertNotFound("/testRoot/subNode", testWsSession);
assertNotFound("/testRoot", testWsSession);
assertEquals(1, testWsSession.getRootNode().getNodes().getSize());
assertNotNull(session.getNode("/jcr:system"));
testWsSession.logout();
}
@Test
@FixFor( "MODE-2012" )
public void clonedReferencesShouldPointToTargetWorkspace() throws Exception {
tools.registerNodeTypes(session, "cnd/references.cnd");
Node nodeA = session.getRootNode().addNode("A", "test:node");
Node nodeB = session.getRootNode().addNode("B", "test:node");
Node nodeC = session.getRootNode().addNode("C", "test:node");
Node nodeD = session.getRootNode().addNode("D", "test:node");
nodeA.setProperty("test:strongReference", session.getValueFactory().createValue(nodeB));
nodeA.setProperty("test:weakReference", session.getValueFactory().createValue(nodeC, true));
nodeA.setProperty("test:simpleReference", session.getValueFactory().createSimpleReference(nodeD));
session.save();
otherWorkspace.clone("default", "/", "/", true);
nodeD.remove();
nodeC.remove();
nodeB.remove();
nodeA.remove();
session.save();
AbstractJcrNode otherA = otherSession.getNode("/A");
AbstractJcrNode otherB = otherSession.getNode("/B");
AbstractJcrNode otherC = otherSession.getNode("/C");
AbstractJcrNode otherD = otherSession.getNode("/D");
assertEquals(otherB.getIdentifier(), otherA.getProperty("test:strongReference").getNode().getIdentifier());
assertEquals(otherC.getIdentifier(), otherA.getProperty("test:weakReference").getNode().getIdentifier());
assertEquals(otherD.getIdentifier(), otherA.getProperty("test:simpleReference").getNode().getIdentifier());
}
@Test
@FixFor( "MODE-2012" )
public void clonedUUIDsShouldBeTheSame() throws Exception {
tools.registerNodeTypes(session, "cnd/references.cnd");
Node nodeA = session.getRootNode().addNode("A", "test:node");
Node nodeB = session.getRootNode().addNode("B", "test:node");
session.save();
otherWorkspace.clone("default", "/", "/", true);
assertEquals(nodeA.getIdentifier(), otherSession.getNode("/A").getIdentifier());
assertEquals(nodeB.getIdentifier(), otherSession.getNode("/B").getIdentifier());
}
@Test
@FixFor( "MODE-2012" )
public void copiedReferencesShouldPointToTargetWorkspace() throws Exception {
tools.registerNodeTypes(session, "cnd/references.cnd");
Node nodeA = session.getRootNode().addNode("A", "test:node");
Node nodeB = session.getRootNode().addNode("B", "test:node");
Node nodeC = session.getRootNode().addNode("C", "test:node");
Node nodeD = session.getRootNode().addNode("D", "test:node");
nodeA.setProperty("test:strongReference", session.getValueFactory().createValue(nodeB));
nodeA.setProperty("test:weakReference", session.getValueFactory().createValue(nodeC, true));
nodeA.setProperty("test:simpleReference", session.getValueFactory().createSimpleReference(nodeD));
session.save();
otherWorkspace.copy("default", "/", "/");
nodeD.remove();
nodeC.remove();
nodeB.remove();
nodeA.remove();
session.save();
AbstractJcrNode otherA = otherSession.getNode("/A");
AbstractJcrNode otherB = otherSession.getNode("/B");
AbstractJcrNode otherC = otherSession.getNode("/C");
AbstractJcrNode otherD = otherSession.getNode("/D");
assertEquals(otherB.getIdentifier(), otherA.getProperty("test:strongReference").getNode().getIdentifier());
assertEquals(otherC.getIdentifier(), otherA.getProperty("test:weakReference").getNode().getIdentifier());
assertEquals(otherD.getIdentifier(), otherA.getProperty("test:simpleReference").getNode().getIdentifier());
}
@Test
@FixFor( "MODE-2012" )
public void copiedUUIDsShouldNotBeTheSame() throws Exception {
tools.registerNodeTypes(session, "cnd/references.cnd");
Node nodeA = session.getRootNode().addNode("A", "test:node");
Node nodeB = session.getRootNode().addNode("B", "test:node");
session.save();
otherWorkspace.copy("default", "/", "/");
assertNotEquals(nodeA.getIdentifier(), otherSession.getNode("/A").getIdentifier());
assertNotEquals(nodeB.getIdentifier(), otherSession.getNode("/B").getIdentifier());
}
@Test
@FixFor( "MODE-2114" )
public void copiedReferencesShouldHaveUpdatedUUIDs() throws Exception {
tools.registerNodeTypes(session, "cnd/references.cnd");
Node parent = session.getRootNode().addNode("parent");
Node nodeA = parent.addNode("A", "test:node");
Node nodeB = parent.addNode("B", "test:node");
Node nodeC = parent.addNode("C", "test:node");
Node nodeD = parent.addNode("D", "test:node");
nodeA.setProperty("test:strongReference", session.getValueFactory().createValue(nodeB));
nodeA.setProperty("test:weakReference", session.getValueFactory().createValue(nodeC, true));
nodeA.setProperty("test:simpleReference", session.getValueFactory().createSimpleReference(nodeD));
session.save();
workspace.copy("/parent", "/new_parent");
AbstractJcrNode otherA = session.getNode("/new_parent/A");
AbstractJcrNode otherB = session.getNode("/new_parent/B");
AbstractJcrNode otherC = session.getNode("/new_parent/C");
AbstractJcrNode otherD = session.getNode("/new_parent/D");
assertEquals(otherB.getIdentifier(), otherA.getProperty("test:strongReference").getNode().getIdentifier());
assertEquals(otherC.getIdentifier(), otherA.getProperty("test:weakReference").getNode().getIdentifier());
assertEquals(otherD.getIdentifier(), otherA.getProperty("test:simpleReference").getNode().getIdentifier());
}
@Test
@FixFor( "MODE-2115" )
public void copiedNodesShouldReplaceAutoCreatedChildren() throws Exception {
tools.registerNodeTypes(session, "cnd/autocreated-child-nodes.cnd");
Node parent = session.getRootNode().addNode("parent", "test:autocreatedFolders");
session.save();
long folder1CreatedTs = parent.getNode("folder1").getProperty("jcr:created").getDate().getTimeInMillis();
long folder2CreatedTs = parent.getNode("folder2").getProperty("jcr:created").getDate().getTimeInMillis();
workspace.copy("/parent", "/new_parent");
AbstractJcrNode newParent = session.getNode("/new_parent");
assertEquals(2, newParent.getNodes().getSize());
Node folder1Copy = assertNode("/new_parent/folder1", "nt:folder");
assertEquals(folder1CreatedTs, folder1Copy.getProperty("jcr:created").getDate().getTimeInMillis());
Node folder2Copy = assertNode("/new_parent/folder2", "nt:folder");
assertEquals(folder2CreatedTs, folder2Copy.getProperty("jcr:created").getDate().getTimeInMillis());
}
@Test
@FixFor( "MODE-2115" )
public void copiedNodesShouldReplaceReferenceableAutoCreatedChildren() throws Exception {
tools.registerNodeTypes(session, "cnd/autocreated-child-nodes.cnd");
session.getRootNode().addNode("parent", "test:autocreatedReferenceableChildren");
session.save();
session.getNode("/parent/child1").addNode("child1_1");
session.save();
workspace.copy("/parent", "/new_parent");
AbstractJcrNode newParent = session.getNode("/new_parent");
assertEquals(2, newParent.getNodes().getSize());
// validate that the jcr:uuid is the same as the identifier of the node
Node child1 = assertNode("/new_parent/child1", "test:subnodeReferenceable");
assertEquals(child1.getIdentifier(), child1.getProperty("jcr:uuid").getString());
Node child2 = assertNode("/new_parent/child2", "test:subnodeReferenceable");
assertEquals(child2.getIdentifier(), child2.getProperty("jcr:uuid").getString());
assertNode("/new_parent/child1/child1_1");
}
@Test
@FixFor( "MODE-2115" )
public void clonedNodesWithAutoCreatedChildrenShouldPreserveIdentifiers() throws Exception {
tools.registerNodeTypes(session, "cnd/autocreated-child-nodes.cnd");
Node parent = session.getRootNode().addNode("parent", "test:autocreatedFolders");
session.save();
Node folder1 = parent.getNode("folder1");
long folder1CreatedTs = folder1.getProperty("jcr:created").getDate().getTimeInMillis();
Node folder2 = parent.getNode("folder2");
long folder2CreatedTs = folder2.getProperty("jcr:created").getDate().getTimeInMillis();
otherWorkspace.clone(workspaceName, "/parent", "/parent", true);
AbstractJcrNode otherParent = otherSession.getNode("/parent");
assertEquals(2, otherParent.getNodes().getSize());
Node otherFolder1 = otherSession.getNode("/parent/folder1");
assertEquals(folder1CreatedTs, otherFolder1.getProperty("jcr:created").getDate().getTimeInMillis());
assertEquals(folder1.getIdentifier(), otherFolder1.getIdentifier());
Node otherFolder2 = otherSession.getNode("/parent/folder2");
assertEquals(folder2.getIdentifier(), otherFolder2.getIdentifier());
assertEquals(folder2CreatedTs, otherFolder2.getProperty("jcr:created").getDate().getTimeInMillis());
}
@Test
@FixFor( "MODE-2456")
public void copyingShouldKeepCorrectACLCount() throws Exception {
AccessControlManager accessControlManager = session.getAccessControlManager();
session.getRootNode().addNode("aclNode");
AccessControlList acl = acl("/aclNode");
acl.addAccessControlEntry(SimplePrincipal.EVERYONE, new Privilege[] {accessControlManager.privilegeFromName(Privilege.JCR_ALL)});
accessControlManager.setPolicy("/aclNode", acl);
session.save();
assertTrue(repository().repositoryCache().isAccessControlEnabled());
// copy the node and check that the ACLs were copied
session.getWorkspace().copy("/aclNode", "/aclNodeCopy");
assertEquals(1, accessControlManager.getPolicies("/aclNodeCopy").length);
Privilege[] privileges = accessControlManager.getPrivileges("/aclNodeCopy");
assertEquals(1, privileges.length);
assertEquals("jcr:all", privileges[0].getName());
// remove the ACLs from the copied node and check that the original ACLs are unaffected
accessControlManager.removePolicy("/aclNodeCopy", null);
session.save();
assertEquals(0, accessControlManager.getPolicies("/aclNodeCopy").length);
assertEquals(1, accessControlManager.getPrivileges("/aclNode").length);
assertTrue("ACLs should not be disabled", repository().repositoryCache().isAccessControlEnabled());
// remove the original ACLs as well
accessControlManager.removePolicy("/aclNode", null);
session.save();
assertFalse("ACLs should be disabled", repository().repositoryCache().isAccessControlEnabled());
}
@Test
@FixFor( "MODE-2456")
public void cloningShouldKeepCorrectACLCount() throws Exception {
AccessControlManager accessControlManager = session.getAccessControlManager();
session.getRootNode().addNode("aclNode");
AccessControlList acl = acl("/aclNode");
acl.addAccessControlEntry(SimplePrincipal.EVERYONE, new Privilege[] { accessControlManager.privilegeFromName(
Privilege.JCR_ALL) });
accessControlManager.setPolicy("/aclNode", acl);
session.save();
assertTrue(repository().repositoryCache().isAccessControlEnabled());
// clone the node into another workspace
otherWorkspace.clone(workspaceName, "/aclNode", "/aclNodeClone", false);
AccessControlManager otherAccessControlManager = otherSession.getAccessControlManager();
assertEquals(1, otherAccessControlManager.getPolicies("/aclNodeClone").length);
Privilege[] privileges = otherAccessControlManager.getPrivileges("/aclNodeClone");
assertEquals(1, privileges.length);
assertEquals("jcr:all", privileges[0].getName());
// remove the ACLs from the copied node and check that the original ACLs are unaffected
otherAccessControlManager.removePolicy("/aclNodeClone", null);
otherSession.save();
assertEquals(0, otherAccessControlManager.getPolicies("/aclNodeClone").length);
assertEquals(1, accessControlManager.getPrivileges("/aclNode").length);
assertTrue("ACLs should not be disabled", repository().repositoryCache().isAccessControlEnabled());
// remove the original ACLs as well
accessControlManager.removePolicy("/aclNode", null);
session.save();
assertFalse("ACLs should be disabled", repository().repositoryCache().isAccessControlEnabled());
}
@Test
@FixFor( "MODE-2457")
public void onlyOwningSessionShouldCopyLockedNode() throws Exception {
Node node = session.getRootNode().addNode("lockable");
node.addMixin("mix:lockable");
session.save();
JcrLockManager lockManager = session.lockManager();
lockManager.lock("/lockable", true, true, Long.MAX_VALUE, null);
// this should succeed because this is the lock owning session
session.getWorkspace().copy("/lockable", "/lockable_copy");
assertNotNull(session.getNode("/lockable_copy"));
assertTrue("Original node should still be locked", lockManager.isLocked("/lockable"));
assertFalse("Copied node should not be locked", lockManager.isLocked("/lockable_copy"));
JcrSession session1 = repository().login();
try {
session1.getWorkspace().copy("/lockable", "/lockable_copy1");
fail("Copy should not succeed because the session does not own the lock");
} catch (LockException e) {
//expected
} finally {
session1.logout();
lockManager.unlock("/lockable");
}
}
@Test
@FixFor( "MODE-2457")
public void shouldNotCloneLockedNode() throws Exception {
Node node = session.getRootNode().addNode("lockable");
node.addMixin("mix:lockable");
session.save();
JcrLockManager lockManager = session.lockManager();
lockManager.lock("/lockable", true, true, Long.MAX_VALUE, null);
try {
// this should fail because the session to the other workspace does not own the lock
otherWorkspace.clone(workspaceName, "/lockable", "/lockable_clone", false);
fail("Copy should not succeed because the session does not own the lock");
} catch (LockException e) {
//expected
} finally {
lockManager.unlock("/lockable");
}
otherWorkspace.clone(workspaceName, "/lockable", "/lockable_clone", false);
assertNotNull(otherSession.getNode("/lockable_clone"));
}
@SkipLongRunning
@FixFor( "MODE-2012" )
@Test
public void shouldCorrectlyImportSameContentIntoMultipleWorkspaces() throws Exception {
// Register the Cars node types used in the content ...
tools.registerNodeTypes(session, "cars.cnd");
createWorkspace("workspaceA");
createWorkspace("workspaceB");
createWorkspace("workspaceC");
importFile("/", "workspaceA", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
importFile("/", "workspaceB", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
importFile("/", "workspaceC", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
// print = true;
printWorkspace("workspaceA", false);
printWorkspace("workspaceB", false);
printWorkspace("workspaceC", false);
checkCorrespondingPaths("workspaceA", "workspaceB", "workspaceC");
checkCorrespondingPaths("workspaceB", "workspaceA", "workspaceC");
checkCorrespondingPaths("workspaceC", "workspaceA", "workspaceB");
Session sessionA = repository.login("workspaceA");
Session sessionB = repository.login("workspaceB");
Session sessionC = repository.login("workspaceC");
for (int i = 0; i != 3; ++i) {
Node utilityA = sessionA.getNode("/Cars/Utility");
Node utilityB = sessionB.getNode("/Cars/Utility");
Node utilityC = sessionC.getNode("/Cars/Utility");
NodeKey utilityAkey = ((AbstractJcrNode)utilityA).key();
NodeKey utilityBkey = ((AbstractJcrNode)utilityA).key();
NodeKey utilityCkey = ((AbstractJcrNode)utilityA).key();
final String sourceKey = utilityAkey.getSourceKey();
assertThat(utilityAkey.getSourceKey(), is(utilityBkey.getSourceKey()));
assertThat(utilityAkey.getSourceKey(), is(utilityCkey.getSourceKey()));
NodeIterator iterA = utilityA.getNodes();
NodeIterator iterB = utilityB.getNodes();
NodeIterator iterC = utilityC.getNodes();
while (iterA.hasNext()) {
Node childA = iterA.nextNode();
Node childB = iterB.nextNode();
Node childC = iterC.nextNode();
assertThat(childA.getName(), is(childB.getName()));
assertThat(childA.getName(), is(childC.getName()));
if (i != 0 && childA.getName().startsWith("newChild")) {
// Must be our new node ...
continue;
}
assertThat(childA.isNodeType("mix:referenceable"), is(true));
assertThat(childB.isNodeType("mix:referenceable"), is(true));
assertThat(childC.isNodeType("mix:referenceable"), is(true));
NodeKey childAkey = ((AbstractJcrNode)childA).key();
NodeKey childBkey = ((AbstractJcrNode)childB).key();
NodeKey childCkey = ((AbstractJcrNode)childC).key();
assertThat(childAkey.getSourceKey(), is(sourceKey));
assertThat(childBkey.getSourceKey(), is(sourceKey));
assertThat(childCkey.getSourceKey(), is(sourceKey));
assertThat(childBkey.getWorkspaceKey(), is(not(childAkey.getWorkspaceKey())));
assertThat(childCkey.getWorkspaceKey(), is(not(childAkey.getWorkspaceKey())));
assertThat(childBkey.getIdentifier(), is(childAkey.getIdentifier()));
assertThat(childCkey.getIdentifier(), is(childAkey.getIdentifier()));
}
// Now add a new non-referencable child to "/Cars/Utility" ...
Node childA = utilityA.addNode("newChild" + (i + 1));
Node childB = utilityB.addNode("newChild" + (i + 1));
Node childC = utilityC.addNode("newChild" + (i + 1));
childA.getSession().save();
childB.getSession().save();
childC.getSession().save();
}
checkCorrespondingPaths("workspaceA", "workspaceB", "workspaceC");
checkCorrespondingPaths("workspaceB", "workspaceA", "workspaceC");
checkCorrespondingPaths("workspaceC", "workspaceA", "workspaceB");
sessionA.logout();
sessionB.logout();
sessionC.logout();
}
@SkipLongRunning
@FixFor( "MODE-2012" )
@Test
public void shouldCorrectlyCloneWorkspacesWithCorrespondingContent() throws Exception {
// Register the Cars node types used in the content ...
tools.registerNodeTypes(session, "cars.cnd");
createWorkspace("workspaceA");
createWorkspace("workspaceB");
createWorkspace("workspaceC");
importFile("/", "workspaceA", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
cloneWorkspace("workspaceA", "workspaceB", false);
cloneWorkspace("workspaceA", "workspaceC", false);
// print = true;
printWorkspace("workspaceA", false);
printWorkspace("workspaceB", false);
printWorkspace("workspaceC", false);
checkCorrespondingPaths("workspaceA", "workspaceB", "workspaceC");
checkCorrespondingPaths("workspaceB", "workspaceA", "workspaceC");
checkCorrespondingPaths("workspaceC", "workspaceA", "workspaceB");
Session sessionA = repository.login("workspaceA");
Session sessionB = repository.login("workspaceB");
Session sessionC = repository.login("workspaceC");
for (int i = 0; i != 3; ++i) {
Node utilityA = sessionA.getNode("/Cars/Utility");
Node utilityB = sessionB.getNode("/Cars/Utility");
Node utilityC = sessionC.getNode("/Cars/Utility");
NodeKey utilityAkey = ((AbstractJcrNode)utilityA).key();
NodeKey utilityBkey = ((AbstractJcrNode)utilityA).key();
NodeKey utilityCkey = ((AbstractJcrNode)utilityA).key();
final String sourceKey = utilityAkey.getSourceKey();
assertThat(utilityAkey.getSourceKey(), is(utilityBkey.getSourceKey()));
assertThat(utilityAkey.getSourceKey(), is(utilityCkey.getSourceKey()));
NodeIterator iterA = utilityA.getNodes();
NodeIterator iterB = utilityB.getNodes();
NodeIterator iterC = utilityC.getNodes();
while (iterA.hasNext()) {
Node childA = iterA.nextNode();
if (childA.getName().startsWith("newChild")) {
assertThat(iterB.hasNext(), is(false));
assertThat(iterC.hasNext(), is(false));
continue;
}
Node childB = iterB.nextNode();
Node childC = iterC.nextNode();
assertThat(childA.getName(), is(childB.getName()));
assertThat(childA.getName(), is(childC.getName()));
assertThat(childA.isNodeType("mix:referenceable"), is(true));
assertThat(childB.isNodeType("mix:referenceable"), is(true));
assertThat(childC.isNodeType("mix:referenceable"), is(true));
NodeKey childAkey = ((AbstractJcrNode)childA).key();
NodeKey childBkey = ((AbstractJcrNode)childB).key();
NodeKey childCkey = ((AbstractJcrNode)childC).key();
assertThat(childAkey.getSourceKey(), is(sourceKey));
assertThat(childBkey.getSourceKey(), is(sourceKey));
assertThat(childCkey.getSourceKey(), is(sourceKey));
assertThat(childBkey.getWorkspaceKey(), is(not(childAkey.getWorkspaceKey())));
assertThat(childCkey.getWorkspaceKey(), is(not(childAkey.getWorkspaceKey())));
assertThat(childBkey.getIdentifier(), is(childAkey.getIdentifier()));
assertThat(childCkey.getIdentifier(), is(childAkey.getIdentifier()));
}
// Now add a new non-referencable child to "/Cars/Utility" in workspace A only ...
Node childA = utilityA.addNode("newChild" + (i + 1));
childA.getSession().save();
}
checkCorrespondingPaths("workspaceA", "workspaceB", "workspaceC");
checkCorrespondingPaths("workspaceB", "workspaceA", "workspaceC");
checkCorrespondingPaths("workspaceC", "workspaceA", "workspaceB");
sessionA.logout();
sessionB.logout();
sessionC.logout();
}
@Test
@FixFor("MODE-2637")
public void copyNodeWithEmptyMultiValueProperty() throws Exception {
// start a repository with some indexes
startRepositoryWithConfigurationFrom("config/repo-config-local-provider-and-nodetype-index.json");
session.getRootNode().addNode("a");
Node node = session.getNode("/a");
node.addMixin("mix:referenceable");
session.save();
node.removeMixin("mix:referenceable");
session.save();
assertTrue(node.hasProperty("jcr:mixinTypes"));
assertThat(node.getProperty("jcr:mixinTypes").getValues().length, is(0));
session.getWorkspace().copy(session.getNode("/a").getPath(), "/a2");
session.save();
}
protected void checkCorrespondingPaths( String workspace,
final String... otherWorkspaces ) throws Exception {
final Session session = repository.login(workspace);
final Map<String, Session> otherWorkspaceSessions = new HashMap<String, Session>();
for (String otherWorkspace : otherWorkspaces) {
assertThat(otherWorkspace, is(not(workspace)));
Session otherSession = repository.login(otherWorkspace);
otherWorkspaceSessions.put(otherWorkspace, otherSession);
}
final String expectedWorkspaceKey = ((AbstractJcrNode)session.getRootNode()).key().getWorkspaceKey();
try {
tools.onEachNode(session, false, new JcrTools.NodeOperation() {
@Override
public void run( Node node ) throws Exception {
final String path = node.getPath();
final NodeKey key = ((AbstractJcrNode)node).key();
assertThat(key.getWorkspaceKey(), is(expectedWorkspaceKey));
if (node.isNodeType("mix:referenceable")) {
for (String otherWorkspace : otherWorkspaces) {
String correspondingPath = node.getCorrespondingNodePath(otherWorkspace);
assertThat(correspondingPath, is(path));
// Check that the node keys are actually different ...
final Node correspondingNode = otherWorkspaceSessions.get(otherWorkspace).getNode(correspondingPath);
final NodeKey correspondingKey = ((AbstractJcrNode)correspondingNode).key();
assertThat(correspondingKey, is(not(key)));
assertThat(correspondingKey.getIdentifier(), is(key.getIdentifier()));
assertThat(correspondingKey.getSourceKey(), is(key.getSourceKey()));
assertThat(correspondingKey.getWorkspaceKey(), is(not(key.getWorkspaceKey()))); // this is what
// differs
}
}
}
});
} finally {
session.logout();
for (Session otherSession : otherWorkspaceSessions.values()) {
otherSession.logout();
}
}
}
protected void printWorkspace( String workspace,
boolean includeSystem ) throws Exception {
printWorkspace(workspace, includeSystem, Integer.MAX_VALUE);
}
protected void printWorkspace( String workspaceName,
boolean includeSystem,
int maxDepthToPrint ) throws Exception {
if (!print) return;
Session session = repository.login(workspaceName);
try {
printMessage("Content of workspace '" + workspaceName + "'");
NodeIterator iter = session.getRootNode().getNodes();
while (iter.hasNext()) {
Node node = iter.nextNode();
if (!includeSystem && node.getName().equals("jcr:system")) continue;
tools.printSubgraph(node, " ", 1, maxDepthToPrint);
}
} finally {
session.logout();
}
}
protected void createWorkspace( String workspaceName ) throws Exception {
Session session = repository.login();
try {
session.getWorkspace().createWorkspace(workspaceName);
} finally {
session.logout();
}
}
protected void cloneWorkspace( String sourceWorkspace,
String targetWorkspace,
boolean removeExisting ) throws Exception {
Session session = repository.login(targetWorkspace);
try {
session.getWorkspace().clone(sourceWorkspace, "/", "/", removeExisting);
} finally {
session.logout();
}
}
protected void importFile( String importIntoPath,
String workspaceName,
String resourceName,
int importBehavior ) throws Exception {
// Import the car content ...
InputStream stream = getClass().getClassLoader().getResourceAsStream(resourceName);
assertThat(stream, is(notNullValue()));
Session session = repository.login(workspaceName);
try {
session.getWorkspace().importXML(importIntoPath, stream, importBehavior); // shouldn't exist yet
} finally {
try {
stream.close();
} finally {
session.logout();
}
}
}
private void assertNotFound( String absPath,
JcrSession jcrSession ) throws RepositoryException {
try {
jcrSession.getNode(absPath);
fail("Node " + absPath + " should not have been found in the session " + session);
} catch (PathNotFoundException e) {
// expected
}
}
}