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 2011/06/08 11:25:14 UTC

svn commit: r1133303 - in /jackrabbit/sandbox/microkernel/src: main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java test/java/org/apache/jackrabbit/mk/HelloWorld.java

Author: thomasm
Date: Wed Jun  8 09:25:14 2011
New Revision: 1133303

URL: http://svn.apache.org/viewvc?rev=1133303&view=rev
Log:
A simple in-memory micro kernel implementation, including a test case.

Added:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java?rev=1133303&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java Wed Jun  8 09:25:14 2011
@@ -0,0 +1,167 @@
+/*
+ * 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.mk.mem;
+
+import java.io.InputStream;
+import java.util.TreeMap;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+
+/**
+ * An in-memory implementation.
+ * Useful for testing.
+ */
+public class MemoryKernelImpl implements MicroKernel {
+
+    private final MemoryDataStore ds = new MemoryDataStore();
+    private long headRevId;
+    private TreeMap<Long, NodeImpl> roots = new TreeMap<Long, NodeImpl>();
+    private NodeImpl headRoot;
+
+    public MemoryKernelImpl() {
+        headRoot = new NodeImpl(headRevId);
+        commit();
+    }
+
+    private void commit() {
+        roots.put(headRevId, headRoot);
+    }
+
+    public synchronized String commit(String path, String jsonDiff, String revisionId) {
+        // TODO do we really need 'path'? store the path is in the diff instead
+        // TODO what is the exact meaning of 'revisionId'? is it allowed to commit
+        // using an old revision, if yes when is it allowed, or how is it different from using head?
+        // TODO increment headRevId less often? commit in the background?
+        // would be possible if we don't (always) return the head revision
+        headRevId++;
+        apply(jsonDiff);
+        commit();
+        return getHeadRevision();
+    }
+
+    private void apply(String jsonDiff) {
+        JsopTokenizer t = new JsopTokenizer(jsonDiff);
+        while (true) {
+            int r = t.read();
+            if (r == JsopTokenizer.END) {
+                break;
+            }
+            String path;
+            switch (r) {
+            case '+':
+                path = t.readString();
+                t.read(':');
+                t.read('{');
+                NodeImpl n = NodeImpl.parse(t, headRevId);
+                headRoot = headRoot.cloneAndAddChildNode(path, n, headRevId);
+                break;
+            case '-':
+                path = t.readString();
+                headRoot = headRoot.cloneAndRemoveChildNode(path, headRevId);
+                break;
+            default:
+                throw new AssertionError("token type: " + t.getTokenType());
+            }
+        }
+    }
+
+    public long getChildNodeCount(String path, String revisionId) {
+        // TODO is it required? might as well use getNodes(path) with depth 0
+        return getNode(path, revisionId).getChildNodeCount();
+    }
+
+    public String getChildNodes(String path, long offset, long count, int depth, String revisionId) {
+        return getNode(path, revisionId).toString(false, depth, (int) offset, (int) count);
+    }
+
+    private NodeImpl getNode(String path, String revisionId) {
+        if (!path.startsWith("/")) {
+            throw new IllegalArgumentException("not a root path: " + path);
+        }
+        path = path.substring(1);
+        long revId = parseRevisionId(revisionId);
+        NodeImpl root = roots.get(revId);
+        NodeImpl n = root.getNode(path);
+        if (n == null) {
+            throw new RuntimeException("path not found: " + path);
+        }
+        return n;
+    }
+
+    public String getHeadRevision() {
+        return getRevisionId(headRevId);
+    }
+
+    public String getJournal(String fromRevisionId, String toRevisionId) {
+        long fromRevId = parseRevisionId(fromRevisionId);
+        long toRevId = parseRevisionId(fromRevisionId);
+        return "";
+    }
+
+    public String getNodes(String path, int depth, String revisionId) {
+        if (!path.startsWith("/")) {
+            throw new IllegalArgumentException("not a root path: " + path);
+        }
+        path = path.substring(1);
+        long revId = parseRevisionId(revisionId);
+        NodeImpl root = roots.get(revId);
+        NodeImpl n = root.getNode(path);
+        if (n == null) {
+            throw new RuntimeException("path not found: " + path);
+        }
+        return n.toString(true, depth, -1, -1);
+    }
+
+    public String getRevisions(long since, int maxEntries) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public boolean nodeExists(String path, String revisionId) {
+        if (!path.startsWith("/")) {
+            throw new IllegalArgumentException("not a root path: " + path);
+        }
+        path = path.substring(1);
+        long revId = parseRevisionId(revisionId);
+        NodeImpl n = roots.get(revId);
+        if (n == null) {
+            return false;
+        }
+        return n.exists(path);
+    }
+
+    private static String getRevisionId(long revId) {
+        return Long.toHexString(revId);
+    }
+
+    // TODO revisionId could be a long
+    private static long parseRevisionId(String revisionId) {
+        return Long.parseLong(revisionId, 16);
+    }
+
+    public long getLength(String blobId) {
+        return ds.getLength(blobId);
+    }
+
+    public int read(String blobId, long pos, byte[] buff, int off, int length) {
+        return ds.read(blobId, pos, buff, off, length);
+    }
+
+    public String write(InputStream in) {
+        return ds.write(in);
+    }
+
+}

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java?rev=1133303&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java Wed Jun  8 09:25:14 2011
@@ -0,0 +1,234 @@
+/*
+ * 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.mk.mem;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+public class NodeImpl {
+
+    final long revId;
+    HashMap<String, Val> properties;
+    LinkedHashMap<String, NodeImpl> childNodes;
+
+    NodeImpl(long revId) {
+        this.revId = revId;
+    }
+
+    public NodeImpl clone(long revId) {
+        if (revId == this.revId) {
+            return this;
+        }
+        NodeImpl clone = new NodeImpl(revId);
+        if (properties != null) {
+            clone.properties = new HashMap<String, Val>(properties);
+        }
+        if (childNodes != null) {
+            clone.childNodes = new LinkedHashMap<String, NodeImpl>(childNodes);
+        }
+        return clone;
+    }
+
+    public long getChildNodeCount() {
+        return childNodes == null ? 0 : childNodes.size();
+    }
+
+    public boolean exists(String path) {
+        if (childNodes == null) {
+            return false;
+        }
+        int index = path.indexOf('/');
+        if (index < 0) {
+            return childNodes.containsKey(path);
+        }
+        String child = path.substring(0, index);
+        NodeImpl n = childNodes.get(child);
+        if (n == null) {
+            return false;
+        }
+        return n.exists(path.substring(index + 1));
+    }
+
+    public NodeImpl getNode(String path) {
+        if (path.length() == 0) {
+            return this;
+        }
+        if (childNodes == null) {
+            return null;
+        }
+        int index = path.indexOf('/');
+        if (index < 0) {
+            return childNodes.get(path);
+        }
+        String child = path.substring(0, index);
+        NodeImpl n = childNodes.get(child);
+        if (n == null) {
+            return null;
+        }
+        return n.getNode(path.substring(index + 1));
+    }
+
+    public NodeImpl cloneAndAddChildNode(String path, NodeImpl newNode, long revId) {
+        int index = path.indexOf('/');
+        if (index < 0) {
+            NodeImpl clone = clone(revId);
+            clone.addChildNode(path, newNode);
+            return clone;
+        }
+        String child = path.substring(0, index);
+        NodeImpl n = childNodes.get(child);
+        if (n == null) {
+            throw new RuntimeException("Node not found: " + path);
+        }
+        NodeImpl n2 = n.cloneAndAddChildNode(path.substring(index + 1), newNode, revId);
+        NodeImpl c = clone(revId);
+        c.childNodes.put(child, n2);
+        return c;
+    }
+
+    public NodeImpl cloneAndRemoveChildNode(String path, long revId) {
+        int index = path.indexOf('/');
+        if (index < 0) {
+            NodeImpl clone = clone(revId);
+            clone.removeChildNode(path);
+            return clone;
+        }
+        String child = path.substring(0, index);
+        NodeImpl n = childNodes.get(child);
+        if (n == null) {
+            throw new RuntimeException("Node not found: " + path);
+        }
+        NodeImpl n2 = n.cloneAndRemoveChildNode(path.substring(index + 1), revId);
+        NodeImpl c = clone(revId);
+        c.childNodes.put(child, n2);
+        return c;
+    }
+
+    public String toString() {
+        return toString(true, 1, -1, -1);
+    }
+
+    public String toString(boolean includeProperties, int depth, int childOffset, int childCount) {
+        JsopBuilder json = new JsopBuilder();
+        if (includeProperties) {
+            json.beginObject();
+            if (properties != null) {
+                for (Entry<String, Val> e : properties.entrySet()) {
+                    json.addProperty(e.getKey(), e.getValue().toString());
+                }
+            }
+        }
+        if (childNodes != null) {
+            if (depth > 0) {
+                for (Entry<String, NodeImpl> e : childNodes.entrySet()) {
+                    json.addProperty(e.getKey(), e.getValue().toString(true, depth - 1, -1, -1));
+                }
+            }
+            json.addProperty(":childNodeCount", "" + childNodes.size());
+        }
+        if (includeProperties) {
+            json.endObject();
+        }
+        return json.toString();
+    }
+
+    void addChildNode(String name, NodeImpl node) {
+        if (childNodes == null) {
+            childNodes = new LinkedHashMap<String, NodeImpl>();
+        } else if (childNodes.containsKey(name)) {
+            throw new RuntimeException("Node already exiss: " + name);
+        }
+        childNodes.put(name, node);
+    }
+
+    void removeChildNode(String name) {
+        if (childNodes == null) {
+            throw new RuntimeException("Node not found: " + name);
+        }
+        if (childNodes.remove(name) == null) {
+            throw new RuntimeException("Node not found: " + name);
+        }
+        if (childNodes.size() == 0) {
+            childNodes = null;
+        }
+    }
+
+    void setProperty(String name, Val value) {
+        if (properties == null) {
+            properties = new HashMap<String, Val>();
+        }
+        if (value == null) {
+            properties.remove(name);
+        } else {
+            properties.put(name, value);
+        }
+    }
+
+    static NodeImpl parse(JsopTokenizer t, long revId) {
+        NodeImpl node = new NodeImpl(revId);
+        if (!t.matches('}')) {
+            do {
+                String key = t.readString();
+                t.read(':');
+                if (t.matches('{')) {
+                    node.addChildNode(key, parse(t, revId));
+                } else {
+                    Val val = parseValue(t);
+                    node.setProperty(key, val);
+                }
+            } while (t.matches(','));
+            t.read('}');
+        }
+        return node;
+    }
+
+    static Val parseValue(JsopTokenizer t) {
+        if (t.matches('[')) {
+            ArrayList<Val> list = new ArrayList<Val>();
+            if (!t.matches(']')) {
+                do {
+                    list.add(parseValue(t));
+                } while (t.matches(','));
+                t.read(']');
+            }
+            Val[] array = new Val[list.size()];
+            list.toArray(array);
+            return Val.get(array);
+        } else if (t.matches(JsopTokenizer.NUMBER)) {
+            String token = t.getToken();
+            if (token.indexOf('.') >= 0) {
+                if (token.indexOf('e') >= 0) {
+                    return Val.get(Double.parseDouble(token));
+                }
+                return Val.get(PropertyType.DECIMAL, token);
+            }
+            return Val.get(Long.parseLong(token));
+        } else if (t.matches(JsopTokenizer.TRUE)) {
+            return Val.TRUE;
+        } else if (t.matches(JsopTokenizer.FALSE)) {
+            return Val.FALSE;
+        } else if (t.matches(JsopTokenizer.COMMENT)) {
+            String typeName = t.getToken().trim();
+            int type = PropertyType.valueFromName(typeName);
+            return Val.get(type, t.readString());
+        }
+        return Val.get(t.readString());
+    }
+
+}

Added: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java?rev=1133303&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java (added)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java Wed Jun  8 09:25:14 2011
@@ -0,0 +1,58 @@
+/*
+ * 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.mk;
+
+import java.io.File;
+import org.apache.jackrabbit.mk.mem.MemoryKernelImpl;
+
+/**
+ * A simple hello world app.
+ */
+public class HelloWorld {
+
+    public static void main(String... args) {
+        String dir = ".";
+        new File(dir, ".mk/db/revs.h2.db").delete();
+
+        // MicroKernelImpl mk = new MicroKernelImpl(System.getProperty("homeDir", "."));
+        MemoryKernelImpl mk = new MemoryKernelImpl();
+
+        String head = mk.getHeadRevision();
+
+        System.out.println("head: " + head);
+        // head: b452fb882383cce6fdd123a78b5f1070728e2a0e
+
+        if (mk.nodeExists("/test", head)) {
+            // head = mk.commit(null, "- \"/test\"", head);
+            head = mk.commit("/", "-\"test\"", head);
+        }
+        // head = mk.commit(null, "+\"/test\" : {\"name\":\"hello\"}", head);
+        head = mk.commit("/", "+\"test\" : {\"name\":\"hello\"}", head);
+
+        // head = mk.getHeadRevision();
+        String s = mk.getNodes("/test", 0, head);
+        // {":name":"test","name":"hello",":childNodeCount":0}
+
+        System.out.println(s);
+
+        head = mk.commit("/", "-\"test\"", head);
+        s = mk.getNodes("/", 0, head);
+        System.out.println(s);
+
+        // mk.dispose();
+    }
+}