You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2013/08/28 13:47:51 UTC

svn commit: r1518170 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/persistence/util/ main/java/org/apache/jackrabbit/core/version/ test/java/org/apache/jackrabbit/core/persistence...

Author: thomasm
Date: Wed Aug 28 11:47:51 2013
New Revision: 1518170

URL: http://svn.apache.org/r1518170
Log:
JCR-3652 Bundle serialization broken

Added:
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/BundleBindingRandomizedTest.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/NodeCorruptionTest.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleWriter.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodePropBundle.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/NodeStateEx.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/TestAll.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java?rev=1518170&r1=1518169&r2=1518170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/NodeImpl.java Wed Aug 28 11:47:51 2013
@@ -2058,6 +2058,18 @@ public class NodeImpl extends ItemImpl i
                     removeChildProperty(name);
                 }
                 throw e; // rethrow
+            } catch (RuntimeException e) {
+                if (status.get(CREATED)) {
+                    // setting value failed, get rid of newly created property
+                    removeChildProperty(name);
+                }
+                throw e; // rethrow
+            } catch (Error e) {
+                if (status.get(CREATED)) {
+                    // setting value failed, get rid of newly created property
+                    removeChildProperty(name);
+                }
+                throw e; // rethrow
             }
             return property;
         }
@@ -2851,7 +2863,13 @@ public class NodeImpl extends ItemImpl i
             PropertyId id = new PropertyId(state.getNodeId(), JCR_ISCHECKEDOUT);
             PropertyState ps =
                 (PropertyState) sessionContext.getItemStateManager().getItemState(id);
-            return ps.getValues()[0].getBoolean();
+            InternalValue[] values = ps.getValues();
+            if (values == null || values.length != 1) {
+                // the property is not fully set, or it is a multi-valued property
+                // in which case it's probably not mix:versionable
+                return true;
+            }
+            return values[0].getBoolean();
         } catch (ItemStateException e) {
             throw new RepositoryException(e);
         }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleWriter.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleWriter.java?rev=1518170&r1=1518169&r2=1518170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleWriter.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleWriter.java Wed Aug 28 11:47:51 2013
