[exo-jcr-commits] exo-jcr SVN: r4015 - in jcr/branches/1.12.x: exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/importing and 1 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Thu Feb 24 22:02:50 EST 2011


Author: paristote
Date: 2011-02-24 22:02:49 -0500 (Thu, 24 Feb 2011)
New Revision: 4015

Added:
   jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/importing/TestImportVersionedChild.java
   jcr/branches/1.12.x/patch/1.12.8-GA/JCR-1584/readme.txt
Modified:
   jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/xml/importing/SystemViewImporter.java
Log:
JCR-1584

What is the problem to fix?

    * SysViewImporter tests nt:versionedChild nodes - does it allowed as child for nt:folder node. This is incorrevt, since nt:versionedChild allowed in any place below frozen node.
    * SystemViewStreamExporter exports pure nodes Version history. It is correct. But there is problem when we import version history of node, that have versionable subnode. And remove this node tree after export. In this case version history of versioned subnode will be lost.

How is the problem fixed?

    * In case if exported node is nt:versionableChild and it is a descendant of frozen node - there is no child node primary type validation.
    * SystemViewStreamExporter exports versioned subnodes version history as sv:versionedChild structure into <sv:node>nt:verstionedChild</sv:node>
    * SysViewImporter imports all version histories in <sv:versionedChild> tags, and stores these version UUIDs in Context as "importedSubversions"-named list.
    * VersionHistoryImporter uses this list to update versionable child node properties (versionHistory, baseVerasion, predecessors) with actual values.




