You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2013/10/18 21:38:45 UTC

svn commit: r1533608 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak: kernel/JsonSerializer.java kernel/JsopDiff.java kernel/NodeStoreKernel.java plugins/memory/AbstractBlob.java

Author: jukka
Date: Fri Oct 18 19:38:45 2013
New Revision: 1533608

URL: http://svn.apache.org/r1533608
Log:
OAK-987: Implement the MicroKernel API

Improved JSON serialization

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java?rev=1533608&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsonSerializer.java Fri Oct 18 19:38:45 2013
@@ -0,0 +1,154 @@
+/*
+ * 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.oak.kernel;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.apache.jackrabbit.oak.api.Type.BINARY;
+import static org.apache.jackrabbit.oak.api.Type.BOOLEAN;
+import static org.apache.jackrabbit.oak.api.Type.DOUBLE;
+import static org.apache.jackrabbit.oak.api.Type.LONG;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.mk.json.JsopBuilder;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.AbstractBlob;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+/**
+ * Utility class for serializing node and property states to JSON.
+ */
+class JsonSerializer {
+
+    private final JsopBuilder json;
+
+    private final int depth;
+
+    private final long offset;
+
+    private final int maxChildNodes;
+
+    private final boolean includeChildNodeCount;
+
+    private JsonSerializer(
+            JsopBuilder json, int depth, long offset, int maxChildNodes,
+            boolean includeChildNodeCount) {
+        this.json = checkNotNull(json);
+        this.depth = depth;
+        this.offset = offset;
+        this.maxChildNodes = maxChildNodes;
+        this.includeChildNodeCount = includeChildNodeCount;
+    }
+
+    JsonSerializer(int depth, long offset, int maxChildNodes) {
+        this(new JsopBuilder(), depth, offset, maxChildNodes, true);
+    }
+
+    JsonSerializer(JsopBuilder json) {
+        this(json, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, false);
+    }
+
+    protected JsonSerializer getChildSerializer() {
+        return new JsonSerializer(
+                json, depth - 1, 0, maxChildNodes, includeChildNodeCount);
+    }
+
+    protected String getBlobId(Blob blob) {
+        return AbstractBlob.calculateSha256(blob).toString();
+    }
+
+    void serialize(NodeState node) {
+        json.object();
+
+        for (PropertyState property : node.getProperties()) {
+            json.key(property.getName());
+            serialize(property);
+        }
+
+        if (includeChildNodeCount) {
+            json.key(":childNodeCount");
+            json.value(node.getChildNodeCount(Integer.MAX_VALUE));
+        }
+
+        int index = 0;
+        int count = 0;
+        for (ChildNodeEntry child : node.getChildNodeEntries()) {
+            if (index++ >= offset) {
+                if (count++ >= maxChildNodes) {
+                    break;
+                }
+
+                json.key(child.getName());
+                if (depth > 0) {
+                    getChildSerializer().serialize(child.getNodeState());
+                } else {
+                    json.object();
+                    json.endObject();
+                }
+            }
+        }
+
+        json.endObject();
+    }
+
+    void serialize(PropertyState property) {
+        Type<?> type = property.getType();
+        if (!type.isArray()) {
+            serialize(property, type, 0);
+        } else {
+            Type<?> base = type.getBaseType();
+            int count = property.count();
+            if (base == STRING || count > 0) {
+                json.array();
+                for (int i = 0; i < count; i++) {
+                    serialize(property, base, i);
+                }
+                json.endArray();
+            } else {
+                // type-safe encoding of an empty array
+                json.value(TypeCodes.EMPTY_ARRAY
+                        + PropertyType.nameFromValue(type.tag()));
+            }
+        }
+    }
+
+    void serialize(PropertyState property, Type<?> type, int index) {
+        if (type == BOOLEAN) {
+            json.value(property.getValue(BOOLEAN, index).booleanValue());
+        } else if (type == LONG) {
+            json.value(property.getValue(LONG, index).longValue());
+        } else if (type == BINARY) {
+            Blob blob = property.getValue(BINARY, index);
+            json.value(TypeCodes.encode(type.tag(), getBlobId(blob)));
+        } else  {
+            String value = property.getValue(STRING, index);
+            if (type != STRING || TypeCodes.split(value) != -1) {
+                value = TypeCodes.encode(type.tag(), value);
+            }
+            json.value(value);
+        }
+    }
+
+    public String toString() {
+        return json.toString();
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java?rev=1533608&r1=1533607&r2=1533608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/JsopDiff.java Fri Oct 18 19:38:45 2013
@@ -16,21 +16,12 @@
  */
 package org.apache.jackrabbit.oak.kernel;
 
-import static org.apache.jackrabbit.oak.api.Type.BINARIES;
-import static org.apache.jackrabbit.oak.api.Type.BOOLEANS;
-import static org.apache.jackrabbit.oak.api.Type.LONGS;
-import static org.apache.jackrabbit.oak.api.Type.STRINGS;
-
 import java.io.IOException;
 
-import javax.jcr.PropertyType;
-
 import org.apache.jackrabbit.mk.json.JsopBuilder;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
 
@@ -41,7 +32,7 @@ public class JsopDiff implements NodeSta
 
     private final KernelNodeStore store;
 
-    protected final JsopBuilder jsop;
+    private final JsopBuilder jsop;
 
     protected final String path;
 
@@ -109,7 +100,6 @@ public class JsopDiff implements NodeSta
         return diff.toString();
     }
 
-
     protected JsopDiff createChildDiff(JsopBuilder jsop, String path) {
         return new JsopDiff(store, jsop, path);
     }
@@ -119,14 +109,14 @@ public class JsopDiff implements NodeSta
     @Override
     public boolean propertyAdded(PropertyState after) {
         jsop.tag('^').key(buildPath(after.getName()));
-        toJson(after, jsop);
+        new DiffJsonSerializer().serialize(after);
         return true;
     }
 
     @Override
     public boolean propertyChanged(PropertyState before, PropertyState after) {
         jsop.tag('^').key(buildPath(after.getName()));
-        toJson(after, jsop);
+        new DiffJsonSerializer().serialize(after);
         return true;
     }
 
@@ -139,7 +129,7 @@ public class JsopDiff implements NodeSta
     @Override
     public boolean childNodeAdded(String name, NodeState after) {
         jsop.tag('+').key(buildPath(name));
-        toJson(after, jsop);
+        new DiffJsonSerializer().serialize(after);
         return true;
     }
 
@@ -169,67 +159,10 @@ public class JsopDiff implements NodeSta
         return PathUtils.concat(path, name);
     }
 
