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();
+ }
+}