/* * 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.assertArrayEquals; 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 java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.jcr.ImportUUIDBehavior; import javax.jcr.ItemExistsException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.security.AccessControlList; import javax.jcr.security.AccessControlManager; import javax.jcr.security.Privilege; import javax.jcr.version.Version; import org.junit.Test; import org.modeshape.common.FixFor; import org.modeshape.common.junit.SkipLongRunning; import org.modeshape.common.util.FileUtil; import org.modeshape.jcr.api.Binary; import org.modeshape.jcr.api.JcrTools; import org.modeshape.jcr.api.Workspace; import org.modeshape.jcr.security.SimplePrincipal; import org.modeshape.jcr.value.Name; import org.modeshape.jcr.value.Path; /** * Tests of round-trip importing/exporting of repository content. */ public class ImportExportTest extends SingleUseAbstractTest { private enum ExportType { SYSTEM, DOCUMENT } private static final String BAD_CHARACTER_STRING = "Test & <Test>*"; private String expectedIdentifier = "e41075cb-a09a-4910-87b1-90ce8b4ca9dd"; private void testImportExport( String sourcePath, String targetPath, ExportType useSystemView, boolean skipBinary, boolean noRecurse, boolean useWorkspace ) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (useSystemView == ExportType.SYSTEM) { session.exportSystemView(sourcePath, baos, skipBinary, noRecurse); } else { session.exportDocumentView(sourcePath, baos, skipBinary, noRecurse); } ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); if (useWorkspace) { // import via workspace ... session.getWorkspace().importXML(targetPath, bais, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); } else { // import via session ... session.importXML(targetPath, bais, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); } } @Test public void shouldImportExportEscapedXmlCharactersInSystemViewUsingSession() throws Exception { String testName = "importExportEscapedXmlCharacters"; Node rootNode = session.getRootNode(); Node sourceNode = rootNode.addNode(testName + "Source", "nt:unstructured"); Node targetNode = rootNode.addNode(testName + "Target", "nt:unstructured"); // Test data sourceNode.setProperty("badcharacters", BAD_CHARACTER_STRING); assertThat(sourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING)); sourceNode.addNode(BAD_CHARACTER_STRING); testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.SYSTEM, false, false, false); Node newSourceNode = targetNode.getNode(testName + "Source"); newSourceNode.getNode(BAD_CHARACTER_STRING); assertThat(newSourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING)); } @Test public void shouldImportExportEscapedXmlCharactersInSystemViewUsingWorkspace() throws Exception { String testName = "importExportEscapedXmlCharacters"; Node rootNode = session.getRootNode(); Node sourceNode = rootNode.addNode(testName + "Source", "nt:unstructured"); Node targetNode = rootNode.addNode(testName + "Target", "nt:unstructured"); // Test data sourceNode.setProperty("badcharacters", BAD_CHARACTER_STRING); assertThat(sourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING)); sourceNode.addNode(BAD_CHARACTER_STRING); session.save(); testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.SYSTEM, false, false, true); Node newSourceNode = targetNode.getNode(testName + "Source"); newSourceNode.getNode(BAD_CHARACTER_STRING); assertThat(newSourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING)); } @FixFor("MODE-1405") @Test public void shouldImportProtectedContentUsingWorkpace() throws Exception { String testName = "importExportEscapedXmlCharacters"; Node rootNode = session.getRootNode(); Node sourceNode = rootNode.addNode(testName + "Source", "nt:unstructured"); Node targetNode = rootNode.addNode(testName + "Target", "nt:unstructured"); // Test data Node child = sourceNode.addNode("child"); child.addMixin("mix:created"); session.save(); // Verify there are 'jcr:createdBy' and 'jcr:created' properties ... assertThat(child.getProperty("jcr:createdBy").getString(), is(notNullValue())); assertThat(child.getProperty("jcr:created").getString(), is(notNullValue())); testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.SYSTEM, false, false, true); Node newSourceNode = targetNode.getNode(testName + "Source"); Node newChild = newSourceNode.getNode("child"); // Verify there are 'jcr:createdBy' and 'jcr:created' properties ... assertThat(newChild.getProperty("jcr:createdBy").getString(), is(notNullValue())); assertThat(newChild.getProperty("jcr:created").getString(), is(notNullValue())); } @Test public void shouldImportExportEscapedXmlCharactersInDocumentViewUsingSession() throws Exception { String testName = "importExportEscapedXmlCharacters"; Node rootNode = session.getRootNode(); Node sourceNode = rootNode.addNode(testName + "Source", "nt:unstructured"); Node targetNode = rootNode.addNode(testName + "Target", "nt:unstructured"); // Test data sourceNode.setProperty("badcharacters", BAD_CHARACTER_STRING); assertThat(sourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING)); sourceNode.addNode(BAD_CHARACTER_STRING); testImportExport(sourceNode.getPath(), targetNode.getPath(), ExportType.DOCUMENT, false, false, false); Node newSourceNode = targetNode.getNode(testName + "Source"); newSourceNode.getNode(BAD_CHARACTER_STRING); assertThat(newSourceNode.getProperty("badcharacters").getString(), is(BAD_CHARACTER_STRING)); } @Test public void shouldImportSystemViewWithUuidsAfterNodesWithSameUuidsAreDeletedInSessionAndSaved() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Create the node under which the content will be imported ... session.getRootNode().addNode("/someNode"); session.save(); // Import the car content ... importFile("/someNode", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); session.save(); // tools.printSubgraph(assertNode("/")); // Now delete the '/someNode/Cars' node (which is everything that was imported) ... Node cars = assertNode("/someNode/Cars"); assertThat(cars.getIdentifier(), is(expectedIdentifier)); assertNoNode("/someNode/Cars[2]"); assertNoNode("/someNode[2]"); cars.remove(); session.save(); // Now import again ... importFile("/someNode", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); session.save(); // Verify the same Cars node exists ... cars = assertNode("/someNode/Cars"); assertThat(cars.getIdentifier(), is(expectedIdentifier)); assertNoNode("/someNode/Cars[2]"); assertNoNode("/someNode[2]"); } @Test public void shouldImportSystemViewWithUuidsAfterNodesWithSameUuidsAreDeletedInSessionButNotSaved() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Create the node under which the content will be imported ... session.getRootNode().addNode("/someNode"); session.save(); // Import the car content ... importFile("/someNode", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); session.save(); // Now delete the '/someNode/Cars' node (which is everything that was imported) ... Node cars = assertNode("/someNode/Cars"); assertThat(cars.getIdentifier(), is(expectedIdentifier)); assertNoNode("/someNode/Cars[2]"); assertNoNode("/someNode[2]"); cars.remove(); assertNoNode("/someNode/Cars"); // session.save(); // DO NOT SAVE // Now import again ... importFile("/someNode", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); session.save(); // Verify the same Cars node exists ... cars = assertNode("/someNode/Cars"); assertThat(cars.getIdentifier(), is(expectedIdentifier)); assertNoNode("/someNode/Cars[2]"); assertNoNode("/someNode[2]"); } @Test public void shouldImportSystemViewWithUuidsIntoDifferentSpotAfterNodesWithSameUuidsAreDeletedInSessionButNotSaved() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Create the node under which the content will be imported ... Node someNode = session.getRootNode().addNode("/someNode"); session.getRootNode().addNode("/otherNode"); session.save(); // Import the car content ... importFile("/someNode", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); session.save(); // Now delete the '/someNode/Cars' node (which is everything that was imported) ... Node cars = assertNode("/someNode/Cars"); assertThat(cars.getIdentifier(), is(expectedIdentifier)); assertNoNode("/someNode/Cars[2]"); assertNoNode("/someNode[2]"); cars.remove(); // Now create a node at the same spot as cars, but with a different UUID ... Node newCars = someNode.addNode("Cars"); assertThat(newCars.getIdentifier(), is(not(expectedIdentifier))); // session.save(); // DO NOT SAVE // Now import again ... importFile("/otherNode", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); session.save(); // Verify the same Cars node exists ... cars = assertNode("/otherNode/Cars"); assertThat(cars.getIdentifier(), is(expectedIdentifier)); // Make sure some duplicate nodes didn't show up ... assertNoNode("/sameNode/Cars[2]"); assertNoNode("/sameNode[2]"); assertNoNode("/otherNode/Cars[2]"); assertNoNode("/otherNode[2]"); } @FixFor("MODE-1137") @Test public void shouldExportContentWithUnicodeCharactersAsDocumentView() throws Exception { Node unicode = session.getRootNode().addNode("unicodeContent"); Node desc = unicode.addNode("descriptionNode"); desc.setProperty("ex1", "étudiant (student)"); desc.setProperty("ex2", "où (where)"); desc.setProperty("ex3", "forêt (forest)"); desc.setProperty("ex4", "naïve (naïve)"); desc.setProperty("ex5", "garçon (boy)"); desc.setProperty("ex6", "multi\nline\nvalue"); desc.setProperty("ex7", "prop \"value\" with quotes"); desc.setProperty("ex7", "values with \r various \t\n : characters"); session.save(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); session.exportDocumentView("/unicodeContent", baos, false, false); baos.close(); predefineWorkspace(repository.getConfiguration(), "workspace2"); InputStream istream = new ByteArrayInputStream(baos.toByteArray()); Session session2 = repository.login("workspace2"); session2.getWorkspace().importXML("/", istream, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); Node desc2 = session2.getNode("/unicodeContent/descriptionNode"); assertSameProperties(desc, desc2); } @FixFor("MODE-1137") @Test public void shouldExportContentWithUnicodeCharactersAsSystemView() throws Exception { Node unicode = session.getRootNode().addNode("unicodeContent"); Node desc = unicode.addNode("descriptionNode"); desc.setProperty("ex1", "étudiant (student)"); desc.setProperty("ex2", "où (where)"); desc.setProperty("ex3", "forêt (forest)"); desc.setProperty("ex4", "naïve (naïve)"); desc.setProperty("ex5", "garçon (boy)"); desc.setProperty("ex6", "multi\nline\nvalue"); desc.setProperty("ex7", "prop \"value\" with quotes"); desc.setProperty("ex7", "values with \n various \t\n : characters"); session.save(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); session.exportSystemView("/unicodeContent", baos, false, false); baos.close(); predefineWorkspace(repository.getConfiguration(), "workspace2"); InputStream istream = new ByteArrayInputStream(baos.toByteArray()); Session session2 = repository.login("workspace2"); session2.getWorkspace().importXML("/", istream, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); Node desc2 = session2.getNode("/unicodeContent/descriptionNode"); assertSameProperties(desc, desc2); } @Test public void shouldImportCarsSystemViewWithCreateNewBehaviorWhenImportedContentDoesNotContainJcrRoot() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); assertImport("io/cars-system-view.xml", "/a/b", ImportBehavior.CREATE_NEW); assertThat(session, is(notNullValue())); assertCarsImported(); } @Test public void shouldImportCarsSystemViewWithRemoveExistingBehaviorWhenImportedContentDoesNotContainJcrRoot() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); assertImport("io/cars-system-view.xml", "/a/b", ImportBehavior.REMOVE_EXISTING); assertThat(session, is(notNullValue())); assertCarsImported(); } @Test public void shouldImportCarsSystemViewWithReplaceExistingBehaviorWhenImportedContentDoesNotContainJcrRoot() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); assertImport("io/cars-system-view.xml", "/a/b", ImportBehavior.REPLACE_EXISTING); assertThat(session, is(notNullValue())); assertCarsImported(); } @Test public void shouldImportCarsSystemViewWithRemoveExistingBehaviorWhenImportedContentDoesNotContainJcrRootOrAnyUuids() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository with existing content ... assertImport("io/cars-system-view.xml", "/a/b", ImportBehavior.CREATE_NEW); assertCarsImported(); // Now import again to create a second copy ... assertImport("io/cars-system-view.xml", "/a/b", ImportBehavior.REMOVE_EXISTING); assertThat(session, is(notNullValue())); assertNode("/a/b/Cars"); assertNode("/a/b/Cars/Hybrid"); assertNode("/a/b/Cars/Hybrid/Toyota Prius"); assertNode("/a/b/Cars/Sports/Infiniti G37"); assertNode("/a/b/Cars/Utility/Land Rover LR3"); assertNode("/a/b/Cars[2]"); assertNode("/a/b/Cars[2]/Hybrid"); assertNode("/a/b/Cars[2]/Hybrid/Toyota Prius"); assertNode("/a/b/Cars[2]/Sports/Infiniti G37"); assertNode("/a/b/Cars[2]/Utility/Land Rover LR3"); } // ---------------------------------------------------------------------------------------------------------------- // Import System View with NO 'jcr:root' node but WITH uuids // ---------------------------------------------------------------------------------------------------------------- @Test public void shouldImportCarsSystemViewWithCreateNewBehaviorWhenImportedContentDoesNotContainJcrRootButDoesContainUnusedUuids() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository with existing content ... assertImport("io/cars-system-view.xml", "/a/b", ImportBehavior.CREATE_NEW); assertCarsImported(); // And attempt to reimport the same content (with UUIDs) into the repository that already has that content ... // print = true; assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.REPLACE_EXISTING); print(); // Verify that the original content has been left untouched ... assertThat(session, is(notNullValue())); assertNode("/a/b/Cars"); assertNode("/a/b/Cars/Hybrid"); assertNode("/a/b/Cars/Hybrid/Toyota Prius"); assertNode("/a/b/Cars/Sports/Infiniti G37"); assertNode("/a/b/Cars/Utility/Land Rover LR3"); assertNode("/a/b/Cars[2]"); assertNode("/a/b/Cars[2]/Hybrid"); assertNode("/a/b/Cars[2]/Hybrid/Toyota Prius"); assertNode("/a/b/Cars[2]/Sports"); } @Test public void shouldImportCarsSystemViewWithCreateNewBehaviorWhenImportedContentDoesNotContainJcrRootButDoesContainAlreadyUsedUuids() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository with existing content ... assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.CREATE_NEW); assertCarsImported(); // And attempt to reimport the same content (with UUIDs) into the repository that already has that content ... // print = true; assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.CREATE_NEW); print(); // Verify that the original content has been left untouched ... assertThat(session, is(notNullValue())); assertNode("/a/b/Cars"); assertNode("/a/b/Cars/Hybrid"); assertNode("/a/b/Cars/Hybrid/Toyota Prius"); assertNode("/a/b/Cars/Sports/Infiniti G37"); assertNode("/a/b/Cars/Utility/Land Rover LR3"); assertNode("/a/b/Cars[2]"); assertNode("/a/b/Cars[2]/Hybrid"); assertNode("/a/b/Cars[2]/Hybrid/Toyota Prius"); assertNode("/a/b/Cars[2]/Sports"); } @Test public void shouldImportCarsSystemViewOverExistingContentWhenImportedContentDoesNotContainJcrRootButDoesContainAlreadyUsedUuids() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository with existing content ... assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.CREATE_NEW); assertCarsImported(); // And attempt to reimport the same content (with UUIDs) into the repository that already has that content ... // print = true; assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.REMOVE_EXISTING); print(); // Verify that the original content has been replaced (since the SystemView contained UUIDs) and there is no copy ... assertThat(session, is(notNullValue())); assertCarsImported(); // And attempt to reimport the same content (with UUIDs) into the repository that already has that content ... // print = true; assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.REPLACE_EXISTING); print(); // Verify that the original content has been replaced (since the SystemView contained UUIDs) and there is no copy ... assertThat(session, is(notNullValue())); assertCarsImported(); } @Test public void shouldImportCarsSystemViewWhenImportedContentDoesNotContainJcrRootButDoesContainAlreadyUsedUuids() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository with existing content ... assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.CREATE_NEW); assertCarsImported(); // And attempt to reimport the same content (with UUIDs) into the repository that already has that content ... // print = true; assertImport("io/cars-system-view-with-uuids.xml", "/a/c", ImportBehavior.REPLACE_EXISTING); print(); // Verify that the original content has been replaced (since the SystemView contained UUIDs) and there is no copy ... assertThat(session, is(notNullValue())); assertNode("/a/b"); assertNode("/a/c"); assertCarsImported(); // And attempt to reimport the same content (with UUIDs) into the repository that already has that content ... // print = true; assertImport("io/cars-system-view-with-uuids.xml", "/a/d", ImportBehavior.REMOVE_EXISTING); print(); // Verify that the original content has been replaced (since the SystemView contained UUIDs) and there is no copy ... assertThat(session, is(notNullValue())); assertNode("/a/b"); assertNode("/a/c"); assertNode("/a/d/Cars"); assertNode("/a/d/Cars/Hybrid"); assertNode("/a/d/Cars/Hybrid/Toyota Prius"); assertNode("/a/d/Cars/Sports/Infiniti G37"); assertNode("/a/d/Cars/Utility/Land Rover LR3"); assertNoNode("/a/b/Cars[2]"); assertNoNode("/a/b/Cars/Hybrid[2]"); assertNoNode("/a/b/Cars/Hybrid/Toyota Prius[2]"); assertNoNode("/a/b/Cars/Sports[2]"); } @Test(expected = ItemExistsException.class) public void shouldFailToImportCarsSystemViewWithThrowBehaviorWhenImportedContentDoesNotContainJcrRootButDoesContainAlreadyUsedUuids() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository with existing content ... assertImport("io/cars-system-view-with-uuids.xml", "/a/b", ImportBehavior.CREATE_NEW); assertCarsImported(); // And attempt to reimport the same content (with UUIDs) into the repository that already has that content ... // print = true; assertImport("io/cars-system-view-with-uuids.xml", "/a/c", ImportBehavior.THROW); } // ---------------------------------------------------------------------------------------------------------------- // Import System View WITH 'jcr:root' node and NO matching uuids // ---------------------------------------------------------------------------------------------------------------- @Test public void shouldImportSystemViewOfEntireWorkspaceWithNoAlreadyUsedUuids() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository ... assertImport("io/full-workspace-system-view-with-uuids.xml", "/", ImportBehavior.THROW); // no matching UUIDs expected assertCarsImported(); } @Test public void shouldImportSystemViewOfEntireWorkspaceExportedFromJackrabbit() throws Exception { // Register the Cars node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository ... assertImport("io/full-workspace-system-view.xml", "/", ImportBehavior.THROW); // no matching UUIDs expected assertNode("/page1"); } @Test public void shouldImportFileExportedFromJackrabbitContainingBinaryData() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cars.cnd"); tools.registerNodeTypes(session, "cnd/magnolia.cnd"); assertThat(session.getWorkspace().getNodeTypeManager().getNodeType("mgnl:content"), is(notNullValue())); // Now import the file ... String filename = "io/system-export-with-binary-data-and-uuids.xml"; assertImport(filename, "/a", ImportBehavior.THROW); // no matching UUIDs expected } @FixFor("MODE-1026") @Test public void shouldImportFileExportedFromJackrabbitContainingBinaryStringData() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cars.cnd"); tools.registerNodeTypes(session, "cnd/magnolia.cnd"); assertThat(session.getWorkspace().getNodeTypeManager().getNodeType("mgnl:content"), is(notNullValue())); // Now import the file ... String filename = "io/system-export-with-xsitype-data-and-uuids.xml"; assertImport(filename, "/a", ImportBehavior.THROW); // no matching UUIDs expected // print = true; print("/a"); Node imageNode = assertNode("/a/company/image"); assertThat(imageNode.getProperty("extension").getValue().getString(), is("gif")); } @Test public void shouldDecodeBase64() throws Exception { String base64Str = "R0lGODlhEAAQAMZpAGxZMW1bNW9bMm9cNnJdNXJdNnNfN3tnPX5oQIBqQYJrO4FrQoVtQIZuQohxQopyRopzQYtzRo10Qo51SI12SJB3SZN5Q5N5RpV7TJZ8TJd9SJh+TpyAT52CUJ+FUaOGU6OHUaSIUqGKUqaJVaiKVaGMV6mLVqWOXqyOVayOWLCSWa2VWrSVW7aXXbSbXbqaXrqaX7uaX7ubXsCfYrigcMKgY8OhY8SiZMWjZMWjZcelZsimZsqnZ8unZ8uoaMypaM2pac6qacOtbc+ratCsatKta8uwbdGvctOvbtSvbNWwbciyhdaxbdm2dda7ddq5gd26fN28gNe/ed6+htvCeuHAhd3EfOLCidrDmd7GfuLEj9/HfubKlufLmOjLmOnMmOnNmujNne3UpuzUqe3Vp+7VqO/VqO/Wqe7YsP///////////////////////////////////////////////////////////////////////////////////////////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAH8ALAAAAAAQABAAAAeJgH+Cg4SFhoeIiYqLhSciiR40S1hoY0JZVE5GK4MOZWdmZGJhJVumW1aDFGBfXl1cWiRSp1sufxYXV1VQUVNPMSAYDwgHEINNTEpJSEcwKR0VCwWERURDQUA9LSYcEwkDhD8+PDs6OCwjGxEIAYQyOTc2NTMqHxkNBgCFChIaKC8hGBBgJIARo0AAOw=="; boolean print = false; // // Try apache ... // byte[] apacheBytes = org.apache.util.Base64.decode(base64Str.getBytes("UTF-8")); // if (print) { // System.out.println("Apache: " + toString(apacheBytes)); // System.out.println(" length: " + apacheBytes.length); // } // Try jboss ... byte[] jbossBytes = org.jboss.util.Base64.decode(base64Str); if (print) { System.out.println("JBoss: " + toString(jbossBytes)); System.out.println(" length: " + jbossBytes.length); } // Try jackrabbit ... ByteArrayOutputStream jrOutput = new ByteArrayOutputStream(); org.apache.jackrabbit.test.api.Base64.decode(base64Str, jrOutput); byte[] jrBytes = jrOutput.toByteArray(); if (print) { System.out.println("Jackrabbit: " + toString(jrBytes)); System.out.println(" length: " + jrBytes.length); } // Try modeshape ... byte[] msBytes = org.modeshape.common.util.Base64.decode(base64Str); if (print) { System.out.println("ModeShape: " + toString(msBytes)); System.out.println(" length: " + msBytes.length); } // assertThat(apacheBytes, is(jbossBytes)); // apache pads 3 0s at the end assertThat(jrBytes, is(jbossBytes)); assertThat(msBytes, is(jbossBytes)); } String toString( byte[] bytes ) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(b); } return sb.toString(); } // ---------------------------------------------------------------------------------------------------------------- // Import Document and System View containing constraint violations // ---------------------------------------------------------------------------------------------------------------- @FixFor("MODE-1139") @Test(expected = ConstraintViolationException.class) public void shouldThrowCorrectExceptionWhenImportingDocumentViewContainingPropertiesThatViolateConstraints() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository ... assertImport("io/full-workspace-document-view-with-constraint-violation.xml", "/", ImportBehavior.THROW); } @FixFor("MODE-1139") @Test(expected = ConstraintViolationException.class) public void shouldThrowCorrectExceptionWhenImportingSystemViewContainingPropertiesThatViolateConstraints() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository ... assertImport("io/full-workspace-system-view-with-constraint-violation.xml", "/", ImportBehavior.THROW); } // ---------------------------------------------------------------------------------------------------------------- // Import Document View WITH 'jcr:root' node and WITH uuids // ---------------------------------------------------------------------------------------------------------------- @Test public void shouldImportDocumentViewWithUuids() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository ... assertImport("io/full-workspace-document-view-with-uuids.xml", "/", ImportBehavior.THROW); // no matching UUIDs expected assertCarsImported(); } // ---------------------------------------------------------------------------------------------------------------- // Import Document View WITH constraints // ---------------------------------------------------------------------------------------------------------------- @Test public void shouldImportDocumentViewWithReferenceConstraints() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository ... assertImport("io/full-workspace-document-view-with-uuids.xml", "/", ImportBehavior.THROW); // no matching UUIDs expected assertCarsImported(); } @FixFor("MODE-1171") @Test public void shouldExportAndImportMultiValuedPropertyWithSingleValue() throws Exception { Node rootNode = session.getRootNode(); Node nodeA = rootNode.addNode("a", "nt:unstructured"); Node nodeB = nodeA.addNode("b", "nt:unstructured"); Value v = session.getValueFactory().createValue("singleValue"); javax.jcr.Property prop = nodeB.setProperty("multiValuedProp", new Value[] { v }); assertTrue(prop.isMultiple()); prop = nodeB.setProperty("singleValuedProp", v); assertTrue(!prop.isMultiple()); session.save(); File exportFile = exportSystemView("/a"); try { nodeA.remove(); session.save(); assertImport(exportFile, "/", ImportBehavior.THROW); prop = session.getProperty("/a/b/multiValuedProp"); assertTrue(prop.isMultiple()); assertThat(prop.getValues().length, is(1)); assertThat(prop.getValues()[0].getString(), is("singleValue")); prop = session.getProperty("/a/b/singleValuedProp"); assertTrue(!prop.isMultiple()); assertThat(prop.getString(), is("singleValue")); } finally { exportFile.delete(); } } @Test public void shouldImportIntoWorkspaceTheDocumentViewOfTheContentUsedInTckTests() throws Exception { Session session3 = repository.login(); session.nodeTypeManager().registerNodeTypes(resourceStream("tck/tck_test_types.cnd"), true); session.getWorkspace().importXML("/", resourceStream("tck/documentViewForTckTests.xml"), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); assertThat(session.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session.getNode("/testroot/workarea"), is(notNullValue())); assertNode("/testroot/workarea"); Session session1 = repository.login(); assertThat(session1.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session1.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session1.getNode("/testroot/workarea"), is(notNullValue())); session1.logout(); assertThat(session3.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session3.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session3.getNode("/testroot/workarea"), is(notNullValue())); session3.logout(); } @Test public void shouldImportIntoWorkspaceTheSystemViewOfTheContentUsedInTckTests() throws Exception { Session session3 = repository.login(); session.nodeTypeManager().registerNodeTypes(resourceStream("tck/tck_test_types.cnd"), true); session.getWorkspace().importXML("/", resourceStream("tck/systemViewForTckTests.xml"), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); assertThat(session.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertNode("/testroot/workarea"); Session session1 = repository.login(); assertThat(session1.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session1.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session1.getNode("/testroot/workarea"), is(notNullValue())); session1.logout(); assertThat(session3.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session3.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session3.getNode("/testroot/workarea"), is(notNullValue())); session3.logout(); } @Test public void shouldImportIntoSessionTheDocumentViewOfTheContentUsedInTckTests() throws Exception { Session session3 = repository.login(); session.nodeTypeManager().registerNodeTypes(resourceStream("tck/tck_test_types.cnd"), true); session.importXML("/", resourceStream("tck/documentViewForTckTests.xml"), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); assertThat(session.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session.getNode("/testroot/workarea"), is(notNullValue())); assertNode("/testroot/workarea"); Session session1 = repository.login(); assertThat(session1.getRootNode().hasNode("testroot/workarea"), is(false)); session.save(); assertThat(session1.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session1.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session1.getNode("/testroot/workarea"), is(notNullValue())); session1.logout(); assertThat(session3.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session3.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session3.getNode("/testroot/workarea"), is(notNullValue())); session3.logout(); } @Test public void shouldImportIntoSessionTheSystemViewOfTheContentUsedInTckTests() throws Exception { Session session3 = repository.login(); session.nodeTypeManager().registerNodeTypes(resourceStream("tck/tck_test_types.cnd"), true); session.importXML("/", resourceStream("tck/systemViewForTckTests.xml"), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); assertThat(session.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertNode("/testroot/workarea"); Session session1 = repository.login(); assertThat(session1.getRootNode().hasNode("testroot/workarea"), is(false)); session.save(); assertThat(session1.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session1.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session1.getNode("/testroot/workarea"), is(notNullValue())); session1.logout(); assertThat(session3.getRootNode().hasNode("testroot/workarea"), is(true)); assertThat(session3.getRootNode().getNode("testroot/workarea"), is(notNullValue())); assertThat(session3.getNode("/testroot/workarea"), is(notNullValue())); session3.logout(); } @Test @FixFor("MODE-1573") public void shouldPerformRoundTripOnDocumentViewWithBinaryContent() throws Exception { JcrTools tools = new JcrTools(); File binaryFile = new File("src/test/resources/io/binary.pdf"); assert (binaryFile.exists() && binaryFile.isFile()); File outputFile = File.createTempFile("modeshape_import_export_" + System.currentTimeMillis(), "_test"); outputFile.deleteOnExit(); tools.uploadFile(session, "file", binaryFile); session.save(); session.exportDocumentView("/file", new FileOutputStream(outputFile), false, false); assertTrue(outputFile.length() > 0); session.getRootNode().getNode("file").remove(); session.save(); //sleep so that the binary can be properly cleaned up (this is done via a listener) Thread.sleep(200); session.getWorkspace().importXML("/", new FileInputStream(outputFile), ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); assertNotNull(session.getNode("/file")); assertNotNull(session.getNode("/file/jcr:content")); Property data = session.getNode("/file/jcr:content").getProperty("jcr:data"); assertNotNull(data); Binary binary = (Binary)data.getBinary(); assertNotNull(binary); assertEquals(binaryFile.length(), binary.getSize()); } @FixFor("MODE-1478") @Test public void shouldBeAbleToImportDroolsXMLIntoSystemView() throws Exception { startRepositoryWithConfigurationFrom("config/drools-repository.json"); session.importXML("/", resourceStream("io/drools-system-view.xml"), ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); assertNode("/drools:repository", "nt:folder"); assertNode("/drools:repository/drools:package_area", "nt:folder"); assertNode("/drools:repository/drools:package_area/defaultPackage", "drools:packageNodeType"); assertNode("/drools:repository/drools:package_area/defaultPackage/assets", "drools:versionableAssetFolder"); assertNode("/drools:repository/drools:package_area/defaultPackage/assets/drools", "drools:assetNodeType"); assertNode("/drools:repository/drools:packagesnapshot_area", "nt:folder"); assertNode("/drools:repository/drools:tag_area", "nt:folder"); assertNode("/drools:repository/drools:state_area", "nt:folder"); assertNode("/drools:repository/drools.package.migrated", "nt:folder"); } @FixFor("MODE-1795") @Test public void shouldBeAbleToImportXmlFileThatUsesDefaultNamespaceWithNonBlankUri() throws Exception { session.importXML("/", resourceStream("io/simple-document-view-with-default-namespace.xml"), ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); // Get the prefix for the namespace used in the imported file ... String prefix = session.getWorkspace().getNamespaceRegistry().getPrefix("http://www.ns.com"); assertNode("/" + prefix + ":childNode", "nt:unstructured"); } @FixFor("MODE-1945") @Test public void shouldBeAbleToImportDocumentViewTwiceWithRemoveExistingCollisionMode() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cars.cnd"); // Set up the repository ... assertImport("io/full-workspace-document-view-with-uuids.xml", "/", ImportBehavior.REMOVE_EXISTING); assertCarsImported(); assertImport("io/full-workspace-document-view-with-uuids.xml", "/", ImportBehavior.REMOVE_EXISTING); assertCarsImported(); } @FixFor("MODE-1945") @Test public void shouldBeAbleToImportSystemViewWithBinaryTwiceWithRemoveExistingCollisionMode2() throws Exception { // Register the node types ... tools.registerNodeTypes(session, "cnd/magnolia.cnd"); // Now import the file ... assertImport("io/system-export-with-binary-data-and-uuids.xml", "/", ImportBehavior.REMOVE_EXISTING); // no matching UUIDs // expected assertImport("io/system-export-with-binary-data-and-uuids.xml", "/", ImportBehavior.REMOVE_EXISTING); // no matching UUIDs // expected } @Test @FixFor("MODE-1961") public void shouldBeAbleToImportTwiceWithoutLoosingMixins() throws Exception { tools.registerNodeTypes(session, "cnd/brix.cnd"); session.save(); InputStream brixWorkspace = resourceStream("io/brixWorkspace.xml"); session.importXML("/", brixWorkspace, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); session.save(); JcrNode root = (JcrNode)session.getItem("/brix:root"); Set<Name> rootMixins = root.getMixinTypeNames(); assertTrue(rootMixins.contains(session.nameFactory().create("brix:node"))); brixWorkspace = resourceStream("io/brixWorkspace.xml"); session.importXML("/", brixWorkspace, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); session.save(); root = (JcrNode)session.getItem("/brix:root"); rootMixins = root.getMixinTypeNames(); assertTrue(rootMixins.contains(session.nameFactory().create("brix:node"))); } @Test @FixFor("MODE-2039") public void shouldImportVersionedSystemView() throws Exception { assertImport("io/system-export-with-versioning.xml", "/", ImportBehavior.REMOVE_EXISTING); // no matching UUIDs expected } @Test @FixFor( "MODE-2172" ) public void shouldDocumentImportCheckedInNodes() throws Exception { Node node1 = session.getRootNode().addNode("node1"); node1.addMixin("mix:versionable"); Node node2 = session.getRootNode().addNode("node2"); node2.addMixin("mix:versionable"); session.save(); JcrVersionManager versionManager = session.getWorkspace().getVersionManager(); versionManager.checkpoint("/node1"); session.getNode("/node1").setProperty("11", "some string"); session.getNode("/node1").setProperty("11a1", "some string"); session.save(); versionManager.checkin("/node1"); versionManager.checkpoint("/node2"); //export the data ByteArrayOutputStream baos = new ByteArrayOutputStream(); session.exportDocumentView("/", baos, false, false); session.getWorkspace().importXML("/", new ByteArrayInputStream(baos.toByteArray()), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); assertEquals(((org.modeshape.jcr.api.Property) session.getItem("/node1/11")).getString(), "some string"); assertEquals(((org.modeshape.jcr.api.Property) session.getItem("/node1/11a1")).getString(), "some string"); } @Test @FixFor( "MODE-2171" ) public void shouldNotExportACLsInSystemView() throws Exception { Node node = session.getRootNode().addNode("node"); AccessControlList acl = acl("/node"); AccessControlManager accessControlManager = session.getAccessControlManager(); acl.addAccessControlEntry(SimplePrincipal.EVERYONE, new Privilege[]{ accessControlManager.privilegeFromName( Privilege.JCR_ALL)}); accessControlManager.setPolicy("/node", acl); assertTrue(hasMixin(node, ModeShapeLexicon.ACCESS_CONTROLLABLE_STRING)); session.save(); //export the data ByteArrayOutputStream baos = new ByteArrayOutputStream(); session.exportSystemView("/", baos, false, false); node.remove(); session.save(); session.importXML("/", new ByteArrayInputStream(baos.toByteArray()), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); node = session.getNode("/node"); assertTrue(hasMixin(node, ModeShapeLexicon.ACCESS_CONTROLLABLE_STRING)); assertFalse(node.getNodes().hasNext()); } @Test @FixFor( "MODE-2171" ) public void shouldNotExportACLsInDocumentView() throws Exception { Node node = session.getRootNode().addNode("node"); AccessControlList acl = acl("/node"); AccessControlManager accessControlManager = session.getAccessControlManager(); acl.addAccessControlEntry(SimplePrincipal.EVERYONE, new Privilege[]{ accessControlManager.privilegeFromName( Privilege.JCR_ALL)}); accessControlManager.setPolicy("/node", acl); assertTrue(hasMixin(node, ModeShapeLexicon.ACCESS_CONTROLLABLE_STRING)); session.save(); //export the data ByteArrayOutputStream baos = new ByteArrayOutputStream(); session.exportDocumentView("/", baos, false, false); node.remove(); session.save(); session.importXML("/", new ByteArrayInputStream(baos.toByteArray()), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); node = session.getNode("/node"); assertTrue(hasMixin(node, ModeShapeLexicon.ACCESS_CONTROLLABLE_STRING)); assertFalse(node.getNodes().hasNext()); } @Test @FixFor( "MODE-2035" ) public void shouldExportViewsWithLocks() throws Exception { Node node1 = session.getRootNode().addNode("node1"); node1.addMixin("mix:lockable"); Node node1_1 = node1.addNode("node1_1"); node1_1.addMixin("mix:lockable"); Node node2 = session.getRootNode().addNode("node2"); node2.addMixin("mix:lockable"); session.save(); session.getWorkspace().lockManager().lock("/node1", true, true, Long.MAX_VALUE, null); session.getWorkspace().lockManager().lock("/node2", false, false, Long.MAX_VALUE, null); testImportExport("/", "/", ExportType.SYSTEM, true, false, true); testImportExport("/", "/", ExportType.DOCUMENT, true, false, true); } @Test @FixFor( "MODE-2192" ) public void shouldImportSystemViewWithCheckedInNodes() throws Exception { tools.registerNodeTypes(session, "cnd/ecm.cnd"); InputStream stream = resourceStream("io/ecm.xml"); session.importXML("/", stream, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); session.save(); JcrVersionManager versionManager = session.getWorkspace().getVersionManager(); assertNode("/root"); Node file = assertNode("/root/file1", "nt:file"); assertTrue(file.isCheckedOut()); Version fileVersion = versionManager.checkin("/root/file1"); assertEquals("1.0", fileVersion.getName()); assertEquals(2, versionManager.getVersionHistory("/root/file1").getAllVersions().getSize()); Node doc1 = assertNode("/root/folder1/doc1", "nt:file"); assertTrue(doc1.isCheckedOut()); Version docVersion = versionManager.checkin("/root/folder1/doc1"); assertEquals("1.0", docVersion.getName()); assertEquals(2, versionManager.getVersionHistory("/root/folder1/doc1").getAllVersions().getSize()); Node content = assertNode("/root/folder1/doc1/jcr:content", "nt:resource"); assertTrue(content.isCheckedOut()); Version contentVersion = versionManager.checkin("/root/folder1/doc1/jcr:content"); assertEquals("1.0", contentVersion.getName()); assertEquals(2, versionManager.getVersionHistory("/root/folder1/doc1/jcr:content").getAllVersions().getSize()); } @Test @FixFor( "MODE-2012" ) @SkipLongRunning( "There are 4 other test cases in JcrWorkspaceTest which validate the fix" ) public void shouldBeAbleToImportAndCloneWorkspaces() throws Exception { String root = "/brix:root"; // setup String workspaceA = "workspace_a"; String workspaceB = "workspace_b"; String workspaceC = "workspace_c"; Workspace wsA, wsB, wsC; JcrSession sessA, sessB, sessC; JcrWorkspace rootWS = session.getWorkspace(); rootWS.createWorkspace(workspaceA); sessA = repository.login(workspaceA); rootWS.createWorkspace(workspaceB); sessB = repository.login(workspaceB); rootWS.createWorkspace(workspaceC); sessC = repository.login(workspaceC); wsA = sessA.getWorkspace(); wsB = sessB.getWorkspace(); wsC = sessC.getWorkspace(); // namespace registering wsA.getNamespaceRegistry().registerNamespace("brix", "http://brix-cms.googlecode.com"); wsB.getNamespaceRegistry().registerNamespace("brix", "http://brix-cms.googlecode.com"); wsC.getNamespaceRegistry().registerNamespace("brix", "http://brix-cms.googlecode.com"); // initial imports tools.registerNodeTypes(sessA, "cnd/brix.cnd"); sessA.save(); InputStream brixWorkspace = resourceStream("io/brixWorkspace.xml"); sessA.importXML("/", brixWorkspace, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); sessA.save(); tools.registerNodeTypes(sessB, "cnd/brix.cnd"); sessB.save(); brixWorkspace = resourceStream("io/brixWorkspace.xml"); sessB.importXML("/", brixWorkspace, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); sessB.save(); tools.registerNodeTypes(sessC, "cnd/brix.cnd"); sessC.save(); brixWorkspace = resourceStream("io/brixWorkspace.xml"); sessC.importXML("/", brixWorkspace, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); sessC.save(); // re-import if (print) { new JcrTools().printSubgraph(sessA.getNode(root)); } sessA.getItem(root).remove(); sessA.save(); brixWorkspace = resourceStream("io/brixWorkspace.xml"); sessA.importXML("/", brixWorkspace, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); sessA.save(); // now we clone the workspace A over the other ones at the path of root... wsB.getSession().removeItem(root); wsB.getSession().save(); wsB.clone(workspaceA, root, root, true); wsB.getSession().save(); wsC.getSession().removeItem(root); wsC.getSession().save(); wsC.clone(workspaceB, root, root, true); wsC.getSession().save(); // re-import a second time sessA.getItem(root).remove(); sessA.save(); brixWorkspace = resourceStream("io/brixWorkspace.xml"); sessA.importXML("/", brixWorkspace, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING); sessA.save(); sessA.logout(); sessB.logout(); sessC.logout(); } private void assertCarsImported() throws RepositoryException { assertNode("/a/b/Cars"); assertNode("/a/b/Cars/Hybrid"); assertNode("/a/b/Cars/Hybrid/Toyota Prius"); assertNode("/a/b/Cars/Sports/Infiniti G37"); assertNode("/a/b/Cars/Utility/Land Rover LR3"); assertNoNode("/a/b/Cars[2]"); assertNoNode("/a/b/Cars/Hybrid[2]"); assertNoNode("/a/b/Cars/Hybrid/Toyota Prius[2]"); assertNoNode("/a/b/Cars/Sports[2]"); } @FixFor( "MODE-2284" ) @Test public void shouldRestoreBackreferencePropertiesAterImport() throws Exception { Node referenceableNode = session.getRootNode().addNode("referenceable"); referenceableNode.addMixin(JcrMixLexicon.REFERENCEABLE.toString()); Value strongRefValue = session.getValueFactory().createValue(referenceableNode, false); Node node1 = session.getRootNode().addNode("node1"); node1.setProperty("prop1", strongRefValue); session.save(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); session.exportSystemView("/referenceable", outputStream, false, false); // Import node tree. This lose backreferences for all nodes in the imported tree ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); session.importXML("/referenceable", inputStream, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); // Now save the changes session.save(); PropertyIterator propertyIterator = referenceableNode.getReferences(); assertEquals(1, propertyIterator.getSize()); } @Test @FixFor( "MODE-2359" ) public void importingMultipleTimeWithReplaceExistingShouldNotIncreaseReferencesCount() throws Exception { registerNodeTypes("cnd/ab-references.cnd"); session.getNode("/").addNode("testRoot"); session.save(); // A - this is a simple referenceable node // [test:a] > mix:referenceable session.importXML("/testRoot", resourceStream("io/a-references.xml"), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); session.save(); int repeatCount = 2; for (int i = 0; i < repeatCount; i++) { // B - this is a simple node that refers to the node A // [test:b] > mix:referenceable // - test:prop_ref_a (reference) < test:a session.importXML("/testRoot", resourceStream("io/b-references.xml"), ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); session.save(); } // make sure we only have 2 nodes (i.e. replace_existing worked) assertEquals(2, session.getNode("/testRoot").getNodes().getSize()); AbstractJcrNode a = session.getNode("/testRoot/a"); AbstractJcrNode b = session.getNode("/testRoot/b"); // make sure "a' has only referrer assertEquals(1, a.getReferences().getSize()); // Remove node B. b.remove(); session.save(); // After remove node B, node A should have zero referrers a.remove(); session.save(); } @Test @FixFor( "MODE-2375" ) public void importingShouldKeepCorrectReferrerCount() throws Exception { Node testRoot = session.getRootNode().addNode("testRoot"); //create the firt referrer first, so that it's exported before the referenceable node Node referrerOne = testRoot.addNode("referrerOne"); // Create referenceable node Node referenceableNode = testRoot.addNode("referenceable"); referenceableNode.addMixin(JcrMixLexicon.REFERENCEABLE.toString()); Value strongRefValue = session.getValueFactory().createValue(referenceableNode, false); // Create second referrer Node referrerTwo = testRoot.addNode("referrerTwo"); referrerTwo.setProperty("prop1", strongRefValue); // Set strong reference for the first referrer to referenceable node referrerOne.setProperty("prop1", strongRefValue); session.save(); // Check that we have two referrers assertEquals(2, session.getNode("/testRoot/referenceable").getReferences().getSize()); // Export nodes will be in the following order: referrerOne, referenceable, referrerTwo ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); session.exportSystemView("/testRoot", outputStream, false, false); // Cleanup session.getNode("/testRoot").remove(); session.save(); // Import nodes. ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); session.importXML("/", inputStream, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); session.save(); // Check that we have two referrers. // We must not lose the referrer from node referrerOne. assertEquals(2, session.getNode("/testRoot/referenceable").getReferences().getSize()); } @Test @FixFor( "MODE-2482" ) public void shouldSupportMultiValuedPropertiesInDocumentView() throws Exception { Node test = session.getRootNode().addNode("testRoot"); test.setProperty("prop1", "value 1"); String[] values = { "value 1", "value 2", "value 3" }; test.setProperty("prop2", values); session.save(); File file = new File("target/test-classes/document_view_multi_prop.xml"); FileUtil.delete(file); FileOutputStream outputStream = new FileOutputStream(file); exportDocumentView("/testRoot", outputStream); session.getNode("/testRoot").remove(); session.save(); importFile("/", file.getName(), ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); assertNotNull(session.getNode("/testRoot")); Property p1 = (Property)session.getItem("/testRoot/prop1"); assertFalse(p1.isMultiple()); assertEquals("value 1", p1.getString()); Property p2 = (Property)session.getItem("/testRoot/prop2"); assertTrue(p2.isMultiple()); Value[] jcrValues = p2.getValues(); List<String> actualValues = new ArrayList<>(jcrValues.length); for (Value jcrValue : jcrValues) { actualValues.add(jcrValue.getString()); } assertArrayEquals(values, actualValues.toArray()); } @Test @FixFor( "MODE-2482 ") public void shouldSupportMultipleMixinsInDocumentView() throws Exception { registerNodeTypes("cnd/mixins.cnd"); Node test = session.getRootNode().addNode("a", "nt:set"); test.addMixin("mix:a"); test.addMixin("mix:b"); test.setProperty("jcr:a", "a"); test.setProperty("jcr:b", "b"); session.save(); File file = new File("target/test-classes/document_view_mode_2482.xml"); FileUtil.delete(file); FileOutputStream outputStream = new FileOutputStream(file); exportDocumentView("/a", outputStream); session.getRootNode().addNode("b", "nt:folder"); session.save(); importFile("/b", file.getName(), ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW); assertNotNull("/b/a"); assertEquals("a", ((Property)session.getItem("/b/a/jcr:a")).getString()); assertEquals("b", ((Property) session.getItem("/b/a/jcr:b")).getString()); } @Test @FixFor( "MODE-2504" ) public void shouldAllowReorderingAfterImport() throws RepositoryException, IOException { session.getRootNode().addNode("firstLevelNode"); final InputStream xmlStream1 = resourceStream("io/child1-system-view.xml"); session.importXML("/firstLevelNode", xmlStream1, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); final InputStream xmlStream2 = resourceStream("io/child2-system-view.xml"); session.importXML("/firstLevelNode", xmlStream2, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); // now when we call the orderBefore, any operation on non-existing child of /firstLevelNode fails with NPE: session.getNode("/firstLevelNode").orderBefore("child2", "child1"); assertTrue(session.nodeExists("/firstLevelNode/child1")); assertTrue(session.nodeExists("/firstLevelNode/child2")); assertFalse(session.nodeExists("/firstLevelNode/nonexisting")); } // ---------------------------------------------------------------------------------------------------------------- // Utilities // ---------------------------------------------------------------------------------------------------------------- protected static enum ImportBehavior { CREATE_NEW(ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW), REPLACE_EXISTING(ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING), REMOVE_EXISTING(ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING), THROW(ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); private final int jcrValue; private ImportBehavior( int value ) { this.jcrValue = value; } public int getJcrValue() { return jcrValue; } } protected void importFile( String importIntoPath, String resourceName, int importBehavior ) throws Exception { // Import the car content ... InputStream stream = getClass().getClassLoader().getResourceAsStream(resourceName); assertThat(stream, is(notNullValue())); try { session.importXML(importIntoPath, stream, importBehavior); // shouldn't exist yet } finally { stream.close(); } } protected Node assertImport( String resourceName, String pathToParent, ImportBehavior behavior ) throws RepositoryException, IOException { InputStream istream = resourceStream(resourceName); return assertImport(istream, pathToParent, behavior); } protected Node assertImport( File resource, String pathToParent, ImportBehavior behavior ) throws RepositoryException, IOException { InputStream istream = new FileInputStream(resource); return assertImport(istream, pathToParent, behavior); } protected Node assertImport( InputStream istream, String pathToParent, ImportBehavior behavior ) throws RepositoryException, IOException { // Make the parent node if it does not exist ... Path parentPath = path(pathToParent); assertThat(parentPath.isAbsolute(), is(true)); Node node = session.getRootNode(); boolean found = true; for (Path.Segment segment : parentPath) { String name = asString(segment); if (found) { try { node = node.getNode(name); found = true; } catch (PathNotFoundException e) { found = false; } } if (!found) { node = node.addNode(name, "nt:unstructured"); } } if (!found) { // We added at least one node, so we need to save it before importing ... session.save(); } // Verify that the parent node does exist now ... assertNode(pathToParent); // Now, load the content of the resource being imported ... assertThat(istream, is(notNullValue())); try { session.getWorkspace().importXML(pathToParent, istream, behavior.getJcrValue()); } finally { istream.close(); } session.save(); return node; } protected File exportSystemView( String pathToParent ) throws IOException, RepositoryException { assertNode(pathToParent); // Export to a string ... File tmp = File.createTempFile("JcrImportExportText-", ""); FileOutputStream ostream = new FileOutputStream(tmp); boolean skipBinary = false; boolean noRecurse = false; session.exportSystemView(pathToParent, ostream, skipBinary, noRecurse); return tmp; } protected void exportDocumentView( String pathToParent, OutputStream ostream ) throws RepositoryException, IOException { boolean skipBinary = false; boolean noRecurse = false; try { session.exportDocumentView(pathToParent, ostream, skipBinary, noRecurse); } finally { ostream.close(); } } }