/* * 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.connector.mock; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; 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 static org.modeshape.jcr.ValidateQuery.validateQuery; import java.security.AccessControlException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.jcr.ItemExistsException; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.Property; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.nodetype.NodeType; import javax.jcr.query.Query; import javax.jcr.security.AccessControlList; import javax.jcr.security.AccessControlManager; import org.junit.Before; import org.junit.Test; import org.modeshape.common.FixFor; import org.modeshape.jcr.ModeShapePermissions; import org.modeshape.jcr.SingleUseAbstractTest; import org.modeshape.jcr.api.Workspace; import org.modeshape.jcr.api.federation.FederationManager; import org.modeshape.jcr.api.query.QueryManager; import org.modeshape.jcr.spi.federation.ConnectorException; /** * Unit test for the {@link org.modeshape.connector.mock.MockConnector} which validates several areas around the connector SPI and * federation design. * * @author Horia Chiorean (hchiorea@redhat.com) */ public class MockConnectorTest extends SingleUseAbstractTest { private static final String SOURCE_NAME = "mock-source"; private FederationManager federationManager; private Node testRoot; @Before @Override public void beforeEach() throws Exception { startRepositoryWithConfigurationFrom("config/repo-config-mock-federation.json"); testRoot = ((Node)session.getRootNode()).addNode("testRoot"); session.save(); federationManager = ((Workspace)session.getWorkspace()).getFederationManager(); } @Test public void shouldCreateProjectionWithAlias() throws Exception { // add an internal node testRoot.addNode("node1"); session.save(); // link the first external document federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); assertEquals(2, testRoot.getNodes().getSize()); Node doc1Federated = assertNodeFound("/testRoot/federated1"); assertEquals(testRoot.getIdentifier(), doc1Federated.getParent().getIdentifier()); assertEquals("a string", doc1Federated.getProperty("federated1_prop1").getString()); assertEquals(12, doc1Federated.getProperty("federated1_prop2").getLong()); // link a second external document with a sub-child federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "federated2"); assertEquals(3, testRoot.getNodes().getSize()); Node doc2Federated = assertNodeFound("/testRoot/federated2"); assertEquals(testRoot.getIdentifier(), doc2Federated.getParent().getIdentifier()); assertEquals("another string", doc2Federated.getProperty("federated2_prop1").getString()); assertEquals(false, doc2Federated.getProperty("federated2_prop2").getBoolean()); Node doc2FederatedChild = assertNodeFound("/testRoot/federated2/federated3"); assertEquals("yet another string", doc2FederatedChild.getProperty("federated3_prop1").getString()); } @Test public void shouldCreateProjectionWithoutAlias() throws Exception { // link the first external document federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, null); assertEquals(1, testRoot.getNodes().getSize()); Node doc1Federated = assertNodeFound("/testRoot" + MockConnector.DOC1_LOCATION); assertEquals(testRoot.getIdentifier(), doc1Federated.getParent().getIdentifier()); assertEquals("a string", doc1Federated.getProperty("federated1_prop1").getString()); assertEquals(12, doc1Federated.getProperty("federated1_prop2").getLong()); } @Test public void shouldCreateExternalNode() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Node externalNode1 = doc1Federated.addNode("federated1_1", null); externalNode1.setProperty("prop1", "a value"); externalNode1.addNode("federated1_1_1", null); session.save(); Node federated1_1 = doc1Federated.getNode("federated1_1"); assertNotNull(federated1_1); assertEquals(doc1Federated, federated1_1.getParent()); assertEquals(1, doc1Federated.getNodes().getSize()); assertNotNull(session.getNode("/testRoot/federated1/federated1_1")); assertEquals("a value", federated1_1.getProperty("prop1").getString()); Node federated1_1_1 = assertNodeFound("/testRoot/federated1/federated1_1/federated1_1_1"); assertEquals(federated1_1, federated1_1_1.getParent()); } @Test public void shouldUpdateExternalNodeProperties() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Node externalNode1 = doc1Federated.addNode("federated1_1", null); externalNode1.setProperty("prop1", "a value"); externalNode1.setProperty("prop2", "a value 2"); session.save(); externalNode1.setProperty("prop1", "edited value"); assertEquals("a value 2", externalNode1.getProperty("prop2").getString()); externalNode1.getProperty("prop2").remove(); externalNode1.setProperty("prop3", "a value 3"); session.save(); Node federated1_1 = doc1Federated.getNode("federated1_1"); assertEquals("edited value", federated1_1.getProperty("prop1").getString()); assertEquals("a value 3", federated1_1.getProperty("prop3").getString()); try { federated1_1.getProperty("prop2"); fail("Property was not removed from external node"); } catch (PathNotFoundException e) { // expected } } @Test public void shouldUpdateExternalNodeMixins() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Node externalNode1 = doc1Federated.addNode("federated1_1", null); externalNode1.addMixin("mix:created"); session.save(); externalNode1 = session.getNode("/testRoot/federated1/federated1_1"); NodeType[] mixins = externalNode1.getMixinNodeTypes(); assertEquals(1, mixins.length); assertEquals("mix:created", mixins[0].getName()); externalNode1.removeMixin("mix:created"); externalNode1.addMixin("mix:lastModified"); session.save(); externalNode1 = session.getNode("/testRoot/federated1/federated1_1"); mixins = externalNode1.getMixinNodeTypes(); assertEquals(1, mixins.length); assertEquals("mix:lastModified", mixins[0].getName()); } @Test public void shouldUpdateExternalNodeChildren() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); doc1Federated.addNode("federated1_1", null); session.save(); String externalNodePath = "/testRoot/federated1/federated1_1"; assertExternalNodeHasChildren(externalNodePath); Node externalNode = session.getNode(externalNodePath); externalNode.addNode("child1"); externalNode.addNode("child2"); session.save(); assertExternalNodeHasChildren(externalNodePath, "child1", "child2"); externalNode = session.getNode(externalNodePath); externalNode.getNode("child1").remove(); externalNode.getNode("child2").remove(); externalNode.addNode("child3"); session.save(); assertExternalNodeHasChildren(externalNodePath, "child3"); } @Test public void shouldMoveExternalNode() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Node parent1 = doc1Federated.addNode("parent1", null); parent1.addNode("child1"); parent1.addNode("childX"); parent1.addNode("child2"); Node parent2 = doc1Federated.addNode("parent2", null); parent2.addNode("child3"); parent2.addNode("child4"); session.save(); assertExternalNodeHasChildren("/testRoot/federated1/parent1", "child1", "childX", "child2"); assertExternalNodeHasChildren("/testRoot/federated1/parent2", "child3", "child4"); ((Workspace)session.getWorkspace()).move("/testRoot/federated1/parent1/childX", "/testRoot/federated1/parent2/childX"); assertExternalNodeHasChildren("/testRoot/federated1/parent1", "child1", "child2"); assertExternalNodeHasChildren("/testRoot/federated1/parent2", "child3", "child4", "childX"); } @Test public void shouldReorderExternalNodes() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Node parent1 = doc1Federated.addNode("parent1", null); parent1.addNode("child1"); parent1.addNode("child2"); parent1.addNode("child3"); session.save(); assertExternalNodeHasChildren("/testRoot/federated1/parent1", "child1", "child2", "child3"); parent1.orderBefore("child1", "child2"); session.save(); assertExternalNodeHasChildren("/testRoot/federated1/parent1", "child2", "child1", "child3"); } @Test public void shouldNotAllowInternalNodesAsReferrers() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Node externalNode = doc1Federated.addNode("federated1_1", null); externalNode.addMixin("mix:referenceable"); session.save(); Value weakRef = session.getValueFactory().createValue(externalNode, true); testRoot.setProperty("weakRef", weakRef); try { session.save(); fail("It should not be possible to create weak references from internal nodes to external nodes"); } catch (RepositoryException e) { assertTrue(e.getCause() instanceof ConnectorException); } Value strongRef = session.getValueFactory().createValue(externalNode, false); testRoot.setProperty("strongRef", strongRef); try { session.save(); fail("It should not be possible to create strong references from internal nodes to external nodes"); } catch (RepositoryException e) { assertTrue(e.getCause() instanceof ConnectorException); } } @Test public void shouldRemoveExternalNode() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); doc1Federated.addNode("federated1_1", null); session.save(); Node externalNode = session.getNode("/testRoot/federated1/federated1_1"); externalNode.remove(); session.save(); assertNodeNotFound("/testRoot/federated1/federated1_1"); } @Test public void shouldRemoveProjectionViaNodeRemove() throws Exception { testRoot.addNode("child1"); session.save(); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "federated2"); Node projection = session.getNode("/testRoot/federated1"); projection.remove(); session.save(); assertNodeNotFound("/testRoot/federated1"); assertNodeFound("/testRoot/federated2"); assertNodeFound("/testRoot/child1"); projection = session.getNode("/testRoot/federated2"); projection.remove(); session.save(); assertNodeNotFound("/testRoot/federated2"); assertNodeFound("/testRoot/child1"); } @Test public void removingProjectionViaNodeRemoveShouldDeleteExternalNodes() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "projection1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "projection2"); Node projection1 = assertNodeFound("/testRoot/projection1/federated3"); assertNodeFound("/testRoot/projection2/federated3"); projection1.remove(); session.save(); assertNodeNotFound("/testRoot/projection2/federated3"); } @Test public void removeProjectionViaFederationManagerShouldNotDeleteExternalNode() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "projection1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "projection2"); federationManager.removeProjection("/testRoot/projection1"); assertNodeFound("/testRoot/projection2/federated3"); } @Test public void shouldRemoveProjectionViaFederationManager() throws Exception { testRoot.addNode("child1"); session.save(); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "federated2"); federationManager.removeProjection("/testRoot/federated2"); assertNodeFound("/testRoot/federated1"); assertNodeFound("/testRoot/child1"); assertNodeNotFound("/testRoot/federated2"); federationManager.removeProjection("/testRoot/federated1"); assertNodeNotFound("/testRoot/federation1"); assertNodeFound("/testRoot/child1"); } @Test public void removingInternalNodeShouldNotRemoveExternalNodes() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "federated2"); Node internalNode1 = testRoot.addNode("internalNode1"); session.save(); federationManager.createProjection("/testRoot/internalNode1", SOURCE_NAME, MockConnector.DOC2_LOCATION, "federated2"); // remove the federated node directly assertNodeFound("/testRoot/internalNode1/federated2/federated3"); internalNode1.remove(); session.save(); // check external nodes are still there assertNodeFound("/testRoot/federated2/federated3"); // but the projection has been removed... assertNoNode("/testRoot/internalNode1/federated2/federated3"); testRoot.addNode("internalNode2").addNode("internalNode2_1"); session.save(); federationManager.createProjection("/testRoot/internalNode2/internalNode2_1", SOURCE_NAME, MockConnector.DOC2_LOCATION, "federated2"); // remove an ancestor of the federated node assertNodeFound("/testRoot/internalNode2/internalNode2_1/federated2/federated3"); ((Node)session.getNode("/testRoot/internalNode2")).remove(); session.save(); // check external nodes are still there assertNodeFound("/testRoot/federated2/federated3"); } @Test( expected = IllegalArgumentException.class ) public void shouldNotRemoveProjectionUsingRootPath() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); federationManager.removeProjection("/"); } @Test( expected = PathNotFoundException.class ) public void shouldNotRemoveProjectionIfPathInvalid() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); federationManager.removeProjection("/testRoot/federated"); } @Test public void shouldNavigateChildrenFromPagedConnector() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.PAGED_DOC_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); NodeIterator nodesIterator = doc1Federated.getNodes(); assertEquals(3, nodesIterator.getSize()); List<String> childrenNames = new ArrayList<String>(3); while (nodesIterator.hasNext()) { childrenNames.add(nodesIterator.nextNode().getName()); } assertEquals(Arrays.asList("federated4", "federated5", "federated6"), childrenNames); } @Test public void shouldIndexProjectionsAndExternalNodes() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "federated2"); Workspace workspace = session.getWorkspace(); workspace.reindex(); QueryManager queryManager = workspace.getQueryManager(); Query query = queryManager.createQuery("select * FROM [nt:base] WHERE [jcr:path] LIKE '/testRoot/federated1'", Query.JCR_SQL2); assertEquals(1, query.execute().getNodes().getSize()); query = queryManager.createQuery("select * FROM [nt:base] WHERE [jcr:path] LIKE '/testRoot/federated2'", Query.JCR_SQL2); assertEquals(1, query.execute().getNodes().getSize()); Node externalNode = session.getNode("/testRoot/federated2/federated3"); externalNode.setProperty("test", "a value"); session.save(); query = queryManager.createQuery("select * FROM [nt:base] as a WHERE a.test='a value'", Query.JCR_SQL2); assertEquals(1, query.execute().getNodes().getSize()); query = queryManager.createQuery("select * FROM [nt:base] WHERE [jcr:path] LIKE '/testRoot/federated2/federated3'", Query.JCR_SQL2); assertEquals(1, query.execute().getNodes().getSize()); } @Test public void shouldNotIndexNotQueryableConnector() throws Exception { federationManager.createProjection("/testRoot", "mock-source-non-queryable", MockConnector.DOC2_LOCATION, "federated2"); Workspace workspace = session.getWorkspace(); workspace.reindex(); QueryManager queryManager = workspace.getQueryManager(); Query query = queryManager.createQuery("select * FROM [nt:base] WHERE [jcr:path] LIKE '/testRoot/federated2'", Query.JCR_SQL2); assertEquals(0, query.execute().getNodes().getSize()); query = queryManager.createQuery("select * FROM [nt:base] WHERE [jcr:path] LIKE '/testRoot/federated2/federated3'", Query.JCR_SQL2); assertEquals(0, query.execute().getNodes().getSize()); Node externalNode = session.getNode("/testRoot/federated2/federated3"); externalNode.setProperty("test", "a value"); session.save(); query = queryManager.createQuery("select * FROM [nt:base] as a WHERE a.test='a value'", Query.JCR_SQL2); assertEquals(0, query.execute().getNodes().getSize()); } @Test public void shouldNotIndexNotQueryableDocument() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.NONT_QUERYABLE_DOC_LOCATION, "nonQueryableDoc"); Workspace workspace = session.getWorkspace(); workspace.reindex(); Node externalNode = assertNodeFound("/testRoot/nonQueryableDoc"); QueryManager queryManager = workspace.getQueryManager(); Query query = queryManager.createQuery("select * FROM [nt:base] WHERE [jcr:path] LIKE '/testRoot/nonQueryableDoc'", Query.JCR_SQL2); validateQuery().rowCount(0).validate(query, query.execute()); // change the document and re-run the query externalNode.setProperty("test", "a value"); session.save(); validateQuery().rowCount(0).validate(query, query.execute()); } @Test( expected = RepositoryException.class ) public void shouldNotAllowWritesIfReadonly() throws Exception { federationManager.createProjection("/testRoot", "mock-source-readonly", MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Node externalNode1 = doc1Federated.addNode("federated1_1", null); externalNode1.addNode("federated1_1_1", null); session.save(); } @Test @FixFor( "MODE-1964" ) public void shouldSendRemovedPropertiesToConnector() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "federated1"); Node doc1Federated = session.getNode("/testRoot/federated1"); Property doc1FederatedProperty = doc1Federated.getProperty("federated1_prop2"); doc1FederatedProperty.remove(); session.save(); try { ((Node)session.getNode("/testRoot/federated1")).getProperty("federated1_prop2"); fail("Property was not removed by connector"); } catch (PathNotFoundException e) { // exception } } @Test @FixFor( "MODE-1977" ) public void shouldNotAllowMoveIfSourceIsFederatedAndTargetIsNot() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); ((Node)session.getRootNode()).addNode("testRoot2"); session.save(); try { session.move("/testRoot", "/testRoot2"); fail("Should not allow move is source is federated is target is not."); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } try { session.move("/testRoot/fed1", "/testRoot2"); fail("Should not allow move is source is federated is target is not."); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1977" ) public void shouldNotAllowMoveIfSourceIsNotFederatedAndTargetIs() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); ((Node)session.getRootNode()).addNode("testRoot2"); session.save(); try { session.move("/testRoot2", "/testRoot/fed1"); fail("Should not allow move is source is not federated and target is"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1977" ) public void shouldAllowMoveIfSourceIsNotFederatedAndTargetIsNotFederated() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); ((Node)session.getRootNode()).addNode("testRoot2").addNode("a"); session.save(); session.move("/testRoot2", "/testRoot"); session.save(); assertNodeFound("/testRoot[1]/fed1"); assertNodeFound("/testRoot[2]/a"); } @Test @FixFor( "MODE-1977" ) public void shouldNotAllowMoveIfSourceAndTargetBelongToDifferentSources() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); federationManager.createProjection("/testRoot", "mock-source-non-queryable", MockConnector.DOC2_LOCATION, "fed2"); try { session.move("/testRoot/fed1", "/testRoot/fed2"); fail("Should not allow move if source and target don't belong to the same source"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1977" ) public void shouldNotAllowMoveIfSourceOrTargetIsProjection() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "fed2"); try { session.move("/testRoot/fed2/federated3", "/testRoot/fed1"); fail("Should not allow move if target is projection"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } try { session.move("/testRoot/fed2", "/testRoot/fed1"); fail("Should not allow move if source is projection"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1977" ) public void shouldAllowMoveWithSameSource() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); ((Node)session.getNode("/testRoot/fed1")).addNode("federated1"); session.save(); assertNodeFound("/testRoot/fed1/federated1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "fed2"); assertNodeFound("/testRoot/fed2/federated3"); session.move("/testRoot/fed1/federated1", "/testRoot/fed2/federated3"); session.save(); assertNodeFound("/testRoot/fed2/federated3[1]"); assertNodeFound("/testRoot/fed2/federated3[2]"); assertNodeNotFound("/testRoot/fed1/federated1"); } @Test @FixFor( "MODE-1976" ) public void shouldCopyFromFederatedSourceToNonFederatedTargetSameWs() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); Node federated1 = jcrSession().getNode("/testRoot/fed1").addNode("federated1"); federated1.setProperty("prop", "value"); jcrSession().getRootNode().addNode("testRoot2"); jcrSession().save(); jcrSession().getWorkspace().copy("/testRoot/fed1", "/testRoot2/fed1"); assertNodeFound("/testRoot2/fed1"); Node federated1Copy = assertNodeFound("/testRoot2/fed1/federated1"); federated1Copy.remove(); jcrSession().save(); assertNodeFound("/testRoot/fed1/federated1"); jcrSession().getRootNode().addNode("testRoot3"); jcrSession().save(); jcrSession().getWorkspace().copy("/testRoot/fed1/federated1", "/testRoot3"); federated1Copy = assertNodeFound("/testRoot3[2]"); assertNotNull(federated1Copy.getProperty("prop")); } @Test @FixFor( "MODE-1976" ) public void shouldCopyFromNonFederatedSourceToFederatedTargetSameWs() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); jcrSession().getNode("/testRoot/fed1").addNode("federated1"); jcrSession().getRootNode().addNode("testRoot2").addNode("nonFederated2"); jcrSession().save(); jcrSession().getWorkspace().copy("/testRoot2", "/testRoot/fed1/federated2"); assertNodeFound("/testRoot/fed1/federated2"); assertNodeFound("/testRoot/fed1/federated2/nonFederated2"); assertEquals(2, jcrSession().getNode("/testRoot/fed1").getNodes().getSize()); } @Test @FixFor( "MODE-1976" ) public void shouldNotCopyIfSourceAndTargetSourcesDoNotMatch() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); federationManager.createProjection("/testRoot", "mock-source-non-queryable", MockConnector.DOC2_LOCATION, "fed2"); try { jcrSession().getWorkspace().copy("/testRoot/fed1", "/testRoot/fed2/fed1"); fail("Should not allow copy if source and target don't belong to the same source"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1976" ) public void shouldNotCopyIfSourceSubgraphContainsExternalNodesWhichDoNotMatchTargetSource() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); federationManager.createProjection("/testRoot", "mock-source-non-queryable", MockConnector.DOC1_LOCATION, "fed_nq1"); federationManager.createProjection("/testRoot", "mock-source-non-queryable", MockConnector.DOC2_LOCATION, "fed2"); try { jcrSession().getWorkspace().copy("/testRoot", "/testRoot/fed2/fed_mixed"); fail("Should not allow copy if source subgraph contains nodes which don't belong to the same source as the target"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1976" ) public void shouldAllowCopyWithinSameSource() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "fed2"); jcrSession().getWorkspace().copy("/testRoot/fed1", "/testRoot/fed2/fed1"); assertNodeFound("/testRoot/fed2/fed1"); assertNodeFound("/testRoot/fed2/federated3"); } @Test @FixFor( "MODE-1975" ) public void shouldNotAllowCloneWithinTheSameWs() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); try { jcrSession().getWorkspace().clone(jcrSession().getWorkspace().getName(), "/testRoot", "/testRoot1", false); fail("Should not be able to clone in the same ws if external nodes are involved"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1975" ) public void shouldNotAllowMoveWithinTheSameWsViaClone() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); try { // clone with removeExisting = true in the same ws is a move jcrSession().getWorkspace().clone(jcrSession().getWorkspace().getName(), "/testRoot", "/testRoot1", true); fail("Should not be able to clone in the same ws if external nodes are involved"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } } @Test @FixFor( "MODE-1975" ) public void shouldAllowCloneOnlyIfEntireWsAreUsed() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC1_LOCATION, "fed1"); Session ws1Session = jcrSessionTo("ws1"); try { ws1Session.getWorkspace().clone("default", "/testRoot", "/testRoot", true); fail("Should only be able to clone between workspaces if the entire workspace is used"); } catch (RepositoryException e) { // expected if (print) { e.printStackTrace(); } } finally { ws1Session.logout(); } } @Test @FixFor( "MODE-1975" ) public void shouldCloneEntireWorkspaces() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "fed2"); Session ws1Session = jcrSessionTo("ws1"); try { ws1Session.getWorkspace().clone("default", "/", "/", true); assertNodeFound("/testRoot", ws1Session); Node fed2 = assertNodeFound("/testRoot/fed2", ws1Session); assertNodeFound("/testRoot/fed2/federated3", ws1Session); // add an external node in the 2nd workspace and check that it was added via the connector (i.e. the projection was // correctly cloned) fed2.addNode("federated2_1"); ws1Session.save(); //sleep a bit to make sure the events which clear the ws cache have reached the other session Thread.sleep(100L); assertNodeFound("/testRoot/fed2/federated2_1"); } finally { ws1Session.logout(); } } @Test @FixFor( "MODE-1975" ) public void shouldNotCloneEntireWorkspacesIfExternalNodesExist() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "fed2"); Session ws1Session = jcrSessionTo("ws1"); ws1Session.getRootNode().addNode("testRoot"); ws1Session.save(); ((Workspace)ws1Session.getWorkspace()).getFederationManager().createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "ws1Fed2"); try { ws1Session.getWorkspace().clone("default", "/", "/", false); fail("Expected an ItemExistsException because the target workspace already contains an external node"); } catch (ItemExistsException e) { // expected if (print) { e.printStackTrace(); } } finally { ws1Session.logout(); } } @Test public void shouldValidatePermissionsForReadonlyProjections() throws Exception { federationManager.createProjection("/testRoot", "mock-source-readonly", MockConnector.DOC1_LOCATION, "fed1"); federationManager.createProjection("/testRoot", "mock-source-readonly", MockConnector.DOC2_LOCATION, "fed2"); assertPermission(true, "/testRoot", ModeShapePermissions.ADD_NODE); assertPermission(true, "/testRoot", ModeShapePermissions.ADD_NODE, ModeShapePermissions.READ); assertPermission(true, "/testRoot", ModeShapePermissions.READ); assertPermission(false, "/testRoot/fed1", ModeShapePermissions.ADD_NODE); assertPermission(false, "/testRoot/fed1", ModeShapePermissions.ADD_NODE, ModeShapePermissions.READ); assertPermission(true, "/testRoot/fed1", ModeShapePermissions.READ); assertPermission(true, "/testRoot/fed1", ModeShapePermissions.INDEX_WORKSPACE); assertPermission(true, "/testRoot/fed1", ModeShapePermissions.INDEX_WORKSPACE, ModeShapePermissions.READ); assertPermission(false, "/testRoot/fed2", ModeShapePermissions.ADD_NODE); assertPermission(false, "/testRoot/fed2", ModeShapePermissions.ADD_NODE, ModeShapePermissions.READ); assertPermission(true, "/testRoot/fed2", ModeShapePermissions.READ); assertPermission(true, "/testRoot/fed2", ModeShapePermissions.INDEX_WORKSPACE); assertPermission(true, "/testRoot/fed2", ModeShapePermissions.INDEX_WORKSPACE, ModeShapePermissions.READ); assertPermission(false, "/testRoot/fed2/federated3", ModeShapePermissions.ADD_NODE); assertPermission(false, "/testRoot/fed2/federated3", ModeShapePermissions.ADD_NODE, ModeShapePermissions.READ); assertPermission(true, "/testRoot/fed2/federated3", ModeShapePermissions.READ); assertPermission(true, "/testRoot/fed2/federated3", ModeShapePermissions.INDEX_WORKSPACE); assertPermission(true, "/testRoot/fed2/federated3", ModeShapePermissions.INDEX_WORKSPACE, ModeShapePermissions.READ); } @Test @FixFor( "MODE-2147" ) public void shouldNotAllowVersionableMixinOnExternalNodes() throws Exception { federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "fed1"); Node projectionRoot = session.getNode("/testRoot/fed1"); try { projectionRoot.addMixin("mix:versionable"); fail("Should not allow versionable mixin on external nodes"); } catch (ConstraintViolationException e) { // expected } try { Node externalChild = projectionRoot.addNode("child"); externalChild.addMixin("mix:versionable"); session.save(); fail("Should not allow versionable mixin on external nodes"); } catch (ConstraintViolationException e) { // expected } Node externalChild = projectionRoot.addNode("child"); assertThat(externalChild, is(notNullValue())); session.save(); try { ((Node)session.getNode("/testRoot/child")).addMixin("mix:versionable"); fail("Should not allow versionable mixin on external nodes"); } catch (RepositoryException e) { // expected } } @Test public void shouldNotAllowACLsOnExternalNodes() throws Exception { AccessControlManager acm = session.getAccessControlManager(); federationManager.createProjection("/testRoot", SOURCE_NAME, MockConnector.DOC2_LOCATION, "fed1"); session.getNode("/testRoot/fed1"); AccessControlList acl = acl("/testRoot/fed1"); try { acm.setPolicy("/testRoot/fed1", acl); fail("Should not allow ACLs on external nodes"); } catch (RepositoryException e) { // expected } } private void assertPermission( boolean shouldHave, String absPath, String... actions ) throws RepositoryException { StringBuilder actionsBuilder = new StringBuilder(); List<String> actionsList = new ArrayList<String>(Arrays.asList(actions)); for (Iterator<String> actionsIterator = actionsList.iterator(); actionsIterator.hasNext();) { actionsBuilder.append(actionsIterator.next()); if (actionsIterator.hasNext()) { actionsBuilder.append(","); } } String actionsString = actionsBuilder.toString(); if (shouldHave) { assertTrue(session.hasPermission(absPath, actionsString)); session.checkPermission(absPath, actionsString); } else { assertFalse(session.hasPermission(absPath, actionsString)); try { session.checkPermission(absPath, actionsString); fail("There permissions " + actionsString + " should not be valid on " + absPath); } catch (AccessControlException e) { // expected } } } private void assertExternalNodeHasChildren( String externalNodePath, String... children ) throws Exception { Node externalNode = session.getNode(externalNodePath); NodeIterator childNodes = externalNode.getNodes(); if (children.length == 0) { assertEquals(0, childNodes.getSize()); return; } List<String> actualNodes = new ArrayList<String>((int)childNodes.getSize()); while (childNodes.hasNext()) { actualNodes.add(childNodes.nextNode().getName()); } assertEquals(Arrays.asList(children), actualNodes); } private void assertNodeNotFound( String absPath ) throws RepositoryException { try { session.getNode(absPath); fail("Node at " + absPath + " should not exist"); } catch (PathNotFoundException e) { // expected } } private Node assertNodeFound( String absPath ) throws RepositoryException { return assertNodeFound(absPath, session); } private Node assertNodeFound( String absPath, Session session ) throws RepositoryException { Node node = session.getNode(absPath); assertNotNull(node); return node; } private Session jcrSessionTo( String wsName ) throws RepositoryException { return repository.login(wsName); } }