-    private void toJson(NodeState nodeState, JsopBuilder jsop) {
-        jsop.object();
-        for (PropertyState property : nodeState.getProperties()) {
-            jsop.key(property.getName());
-            toJson(property, jsop);
-        }
-        for (ChildNodeEntry child : nodeState.getChildNodeEntries()) {
-            jsop.key(child.getName());
-            toJson(child.getNodeState(), jsop);
-        }
-        jsop.endObject();
-    }
-
-    private void toJson(PropertyState propertyState, JsopBuilder jsop) {
-        if (propertyState.isArray()) {
-            Type<?> type = propertyState.getType();
-            if (type == STRINGS || propertyState.count() > 0) {
-                jsop.array();
-                toJsonValue(propertyState, jsop);
-                jsop.endArray();
-            } else {
-                jsop.value(TypeCodes.EMPTY_ARRAY
-                        + PropertyType.nameFromValue(type.tag()));
-            }
-        } else {
-            toJsonValue(propertyState, jsop);
-        }
-    }
-
-    private void toJsonValue(PropertyState property, JsopBuilder jsop) {
-        int type = property.getType().tag();
-        switch (type) {
-            case PropertyType.BOOLEAN:
-                for (boolean value : property.getValue(BOOLEANS)) {
-                    jsop.value(value);
-                }
-                break;
-            case PropertyType.LONG:
-                for (long value : property.getValue(LONGS)) {
-                    jsop.value(value);
-                }
-                break;
-            case PropertyType.BINARY:
-                for (Blob value : property.getValue(BINARIES)) {
-                    String binId = writeBlob(value);
-                    jsop.value(TypeCodes.encode(type, binId));
-                }
-                break;
-            default:
-                for (String value : property.getValue(STRINGS)) {
-                    if (PropertyType.STRING != type || TypeCodes.split(value) != -1) {
-                        value = TypeCodes.encode(type, value);
-                    }
-                    jsop.value(value);
-                }
-                break;
-        }
-    }
-
     /**
-     * Make sure {@code blob} is persisted and return the id of the persisted blob.
+     * Make sure {@code blob} is persisted and return the id of
+     * the persisted blob.
+     *
      * @param blob  blob to persist
      * @return  id of the persisted blob
      */
@@ -247,4 +180,29 @@ public class JsopDiff implements NodeSta
         return kernelBlob.getBinaryID();
     }
 
+    private class DiffJsonSerializer extends JsonSerializer {
+
+        DiffJsonSerializer() {
+            super(jsop);
+        }
+
+        @Override
+        protected JsonSerializer getChildSerializer() {
+            return new DiffJsonSerializer();
+        }
+
+        /**
+         * Make sure {@code blob} is persisted and return the id of
+         * the persisted blob.
+         *
+         * @param blob  blob to persist
+         * @return  id of the persisted blob
+         */
+        @Override
+        protected String getBlobId(Blob blob) {
+            return writeBlob(blob);
+        }
+
+    }
+
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java?rev=1533608&r1=1533607&r2=1533608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/NodeStoreKernel.java Fri Oct 18 19:38:45 2013
@@ -37,12 +37,10 @@ import org.apache.jackrabbit.mk.json.Jso
 import org.apache.jackrabbit.mk.json.JsopTokenizer;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
-import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
-import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
@@ -162,16 +160,20 @@ public class NodeStoreKernel implements 
             case '>':
                 tokenizer.read(':');
                 String moveTarget = tokenizer.readString();
