/*
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.jcr.api.importing;
import org.exoplatform.services.jcr.dataflow.ItemState;
import org.exoplatform.services.jcr.impl.core.NodeImpl;
import org.exoplatform.services.jcr.impl.core.TransientNodesManagerUtil;
import org.exoplatform.services.jcr.impl.dataflow.session.SessionChangesLog;
import org.xml.sax.SAXException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.jcr.ImportUUIDBehavior;
import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.xml.transform.TransformerConfigurationException;
/**
* Created by The eXo Platform SAS.
*
* @author <a href="mailto:Sergey.Kabashnyuk@gmail.com">Sergey Kabashnyuk</a>
* @version $Id: BaseXmlImporter.java 12649 2008-04-02 12:46:37Z ksm $
*/
public class AbstractCollisionTest extends AbstractImportTest
{
/**
* Import root node name.
*/
private static final String EXPORT_ROOT_NODE_NAME = "exportRoot";
/**
* First child node name.
*/
private static final String FAMILY_A_FIRST_CHILD_NODE_NAME = "afc";
/**
* Mix referenceable node name.
*/
private static final String FAMILY_A_PARENT = "aparent";
/**
* First child node name.
*/
private static final String FAMILY_A_SECOND_CHILD_NODE_NAME = "asc";
/**
* First child node name.
*/
private static final String FAMILY_B_FIRST_CHILD_NODE_NAME = "bfc";
/**
* First child node name.
*/
private static final String FAMILY_B_FIRST_GRAND_CHILD_NODE_NAME = "bfgc";
/**
* Mix referenceable node name.
*/
private static final String FAMILY_B_PARENT = "bparent";
/**
* First child node name.
*/
private static final String FAMILY_B_SECOND_CHILD_NODE_NAME = "bsc";
/**
* First child node name.
*/
private static final String FAMILY_C_FIRST_CHILD_NODE_NAME = "cfc";
/**
* Mix referenceable node name.
*/
private static final String FAMILY_C_PARENT = "cparent";
/**
* First child node name.
*/
private static final String FAMILY_C_SECOND_CHILD_NODE_NAME = "csc";
/**
* Import root node name.
*/
private static final String IMPORT_ROOT_NODE_NAME = "importRoot";
/**
* Root node name.
*/
private static final String ROOT_NODE_NAME = "testRoot";
/**
* Uuid of <code>FAMILY_A_PARENT</code> node.
*/
private String familyAuuid;
/**
* Uuid of <code>FAMILY_B_PARENT</code> node.
*/
private String familyBuuid;
/**
* Uuid of <code>FAMILY_C_PARENT</code> node.
*/
private String familyCuuid;
/**
* @param isSystemView
* @param isExportedByStream
* @param isImportedByStream
* @param saveType
* @param testedBehavior
* @throws RepositoryException
* @throws TransformerConfigurationException
* @throws IOException
* @throws SAXException
*/
protected void importUuidCollisionTest(boolean isSystemView, boolean isExportedByStream, boolean isImportedByStream,
XmlSaveType saveType, int testedBehavior) throws RepositoryException, TransformerConfigurationException,
IOException, SAXException
{
Exception result = null;
try
{
preformImport(isSystemView, isExportedByStream, isImportedByStream, saveType, testedBehavior);
session.save();
}
catch (Exception e)
{
result = e;
}
Node imporRoot = null;
if (testedBehavior != ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW)
{
imporRoot =
session.getRootNode().getNode(ROOT_NODE_NAME).getNode(IMPORT_ROOT_NODE_NAME).getNode(EXPORT_ROOT_NODE_NAME);
}
else
{
imporRoot = session.getRootNode().getNode(ROOT_NODE_NAME).getNode(IMPORT_ROOT_NODE_NAME);
}
checkResult(imporRoot, session.getRootNode().getNode(ROOT_NODE_NAME).getNode(EXPORT_ROOT_NODE_NAME), result,
isImportedByStream, testedBehavior);
session.refresh(false);
if (session.getRootNode().hasNode(ROOT_NODE_NAME))
{
session.getRootNode().getNode(ROOT_NODE_NAME).remove();
session.save();
}
if ((testedBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING || testedBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING)
&& saveType == XmlSaveType.SESSION)
{
preformImport(isSystemView, isExportedByStream, isImportedByStream, saveType, testedBehavior);
SessionChangesLog changesLog = TransientNodesManagerUtil.getChangesLog(session.getTransientNodesManager());
for (ItemState state : changesLog.getAllStates())
{
assertTrue(state.getAncestorToSave().equals(
((NodeImpl)session.getRootNode().getNode(ROOT_NODE_NAME)).getData().getQPath()));
}
}
}
/**
* Checks correctness of an arrangement of members of family in a family tree beginning from the
* <code>checkRoot</code> node.
*
*
* @param checkRoot
* - parent node of family tree.
* @param family
* - family name.
* @param referenceableUuid
* - uuid of famali parent node.
* @throws RepositoryException
* - RepositoryException.
*/
private void checkFamilyTree(final Node checkRoot, final Family family, final String referenceableUuid)
throws RepositoryException
{
String parentName = null;
String child1Name = null;
String child2Name = null;
switch (family)
{
case A :
parentName = FAMILY_A_PARENT;
child1Name = FAMILY_A_FIRST_CHILD_NODE_NAME;
child2Name = FAMILY_A_SECOND_CHILD_NODE_NAME;
break;
case B :
parentName = FAMILY_B_PARENT;
child1Name = FAMILY_B_FIRST_CHILD_NODE_NAME;
child2Name = FAMILY_B_SECOND_CHILD_NODE_NAME;
break;
case C :
parentName = FAMILY_C_PARENT;
child1Name = FAMILY_C_FIRST_CHILD_NODE_NAME;
child2Name = FAMILY_C_SECOND_CHILD_NODE_NAME;
break;
default :
throw new RepositoryException("Unknown famaly " + family.name());
}
// check family root
assertTrue(checkRoot.hasNode(parentName));
Node familyRoot = checkRoot.getNode(parentName);
assertNotNull(familyRoot);
// check is mix:referenceable
assertTrue(familyRoot.isNodeType("mix:referenceable"));
// compare uuids
if (referenceableUuid != null)
assertEquals(referenceableUuid, familyRoot.getProperty("jcr:uuid").getString());
// check childs
assertTrue(familyRoot.hasNode(child1Name));
assertTrue(familyRoot.hasNode(child2Name));
// check child count
assertEquals(2, familyRoot.getNodes().getSize());
}
/**
* @param importRoot
* @param exportRootNode
* @param result
* @param isImportedByStream
* @param testedBehavior
* @throws RepositoryException
*/
private void checkResult(Node importRoot, Node exportRootNode, Exception result, boolean isImportedByStream,
int testedBehavior) throws RepositoryException
{
assertNotNull(exportRootNode);
assertNotNull(importRoot);
// Exception should be thrown
if (testedBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW)
{
assertNotNull("Exception should have been thrown", result);
// Check correct type of exception
if (isImportedByStream)
{
assertTrue("Exception should be the ItemExistsException type", result instanceof ItemExistsException);
}
else
{
assertTrue("Exception should be the SAXException type", result instanceof SAXException);
}
}
else if (testedBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW)
{
assertNull("An exception should not have been thrown", result);
// Old referenceable node should stay on same position with same uuid.
checkFamilyTree(exportRootNode, Family.C, familyCuuid);
checkFamilyTree(exportRootNode, Family.B, familyBuuid);
assertTrue(exportRootNode.hasNode(FAMILY_B_PARENT + "/" + FAMILY_B_FIRST_CHILD_NODE_NAME + "/"
+ FAMILY_B_FIRST_GRAND_CHILD_NODE_NAME));
// all content of document imported to the importRoot
checkFamilyTree(importRoot, Family.A, null);
checkFamilyTree(importRoot, Family.B, null);
}
else if (testedBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING)
{
assertNull("An exception should not have been thrown", result);
// Old referenceable node and all its subtree should be removed
checkFamilyTree(exportRootNode, Family.C, familyCuuid);
assertFalse(exportRootNode.hasNode(FAMILY_B_PARENT));
assertFalse(exportRootNode.hasNode(FAMILY_A_PARENT));
// Check correct importing of document to new location
checkFamilyTree(importRoot, Family.A, familyAuuid);
checkFamilyTree(importRoot, Family.B, familyBuuid);
}
else if (testedBehavior == ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING)
{
checkFamilyTree(exportRootNode, Family.C, familyCuuid);
checkFamilyTree(exportRootNode, Family.B, familyBuuid);
assertFalse(exportRootNode.hasNode(FAMILY_B_PARENT + "/" + FAMILY_B_FIRST_CHILD_NODE_NAME + "/"
+ FAMILY_B_FIRST_GRAND_CHILD_NODE_NAME));
checkFamilyTree(importRoot, Family.A, familyAuuid);
assertFalse(importRoot.hasNode(FAMILY_B_PARENT));
}
}
/**
* @param isSystemView
* @param isExportedByStream
* @param isImportedByStream
* @param saveType
* @param testedBehavior
* @throws RepositoryException
* @throws TransformerConfigurationException
* @throws IOException
* @throws SAXException
*/
private void preformImport(boolean isSystemView, boolean isExportedByStream, boolean isImportedByStream,
XmlSaveType saveType, int testedBehavior) throws RepositoryException, TransformerConfigurationException,
IOException, SAXException
{
NodeImpl testRoot = (NodeImpl)prepareForExport(session);
Node exportRoot = testRoot.getNode(EXPORT_ROOT_NODE_NAME);
byte[] content = serialize(exportRoot, isSystemView, isExportedByStream);
Node importRoot = prepareForImport(testRoot);
deserialize(importRoot, saveType, isImportedByStream, testedBehavior, new ByteArrayInputStream(content));
}
/**
* Prepare repository for uuid collisions tests.
*
* @param exportSession
* - working session.
* @return - prepared root node.
* @throws RepositoryException
*/
private Node prepareForExport(final Session exportSession) throws RepositoryException
{
exportSession.refresh(false);
if (exportSession.getRootNode().hasNode(ROOT_NODE_NAME))
{
exportSession.getRootNode().getNode(ROOT_NODE_NAME).remove();
exportSession.save();
}
Node testRootNode = exportSession.getRootNode().addNode(ROOT_NODE_NAME);
Node exportRootNode = testRootNode.addNode(EXPORT_ROOT_NODE_NAME);
Node familyAParentNode = exportRootNode.addNode(FAMILY_A_PARENT);
familyAParentNode.addMixin("mix:referenceable");
familyAParentNode.addNode(FAMILY_A_FIRST_CHILD_NODE_NAME);
familyAParentNode.addNode(FAMILY_A_SECOND_CHILD_NODE_NAME);
Node familyBParentNode = exportRootNode.addNode(FAMILY_B_PARENT);
familyBParentNode.addMixin("mix:referenceable");
familyBParentNode.addNode(FAMILY_B_FIRST_CHILD_NODE_NAME);
familyBParentNode.addNode(FAMILY_B_SECOND_CHILD_NODE_NAME);
exportSession.save();
familyAuuid = familyAParentNode.getProperty("jcr:uuid").getString();
familyBuuid = familyBParentNode.getProperty("jcr:uuid").getString();
// check correct state
checkFamilyTree(exportRootNode, Family.A, familyAuuid);
checkFamilyTree(exportRootNode, Family.B, familyBuuid);
return testRootNode;
}
/**
* Prepare repository for import content for uuid collisions tests.
* <ol>
* <li>Remove FIRST_CHILD_NODE_NAME and all subnodes</li>
* <li>Add to MIX_REFERENCEABLE_NODE_NAME node SECOND_CHILD_NODE_NAME</li>
* <li>Add to testRoot new node IMPORT_ROOT_NODE_NAME</li>
* </ol>
*
* @param testRootNode
* @return
* @throws RepositoryException
*/
private Node prepareForImport(Node testRootNode) throws RepositoryException
{
Node exportRootNode = testRootNode.getNode(EXPORT_ROOT_NODE_NAME);
assertTrue(exportRootNode.hasNode(FAMILY_A_PARENT));
exportRootNode.getNode(FAMILY_A_PARENT).remove();
exportRootNode.getNode(FAMILY_B_PARENT).getNode(FAMILY_B_FIRST_CHILD_NODE_NAME).addNode(
FAMILY_B_FIRST_GRAND_CHILD_NODE_NAME);
Node familyCParentNode = exportRootNode.addNode(FAMILY_C_PARENT);
familyCParentNode.addMixin("mix:referenceable");
familyCParentNode.addNode(FAMILY_C_FIRST_CHILD_NODE_NAME);
familyCParentNode.addNode(FAMILY_C_SECOND_CHILD_NODE_NAME);
Node importRoot = testRootNode.addNode(IMPORT_ROOT_NODE_NAME);
testRootNode.getSession().save();
familyCuuid = familyCParentNode.getProperty("jcr:uuid").getString();
assertFalse(exportRootNode.hasNode(FAMILY_A_PARENT));
assertTrue(exportRootNode.hasNode(FAMILY_C_PARENT));
checkFamilyTree(exportRootNode, Family.C, familyCuuid);
return importRoot;
}
/**
*
*
*
*/
enum Family {
A, B, C
};
}