Modified: jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/xml/importing/SystemViewImporter.java
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/xml/importing/SystemViewImporter.java	2011-02-25 02:48:27 UTC (rev 4014)
+++ jcr/branches/1.12.x/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/xml/importing/SystemViewImporter.java	2011-02-25 03:02:49 UTC (rev 4015)
@@ -418,9 +418,16 @@
       {
          NodeData parentNodeData = getParent();
          // nodeTypeDataManager.findChildNodeDefinition(primaryTypeName,)
-         if (!nodeTypeDataManager.isChildNodePrimaryTypeAllowed(primaryTypeName, parentNodeData.getPrimaryTypeName(),
-            parentNodeData.getMixinTypeNames()))
+
+         // check is nt:versionedChild subnode of frozenNode
+         if (nodeData.getQPath().getDepth() > 6 && primaryTypeName.equals(Constants.NT_VERSIONEDCHILD)
+            && nodeData.getQPath().getEntries()[5].equals(Constants.JCR_FROZENNODE))
          {
+            //do nothing
+         }
+         else if (!nodeTypeDataManager.isChildNodePrimaryTypeAllowed(primaryTypeName, parentNodeData
+            .getPrimaryTypeName(), parentNodeData.getMixinTypeNames()))
+         {
             throw new ConstraintViolationException("Can't add node " + nodeData.getQName().getAsString() + " to "
                + parentNodeData.getQPath().getAsString() + " node type " + sName
                + " is not allowed as child's node type for parent node type "

Added: jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/importing/TestImportVersionedChild.java
===================================================================
--- jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/importing/TestImportVersionedChild.java	                        (rev 0)
+++ jcr/branches/1.12.x/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/importing/TestImportVersionedChild.java	2011-02-25 03:02:49 UTC (rev 4015)
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2003-2011 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.services.jcr.api.importing;
+
+import org.exoplatform.commons.utils.MimeTypeResolver;
+import org.exoplatform.services.jcr.JcrAPIBaseTest;
+import org.exoplatform.services.jcr.dataflow.ItemState;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLog;
+import org.exoplatform.services.jcr.dataflow.PlainChangesLogImpl;
+import org.exoplatform.services.jcr.impl.core.NodeImpl;
+import org.exoplatform.services.jcr.impl.core.PropertyImpl;
+import org.exoplatform.services.jcr.impl.core.SessionDataManager;
+import org.exoplatform.services.jcr.util.VersionHistoryImporter;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Calendar;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * <br/>Date: 
+ *
+ * @author <a href="karpenko.sergiy at gmail.com">Karpenko Sergiy</a> 
+ * @version $Id: TestImportImage.java 111 2008-11-11 11:11:11Z serg $
+ */
+public class TestImportVersionedChild extends JcrAPIBaseTest
+{
+
+   Node testRoot;
+
+   public void setUp() throws Exception
+   {
+      super.setUp();
+      testRoot = this.root.addNode("parent", "nt:folder");
+      root.save();
+   }
+
+   public void tearDown() throws Exception
+   {
+      testRoot.remove();
+      root.save();
+      super.tearDown();
+   }
+
+   protected void loadTestTree() throws Exception
+   {
+      // wc1/medias/picture
+      Node wc1 = testRoot.addNode("wc1", "nt:folder");
+      wc1.addMixin("mix:versionable");
+      testRoot.save();
+      Node medias = wc1.addNode("medias", "nt:folder");
+
+      Node picture = medias.addNode("picture", "nt:file");
+      picture.addMixin("mix:versionable");
+
+      Node res = picture.addNode("jcr:content", "nt:resource");
+      res.setProperty("jcr:lastModified", Calendar.getInstance());
+      res.setProperty("jcr:data", new ByteArrayInputStream("bla bla".getBytes()));
+      MimeTypeResolver mimres = new MimeTypeResolver();
+      res.setProperty("jcr:mimeType", mimres.getMimeType("screen.txt"));
+      root.save();
+   }
+
+   public void testImportVersionHistoryPreloadChildVersionHistory() throws Exception
+   {
+      loadTestTree();
+      Node wc1 = (NodeImpl)session.getItem("/parent/wc1");
+      Node picture = (NodeImpl)session.getItem("/parent/wc1/medias/picture");
+
+      // make checkin/checkout a lot
+
+      wc1.checkin();
+      wc1.checkout();
+
+      picture.checkin();
+      picture.checkout();
+
+      // export import version history and node
+      ByteArrayOutputStream out = new ByteArrayOutputStream();
+      session.exportSystemView("/parent/wc1", out, false, false);
+
+      ByteArrayOutputStream childvhout = new ByteArrayOutputStream();
+      session.exportSystemView(picture.getVersionHistory().getPath(), childvhout, false, false);
+
+      ByteArrayOutputStream vhout = new ByteArrayOutputStream();
+      session.exportSystemView(wc1.getVersionHistory().getPath(), vhout, false, false);
+
+      // prepare data for version import
+
+      String versionHistory = wc1.getProperty("jcr:versionHistory").getValue().getString();
+      String baseVersion = wc1.getProperty("jcr:baseVersion").getValue().getString();
+      Value[] jcrPredecessors = wc1.getProperty("jcr:predecessors").getValues();
+      StringBuilder jcrPredecessorsBuilder = new StringBuilder();
+      String[] predecessorsHistory;
+      for (Value value : jcrPredecessors)
+      {
+         if (jcrPredecessorsBuilder.length() > 0)
+            jcrPredecessorsBuilder.append(",");
+         jcrPredecessorsBuilder.append(value.getString());
+      }
+      if (jcrPredecessorsBuilder.toString().indexOf(",") > -1)
+      {
+         predecessorsHistory = jcrPredecessorsBuilder.toString().split(",");
+      }
+      else
+      {
+         predecessorsHistory = new String[]{jcrPredecessorsBuilder.toString()};
+      }
+
+      // prepare data for child version import
+
+      String chversionHistory = picture.getProperty("jcr:versionHistory").getValue().getString();
+      String chbaseVersion = picture.getProperty("jcr:baseVersion").getValue().getString();
+      Value[] chjcrPredecessors = picture.getProperty("jcr:predecessors").getValues();
+      StringBuilder chjcrPredecessorsBuilder = new StringBuilder();
+      String[] chpredecessorsHistory;
+      for (Value value : chjcrPredecessors)
+      {
+         if (chjcrPredecessorsBuilder.length() > 0)
+            chjcrPredecessorsBuilder.append(",");
+         chjcrPredecessorsBuilder.append(value.getString());
+      }
+      if (chjcrPredecessorsBuilder.toString().indexOf(",") > -1)
+      {
+         chpredecessorsHistory = chjcrPredecessorsBuilder.toString().split(",");
+      }
+      else
+      {
+         chpredecessorsHistory = new String[]{chjcrPredecessorsBuilder.toString()};
+      }
+
+      // remove node
+      wc1.remove();
+      session.save();
+
+      out.close();
+      vhout.close();
+
+      // import
+      session.importXML("/parent", new ByteArrayInputStream(out.toByteArray()),
+         ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW, true);
+
+      session.save();
+
+      wc1 = (NodeImpl)session.getItem("/parent/wc1");
+      picture = wc1.getNode("medias").getNode("picture");
+
+      VersionHistoryImporter chversionHistoryImporter =
+         new VersionHistoryImporter((NodeImpl)picture, new ByteArrayInputStream(childvhout.toByteArray()),
+            chbaseVersion, chpredecessorsHistory, chversionHistory);
+      chversionHistoryImporter.doImport();
+      session.save();
+
+      VersionHistoryImporter versionHistoryImporter =
+         new VersionHistoryImporter((NodeImpl)wc1, new ByteArrayInputStream(vhout.toByteArray()), baseVersion,
+            predecessorsHistory, versionHistory);
+      versionHistoryImporter.doImport();
+      session.save();
+
+      assertTrue(picture.isNodeType("mix:versionable"));
+      assertEquals(chversionHistory, picture.getProperty("jcr:versionHistory").getValue().getString());
+      assertEquals(chbaseVersion, picture.getProperty("jcr:baseVersion").getValue().getString());
+      assertEquals(chpredecessorsHistory[0], picture.getProperty("jcr:predecessors").getValues()[0].getString());
+   }
+
+   public void testImportVersionHistory() throws Exception
+   {
+      loadTestTree();
+      Node wc1 = (NodeImpl)session.getItem("/parent/wc1");
+
+      // make checkin/checkout 
+      wc1.checkin();
+      wc1.checkout();
+
+      // export import version history and node
+      ByteArrayOutputStream out = new ByteArrayOutputStream();
+      session.exportSystemView("/parent/wc1", out, false, false);
+
+      ByteArrayOutputStream vhout = new ByteArrayOutputStream();
+      session.exportSystemView(wc1.getVersionHistory().getPath(), vhout, false, false);
+
+      // prepare data for version import
+      String versionHistory = wc1.getProperty("jcr:versionHistory").getValue().getString();
+      String baseVersion = wc1.getProperty("jcr:baseVersion").getValue().getString();
+      Value[] jcrPredecessors = wc1.getProperty("jcr:predecessors").getValues();
+      StringBuilder jcrPredecessorsBuilder = new StringBuilder();
+      String[] predecessorsHistory;
+      for (Value value : jcrPredecessors)
+      {
+         if (jcrPredecessorsBuilder.length() > 0)
+            jcrPredecessorsBuilder.append(",");
+         jcrPredecessorsBuilder.append(value.getString());
+      }
+      if (jcrPredecessorsBuilder.toString().indexOf(",") > -1)
+      {
+         predecessorsHistory = jcrPredecessorsBuilder.toString().split(",");
+      }
+      else
+      {
+         predecessorsHistory = new String[]{jcrPredecessorsBuilder.toString()};
+      }
+
+      // remove node
+      wc1.remove();
+      session.save();
+
+      out.close();
+      vhout.close();
+
+      // import
+      session.importXML("/parent", new ByteArrayInputStream(out.toByteArray()),
+         ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW, true);
+
+      session.save();
+
+      wc1 = (NodeImpl)session.getItem("/parent/wc1");
+
+      VersionHistoryImporter versionHistoryImporter =
+         new VersionHistoryImporter((NodeImpl)wc1, new ByteArrayInputStream(vhout.toByteArray()), baseVersion,
+            predecessorsHistory, versionHistory);
+      versionHistoryImporter.doImport();
+      session.save();
+
+      Node picture = wc1.getNode("medias").getNode("picture");
+      assertTrue(picture.isNodeType("mix:versionable"));
+
+      //try to remove wc1, there must be RepositoryException
+      try
+      {
+         wc1.remove();
+         session.save();
+         fail();
+      }
+      catch (RepositoryException e)
+      {
+         // OK - wc1  Version History contain nt:versionedChild with link to non exist Version history
+
+         // remove bugy version history
+         SessionDataManager dataManager = session.getTransientNodesManager();
+         NodeImpl vhPicture =
+            (NodeImpl)session.getItem("/jcr:system/jcr:versionStorage/" + versionHistory
+               + "/1/jcr:frozenNode/medias/picture");
+
+         assertTrue(vhPicture.isNodeType("nt:versionedChild"));
+
+         PlainChangesLog changesLogDelete = new PlainChangesLogImpl();
+         changesLogDelete.add(ItemState.createDeletedState(((PropertyImpl)vhPicture.getProperty("jcr:primaryType"))
+            .getData()));
+         changesLogDelete.add(ItemState.createDeletedState(((PropertyImpl)vhPicture
+            .getProperty("jcr:childVersionHistory")).getData()));
+         for (ItemState itemState : changesLogDelete.getAllStates())
+         {
+            dataManager.delete(itemState.getData(), itemState.getAncestorToSave());
+         }
+         session.save();
+      }
+   }
+}

Added: jcr/branches/1.12.x/patch/1.12.8-GA/JCR-1584/readme.txt
===================================================================
--- jcr/branches/1.12.x/patch/1.12.8-GA/JCR-1584/readme.txt	                        (rev 0)
+++ jcr/branches/1.12.x/patch/1.12.8-GA/JCR-1584/readme.txt	2011-02-25 03:02:49 UTC (rev 4015)
@@ -0,0 +1,89 @@
+Summary
+
+    * Status: ConstraintViolationException when Importing Version history of an nt:folder node having an nt:file child node
+    * CCP Issue: CCP-736, Product Jira Issue: JCR-1584
+    * Fixes: ECMS-1903
+    * Complexity: N/A
+
+The Proposal
+Problem description
+
+What is the problem to fix?
+
+    * SysViewImporter tests nt:versionedChild nodes - does it allowed as child for nt:folder node. This is incorrevt, since nt:versionedChild allowed in any place below frozen node.
+    * SystemViewStreamExporter exports pure nodes Version history. It is correct. But there is problem when we import version history of node, that have versionable subnode. And remove this node tree after export. In this case version history of versioned subnode will be lost.
+
+Fix description
+
+How is the problem fixed?
+
+    * In case if exported node is nt:versionableChild and it is a descendant of frozen node - there is no child node primary type validation.
+    * SystemViewStreamExporter exports versioned subnodes version history as sv:versionedChild structure into <sv:node>nt:verstionedChild</sv:node>
+    * SysViewImporter imports all version histories in <sv:versionedChild> tags, and stores these version UUIDs in Context as "importedSubversions"-named list.
+    * VersionHistoryImporter uses this list to update versionable child node properties (versionHistory, baseVerasion, predecessors) with actual values.
+
+Patch information:
+Patch files: JCR-1584.patch 	  	
+
+Tests to perform
+
+Reproduction test
+
+    * Steps to reproduce:
+      1)Create a web content named for example wc1
+      2)Under wc1/medias/images add a node of type nt:file(upload an image for example IMG_0374.JPG)
+      3)Create many version of this document
+      3)Export wc1 with its version history in the system view format
+      4)Stop the server,clean the database and start the server again
+      5)Import wc1 with its version history
+      In console, you will get a ConstraintViolationException
+
+After investigation, the root cause comes from the export data process, there is something wrong with the exported data (the attached image shows it):
+
+    * Before exporting, the primary type of "IMG_0374.JPG" node is nt:file. After exporting, it becomes nt:versionedChild
+    * In importing process, because nt:versionedChild node type is not allowed as child's node type for parent node type (nt:folder) so nodeTypeDataManager.isChildNodePrimaryTypeAllowed() function returns false and an ConstraintViolationException is throwed
+
+Tests performed at DevLevel
+
+    * Patch contains TestImportVersionedChild
+
+Tests performed at QA/Support Level
+*
+
+
+Documentation changes
+
+Documentation changes:
+  * none
+
+
+Configuration changes
+
+Configuration changes:
+  * none
+
+Will previous configuration continue to work?
+  * yes
+
+
+Risks and impacts
+
+Can this bug fix have any side effects on current client projects?
+
+    * SysViewExporter class has new constructor with additional parameter.
+
+Is there a performance risk/cost?
+
+    * Yes. There are additional validations. Also export/import of big nodes tree with many versionable nodes may take more time.
+
+Validation (PM/Support/QA)
+
+PM Comment
+* Patch approved by the PM
+
+Support Comment
+* Patch validated
+
+QA Feedbacks
+*
+



More information about the exo-jcr-commits mailing list