-                getNode(builder, path).moveTo(
+                if (!getNode(builder, path).moveTo(
                         getNode(builder, getParentPath(moveTarget)),
-                        getName(moveTarget));
+                        getName(moveTarget))) {
+                    throw new MicroKernelException("Move failed");
+                }
                 break;
             case '*':
                 tokenizer.read(':');
                 String copyTarget = tokenizer.readString();
-                getNode(builder, path).copyTo(
+                if (!getNode(builder, path).copyTo(
                         getNode(builder, getParentPath(copyTarget)),
-                        getName(copyTarget));
+                        getName(copyTarget))) {
+                    throw new MicroKernelException("Copy failed");
+                }
                 break;
             default:
                 throw new MicroKernelException(
@@ -335,77 +337,18 @@ public class NodeStoreKernel implements 
         }
 
         if (node.exists()) {
-            JsopBuilder json = new JsopBuilder();
             if (maxChildNodes < 0) {
                 maxChildNodes = Integer.MAX_VALUE;
             }
-            serialize(getNode(revisionId, path), json, depth, offset, maxChildNodes);
+            JsonSerializer json =
+                    new JsonSerializer(depth, offset, maxChildNodes);
+            json.serialize(node);
             return json.toString();
         } else {
             return null;
         }
     }
 
-    private void serialize(
-            NodeState state, JsopBuilder json,
-            int depth, long offset, int maxChildNodes) {
-        json.object();
-
-        for (PropertyState property : state.getProperties()) {
-            json.key(property.getName());
-            Type<?> type = property.getType();
-            if (type.isArray()) {
-                type = type.getBaseType();
-                json.array();
-                for (int i = 0; i < property.size(); i++) {
-                    if (type == Type.BOOLEAN) {
-                        json.value(property.getValue(Type.BOOLEAN, i).booleanValue());
-                    } else if (type == Type.LONG) {
-                        json.value(property.getValue(Type.LONG, i).longValue());
-                    } else if (type == Type.DOUBLE) {
-                        json.encodedValue(property.getValue(Type.DOUBLE, i).toString());
-                    } else {
-                        json.value(property.getValue(Type.STRING, i));
-                    }
-                }
-                json.endArray();
-            } else if (type == Type.BOOLEAN) {
-                json.value(property.getValue(Type.BOOLEAN).booleanValue());
-            } else if (type == Type.LONG) {
-                json.value(property.getValue(Type.LONG).longValue());
-            } else if (type == Type.DOUBLE) {
-                json.encodedValue(property.getValue(Type.DOUBLE).toString());
-            } else {
-                json.value(property.getValue(Type.STRING));
-            }
-        }
-
-        json.key(":childNodeCount");
-        json.value(state.getChildNodeCount(Long.MAX_VALUE));
-
-        long index = 0;
-        int count = 0;
-        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
-            if (index++ >= offset) {
-                if (count++ >= maxChildNodes) {
-                    break;
-                }
-
-                json.key(entry.getName());
-                if (depth > 0) {
-                    serialize(
-                            entry.getNodeState(), json,
-                            depth - 1, 0, maxChildNodes);
-                } else {
-                    json.object();
-                    json.endObject();
-                }
-            }
-        }
-
-        json.endObject();
-    }
-
     @Override
     public synchronized String commit(
             String path, String jsonDiff, String revisionId, String message)

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java?rev=1533608&r1=1533607&r2=1533608&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/AbstractBlob.java Fri Oct 18 19:38:45 2013
@@ -35,34 +35,32 @@ import org.apache.jackrabbit.oak.api.Blo
  */
 public abstract class AbstractBlob implements Blob {
 
-    private static class BlobSupplier implements InputSupplier<InputStream> {
-
-        private final Blob blob;
-
-        private BlobSupplier(Blob blob) {
-            this.blob = blob;
-        }
-
-        @Override
-        public InputStream getInput() throws IOException {
-            return blob.getNewStream();
-        }
-
+    private static InputSupplier<InputStream> supplier(final Blob blob) {
+        return new InputSupplier<InputStream>() {
+            @Override
+            public InputStream getInput() throws IOException {
+                return blob.getNewStream();
+            }
+        };
     }
 
     public static boolean equal(Blob a, Blob b) {
         try {
-            return ByteStreams.equal(new BlobSupplier(a), new BlobSupplier(b));
+            return ByteStreams.equal(supplier(a), supplier(b));
         } catch (IOException e) {
-            throw new RuntimeException("Blob equality check failed", e);
+            throw new IllegalStateException("Blob equality check failed", e);
         }
     }
 
-    private static HashCode calculateSha256(final Blob blob) {
-        try {
-            return ByteStreams.hash(new BlobSupplier(blob), Hashing.sha256());
-        } catch (IOException e) {
-            throw new RuntimeException("Blob hash calculation failed", e);
+    public static HashCode calculateSha256(Blob blob) {
+        if (blob instanceof AbstractBlob) {
+            return ((AbstractBlob) blob).getSha256();
+        } else {
+            try {
+                return ByteStreams.hash(supplier(blob), Hashing.sha256());
+            } catch (IOException e) {
+                throw new IllegalStateException("Hash calculation failed", e);
+            }
         }
     }