@@ -184,7 +184,9 @@ class BundleWriter {
         InternalValue[] values = state.getValues();
 
         int type = state.getType();
-        assert 0 <= type && type <= 0x0f;
+        if (type < 0 || type > 0xf) {
+            throw new IOException("Illegal property type " + type);
+        }
         if (state.isMultiValued()) {
             int len = values.length + 1;
             if (len < 0x0f) {
@@ -194,7 +196,11 @@ class BundleWriter {
                 writeVarInt(len - 0x0f);
             }
         } else {
-            assert values.length == 1;
+            if (values.length != 1) {
+                throw new IOException(
+                        "Single values property with " + values.length + " values: " + 
+                        state.getName());
+            }
             out.writeByte(type);
         }
 
@@ -203,7 +209,7 @@ class BundleWriter {
         // values
         for (int i = 0; i < values.length; i++) {
             InternalValue val = values[i];
-            switch (state.getType()) {
+            switch (type) {
                 case PropertyType.BINARY:
                     try {
                         long size = val.getLength();
@@ -324,9 +330,13 @@ class BundleWriter {
                         throw new IOException("Unexpected error while writing DATE value.");
                     }
                     break;
-                default:
+                case PropertyType.STRING:
+                case PropertyType.PATH:
+                case PropertyType.URI:
                     writeString(val.toString());
                     break;
+                default:
+                    throw new IOException("Inknown property type: " + type);
             }
         }
     }
@@ -449,6 +459,9 @@ class BundleWriter {
             }
 
             String local = name.getLocalName();
+            if (local.length() == 0) {
+                throw new IOException("Attempt to write an empty local name: " + name);
+            }
             byte[] bytes = local.getBytes("UTF-8");
             int len = Math.min(bytes.length - 1, 0x0f);
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodePropBundle.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodePropBundle.java?rev=1518170&r1=1518169&r2=1518170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodePropBundle.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/NodePropBundle.java Wed Aug 28 11:47:51 2013
@@ -434,6 +434,7 @@ public class NodePropBundle {
 
     //--------------------------------------------------------------< Object >
 
+    @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
         builder.append(id);
@@ -459,20 +460,27 @@ public class NodePropBundle {
         return builder.toString();
     }
 
+    @Override
     public boolean equals(Object object) {
         if (object instanceof NodePropBundle) {
             NodePropBundle that = (NodePropBundle) object;
-            return id.equals(that.id)
-                && parentId.equals(that.parentId)
-                && nodeTypeName.equals(that.nodeTypeName)
-                && mixinTypeNames.equals(that.mixinTypeNames)
+            return equalNullSafe(id, that.id)
+                && equalNullSafe(parentId, that.parentId)
+                && equalNullSafe(nodeTypeName, that.nodeTypeName)
+                && equalNullSafe(mixinTypeNames, that.mixinTypeNames)
                 && isReferenceable == that.isReferenceable
-                && sharedSet.equals(that.sharedSet)
-                && properties.equals(that.properties)
-                && childNodeEntries.equals(that.childNodeEntries);
-        } else {
-            return false;
+                && equalNullSafe(sharedSet, that.sharedSet)
+                && equalNullSafe(properties, that.properties)
+                && equalNullSafe(childNodeEntries, that.childNodeEntries);
         }
+        return false;
+    }
+    
+    private static boolean equalNullSafe(Object a, Object b) {
+        if (a == null || b == null) {
+            return a == b;
+        }
+        return a.equals(b);
     }
 
     //-----------------------------------------------------< ChildNodeEntry >---
@@ -590,6 +598,9 @@ public class NodePropBundle {
             values = state.getValues();
             type = state.getType();
             multiValued = state.isMultiValued();
+            if (!multiValued && values.length != 1) {
+                throw new IllegalArgumentException("Non-multi-valued property with values.length " + values.length);
+            }
             modCount = state.getModCount();
             if (type == PropertyType.BINARY) {
                 blobIds = new String[values.length];

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/NodeStateEx.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/NodeStateEx.java?rev=1518170&r1=1518169&r2=1518170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/NodeStateEx.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/version/NodeStateEx.java Wed Aug 28 11:47:51 2013
@@ -279,6 +279,7 @@ public class NodeStateEx {
                     nodeState.setStatus(ItemState.STATUS_EXISTING_MODIFIED);
                 }
                 propState.setType(type);
+                propState.setMultiValued(multiple);
                 propState.setValues(values);
                 return propState;
             } catch (ItemStateException e) {

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/BundleBindingRandomizedTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/BundleBindingRandomizedTest.java?rev=1518170&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/BundleBindingRandomizedTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/BundleBindingRandomizedTest.java Wed Aug 28 11:47:51 2013
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.core.persistence.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+import javax.jcr.PropertyType;
+
+import junit.framework.TestCase;
+
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.id.PropertyId;
+import org.apache.jackrabbit.core.persistence.util.NodePropBundle.PropertyEntry;
+import org.apache.jackrabbit.core.util.StringIndex;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+
+/**
+ * A randomized test for the BundleBinding writer and reader classes.
+ */
+public class BundleBindingRandomizedTest extends TestCase {
+
+    private static final NameFactory factory = NameFactoryImpl.getInstance();
+
+    /**
+     * Tests serialization of a complex bundle.
+     */
+    public void testRandomBundle() throws Exception {
+        int seed = 0;
+        for (int i=0; i<100;) {
+            Random r = new Random(seed++);
+            NodePropBundle bundle;
+            try {
+                bundle = randomBundle(r);
+            } catch (IllegalArgumentException e) {
+                continue;
+            }
+            try {
+                if (tryBundleRoundtrip(bundle)) {
+                    i++;
+                }
+            } catch (Exception e) {
+                throw new Exception(
+                        "Error round-tripping bundle with seed " + seed, e);
+                
+            }
+        }
+    }
+    
+    private static NodePropBundle randomBundle(Random r) {
+        NodeId id = randomNodeId(r);
+        NodePropBundle bundle = new NodePropBundle(id);
+        bundle.setModCount((short) randomSize(r)); 
+        if (r.nextInt(10) > 0) {
+            bundle.setParentId(randomNodeId(r));
+        }
+        if (r.nextInt(100) > 0) {
+            bundle.setNodeTypeName(randomName(r));
+        }
+        if (r.nextInt(100) > 0) {
+            bundle.setMixinTypeNames(randomNameSet(r));
+        }
+        if (r.nextInt(10) > 0) {
+            bundle.setReferenceable(r.nextBoolean());
+        }
+        if (r.nextInt(10) > 0) {
+            bundle.setSharedSet(randomNodeIdSet(r));
+        }
+        int count = randomSize(r);
+        for (int i=0; i<count; i++) {
+            PropertyEntry p = randomProperty(id, r);
+            bundle.addProperty(p);
+        }
+        count = randomSize(r);
+        for (int i=0; i<count; i++) {
+            bundle.addChildNodeEntry(randomName(r), randomNodeId(r));
+        }
+        return bundle;
+    }
+    
+    private static boolean tryBundleRoundtrip(NodePropBundle bundle)
+            throws Exception {
+        BundleBinding binding;
+        StringIndex index = new StringIndex() {
+            @Override
+            public int stringToIndex(String string) {
+                return 0;
+            }
+            @Override
+            public String indexToString(int idx) {
+                return "";
+            }
+        };
+        binding = new BundleBinding(null, null, index, index, null);
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        try {
+            binding.writeBundle(buffer, bundle);
+        } catch (Exception e) {
+            // ignore - error found
+            return false;
+        }
+        byte[] bytes = buffer.toByteArray();
+        NodePropBundle b2 = binding.readBundle(
+                new ByteArrayInputStream(bytes), bundle.getId());
+        if (!bundle.equals(b2)) {
+            // if debugging is needed:
+            // buffer = new ByteArrayOutputStream();
+            // binding.writeBundle(buffer, bundle);
+            // bytes = buffer.toByteArray();
+            // b2 = binding.readBundle(
+            // new ByteArrayInputStream(bytes), bundle.getId());
+            assertEquals(bundle.toString(), b2.toString());
+        }
+        return true;
+    }
+    
+    private static PropertyEntry randomProperty(NodeId nodeId, Random r) {
+        PropertyEntry p = new PropertyEntry(
+                new PropertyId(nodeId, randomName(r)));        
+        int type = PropertyType.STRING;
+        if (r.nextInt(10) == 0) {
+            type = r.nextInt() + 15;
+        }
+        p.setType(type);
+        p.setModCount((short) randomSize(r)); 
+        boolean multiValued = r.nextBoolean();
+        p.setMultiValued(multiValued);
+        int size;
+        if (multiValued && r.nextInt(10) > 0) {
+            size = 1;
+        } else {
+            size = randomSize(r);
+        }
+        InternalValue[] values = new InternalValue[size];
+        for (int i = 0; i < size; i++) {
+            values[i] = randomValue(r);
+        }
+        p.setValues(values);
+        return p;
+    }
+    
+    private static InternalValue randomValue(Random r) {
+        if (r.nextInt(50) == 0) {
+            return null;
+        }
+        // TODO currently only string values
+        return InternalValue.create(randomString(r));
+    }
+
+    private static int randomSize(Random r) {
+        if (r.nextInt(20) == 0) {
+            return 0;
+        } else if (r.nextInt(20) == 0) {
+            return 1;
+        } else if (r.nextInt(20) == 0) {
+            return r.nextInt(10000);
+        }
+        return r.nextInt(5) + 1;
+    }
+    
+    private static Set<Name> randomNameSet(Random r) {
+        if (r.nextInt(100) == 0) {
+            return null;
+        }
+        int size = randomSize(r);
+        HashSet<Name> set = new HashSet<Name>();
+        for(int i=0; i<size; i++) {
+            set.add(randomName(r));
+        }
+        return set;
+    }
+    
+    private static Set<NodeId> randomNodeIdSet(Random r) {
+        if (r.nextInt(100) == 0) {
+            return null;
+        } else if (r.nextInt(10) == 0) {
+            return Collections.emptySet();
+        }
+        int size = randomSize(r);
+        HashSet<NodeId> set = new HashSet<NodeId>();
+        for(int i=0; i<size; i++) {
+            set.add(randomNodeId(r));
+        }
+        return set;
+    }
+    
+    private static NodeId randomNodeId(Random r) {
+        if (r.nextInt(100) == 0) {
+            return null;
+        }
+        return new NodeId(r.nextLong(), r.nextLong());
+    }
+    
+    private static Name randomName(Random r) {
+        if (r.nextInt(100) == 0) {
+            return null;
+        }
+        return factory.create(randomString(r), randomString(r));
+    }
+    
+    private static String randomString(Random r) {
+        int size = randomSize(r);
+        StringBuilder buff = new StringBuilder();
+        for (int i=0; i<size; i++) {
+            buff.append("abcd".charAt(r.nextInt(4)));
+        }
+        return buff.toString();
+    }
+    
+}

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/NodeCorruptionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/NodeCorruptionTest.java?rev=1518170&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/NodeCorruptionTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/NodeCorruptionTest.java Wed Aug 28 11:47:51 2013
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.core.persistence.util;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.test.AbstractJCRTest;
+import org.apache.jackrabbit.test.NotExecutableException;
+
+/**
+ * Tests that a node or a node bundle can not get internally corrupt.
+ */
+public class NodeCorruptionTest extends AbstractJCRTest {
+
+    public void testCopyMultiSingleValue() throws Exception {
+        Node root = superuser.getRootNode();
+        String nodeName = "testCopyMulti" + System.currentTimeMillis();
+        if (root.hasNode(nodeName)) {
+            root.getNode(nodeName).remove();
+            superuser.save();
+        }
+        Node test = root.addNode(nodeName);
+        test.setProperty("x", "Hi");
+        superuser.save();
+
+        String wsp = superuser.getWorkspace().getName();
+        String workspace2 = getAlternativeWorkspaceName();
+        if (workspace2 == null) {
+            throw new NotExecutableException();
+        }
+        Session s2 = getHelper().getSuperuserSession(workspace2);
+        s2.getWorkspace().clone(wsp, "/" + nodeName, "/" + nodeName, true);
+        
+        Node test2 = s2.getRootNode().getNode(nodeName);
+        test2.setProperty("x", (Value) null);
+        test2.setProperty("x", new String[]{});
+        s2.save();
+        
+        test.update(workspace2);
+
+        try {
+            Value[] values = test.getProperty("x").getValues();
+            assertEquals(0, values.length);
+        } catch (RepositoryException e) {
+            // if we get here, it's a bug, as it is a multi-valued property now
+            // anyway, let's see what happens if we try to read it as a single valued property
+            test.getProperty("x").getValue();
+            // even if that works: it's still a bug
+            throw e;
+        }
+        
+    }
+    
+    private String getAlternativeWorkspaceName() throws RepositoryException {
+        String altWsp = null;
+        String[] wsps = superuser.getWorkspace().getAccessibleWorkspaceNames();
+        if (wsps.length == 1) {
+            superuser.getWorkspace().createWorkspace("tmp");
+            altWsp = "tmp";
+        } else {
+            for (String name : wsps) {
+                if (!name.equals(superuser.getWorkspace().getName())) {
+                    altWsp = name;
+                    break;
+                }
+            }
+        }
+        return altWsp;
+    }
+    
+}
\ No newline at end of file

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/TestAll.java?rev=1518170&r1=1518169&r2=1518170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/persistence/util/TestAll.java Wed Aug 28 11:47:51 2013
@@ -27,6 +27,8 @@ public class TestAll extends TestCase {
 
         suite.addTestSuite(HashMapIndexTest.class);
         suite.addTestSuite(BundleBindingTest.class);
+        suite.addTestSuite(NodeCorruptionTest.class);
+        suite.addTestSuite(BundleBindingRandomizedTest.class);
 
         return suite;
     }