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 th...@apache.org on 2013/02/13 09:53:48 UTC
svn commit: r1445508 - in
/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype:
DocumentStore.java MemoryDocumentStore.java MongoMK.java Node.java
UpdateOp.java Utils.java
Author: thomasm
Date: Wed Feb 13 08:53:48 2013
New Revision: 1445508
URL: http://svn.apache.org/r1445508
Log:
OAK-619 Lock-free MongoMK implementation
Added:
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java?rev=1445508&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/DocumentStore.java Wed Feb 13 08:53:48 2013
@@ -0,0 +1,34 @@
+/*
+ * 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.mongomk.prototype;
+
+import java.util.Map;
+
+/**
+ * The interface for the backend storage for documents.
+ */
+public interface DocumentStore {
+
+ enum Collection { NODES }
+
+ Map<String, Object> find(Collection collection, String key);
+
+ void remove(Collection collection, String key);
+
+ Map<String, Object> createOrUpdate(Collection collection, UpdateOp update);
+
+}
Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java?rev=1445508&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MemoryDocumentStore.java Wed Feb 13 08:53:48 2013
@@ -0,0 +1,183 @@
+/*
+ * 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.mongomk.prototype;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import org.apache.jackrabbit.mongomk.prototype.UpdateOp.Operation;
+
+/**
+ * Emulates a MongoDB store (possibly consisting of multiple shards and
+ * replicas).
+ */
+public class MemoryDocumentStore implements DocumentStore {
+
+ /**
+ * The 'nodes' collection. It contains all the node data, with one document
+ * per node, and the path as the primary key. Each document possibly
+ * contains multiple revisions.
+ * <p>
+ * Key: the path, value: the node data (possibly multiple revisions)
+ * <p>
+ * Old revisions are removed after some time, either by the process that
+ * removed or updated the node, lazily when reading, or in a background
+ * process.
+ */
+ private ConcurrentSkipListMap<String, Map<String, Object>> nodes =
+ new ConcurrentSkipListMap<String, Map<String, Object>>();
+
+ /**
+ * Get a document. The returned map is a clone (the caller
+ * can modify it without affecting the stored version).
+ *
+ * @param collection the collection
+ * @param path the path
+ * @return the map, or null if not found
+ */
+ public Map<String, Object> find(Collection collection, String path) {
+ ConcurrentSkipListMap<String, Map<String, Object>> map = getMap(collection);
+ Map<String, Object> n = map.get(path);
+ if (n == null) {
+ return null;
+ }
+ Map<String, Object> copy = Utils.newMap();
+ synchronized (n) {
+ copy.putAll(n);
+ return copy;
+ }
+ }
+
+ /**
+ * Remove a document.
+ *
+ * @param collection the collection
+ * @param path the path
+ */
+ public void remove(Collection collection, String path) {
+ getMap(collection).remove(path);
+ }
+
+ /**
+ * Get the in-memory map for this collection.
+ *
+ * @param collection the collection
+ * @return the map
+ */
+ private ConcurrentSkipListMap<String, Map<String, Object>> getMap(Collection collection) {
+ switch (collection) {
+ case NODES:
+ return nodes;
+ default:
+ throw new IllegalArgumentException(collection.name());
+ }
+ }
+
+ /**
+ * Create or update a document. For MongoDb, this is using "findAndModify" with
+ * the "upsert" flag (insert or update).
+ *
+ * @param collection the collection
+ * @param update the update operation
+ * @return the old document, or null if there was no
+ */
+ public Map<String, Object> createOrUpdate(Collection collection, UpdateOp update) {
+ ConcurrentSkipListMap<String, Map<String, Object>> map = getMap(collection);
+ Map<String, Object> n;
+ Map<String, Object> oldNode;
+
+ // get the node if it's there
+ oldNode = n = map.get(update.key);
+
+ if (n == null) {
+ // for a new node, add it (without synchronization)
+ n = Utils.newMap();
+ oldNode = map.putIfAbsent(update.key, n);
+ if (oldNode != null) {
+ // somebody else added it at the same time
+ n = oldNode;
+ }
+ }
+ if (oldNode != null) {
+ // clone the old node
+ // (document level operations are synchronized)
+ Map<String, Object> old = Utils.newMap();
+ synchronized (oldNode) {
+ old.putAll(oldNode);
+ }
+ oldNode = old;
+ }
+ // update the document
+ // (document level operations are synchronized)
+ synchronized (n) {
+ for (Entry<String, Operation> e : update.changes.entrySet()) {
+ String k = e.getKey();
+ Object old = n.get(k);
+ Operation op = e.getValue();
+ switch (op.type) {
+ case SET: {
+ n.put(k, op.value);
+ break;
+ }
+ case INCREMENT: {
+ Long x = (Long) op.value;
+ if (old == null) {
+ old = 0L;
+ }
+ n.put(k, ((Long) old) + x);
+ break;
+ }
+ case ADD_MAP_ENTRY: {
+ @SuppressWarnings("unchecked")
+ Map<String, String> m = (Map<String, String>) old;
+ if (m == null) {
+ m = Utils.newMap();
+ n.put(k, m);
+ }
+ m.put(op.subKey.toString(), op.value.toString());
+ break;
+ }
+ case REMOVE_MAP_ENTRY: {
+ @SuppressWarnings("unchecked")
+ Map<String, String> m = (Map<String, String>) old;
+ if (m != null) {
+ m.remove(op.subKey.toString());
+ }
+ break;
+ }
+ }
+ }
+ }
+ return oldNode;
+ }
+
+ public String toString() {
+ StringBuilder buff = new StringBuilder();
+ buff.append("Nodes:\n");
+ for(String p : nodes.keySet()) {
+ buff.append("Path: ").append(p).append('\n');
+ Map<String, Object> e = nodes.get(p);
+ for(String prop : e.keySet()) {
+ buff.append(prop).append('=').append(e.get(prop)).append('\n');
+ }
+ buff.append("\n");
+ }
+ return buff.toString();
+ }
+
+}
Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java?rev=1445508&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/MongoMK.java Wed Feb 13 08:53:48 2013
@@ -0,0 +1,263 @@
+/*
+ * 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.mongomk.prototype;
+
+import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.api.MicroKernelException;
+
+public class MongoMK implements MicroKernel {
+
+ /**
+ * The MongoDB store (might be used by multiple MongoMKs).
+ */
+ private final DocumentStore store;
+
+ /**
+ * The unique cluster id, similar to the unique machine id in MongoDB.
+ */
+ private final int clusterId;
+
+ /**
+ * The node cache.
+ *
+ * Key: path@rev
+ * Value: node
+ */
+ // TODO: should be path@id
+ private final Map<String, Node> nodeCache = new Cache<String, Node>(1024);
+
+ /**
+ * For revisions that are older than this many seconds, the MongoMK will
+ * assume the revision is valid. For more recent changes, the MongoMK needs
+ * to verify it first (by reading the revision root). The default is
+ * Integer.MAX_VALUE, meaning no revisions are trusted. Once the garbage
+ * collector removes old revisions, this value is changed.
+ */
+ private static final int trustedRevisionAge = Integer.MAX_VALUE;
+
+ /**
+ * The set of known valid revision.
+ * The key is the revision id, the value is 1 (because a cache can't be a set).
+ */
+ private final Map<String, Long> revCache = new Cache<String, Long>(1024);
+
+ /**
+ * Create a new MongoMK.
+ *
+ * @param store the store (might be shared)
+ * @param clusterId the cluster id (must be unique)
+ */
+ public MongoMK(MemoryDocumentStore store, int clusterId) {
+ this.store = store;
+ this.clusterId = clusterId;
+ }
+
+ String getNewRevision() {
+ return Utils.createRevision(clusterId);
+ }
+
+ /**
+ * Get the node for the given path and revision. The returned object might
+ * not be modified directly.
+ *
+ * @param path
+ * @param rev
+ * @return the node
+ */
+ Node getNode(String path, String rev) {
+ String key = path + "@" + rev;
+ Node node = nodeCache.get(key);
+ if (node == null) {
+ node = readNode(path, rev);
+ if (node != null) {
+ nodeCache.put(key, node);
+ }
+ }
+ return node;
+ }
+
+ /**
+ * Try to add a node.
+ *
+ * @param rev the revision
+ * @param n the node
+ * @throw IllegalStateException if the node already existed
+ */
+ public void addNode(String rev, String commitRoot, Node n) {
+ UpdateOp node = new UpdateOp(n.path);
+ int depth = Utils.pathDepth(n.path);
+ node.set("_path", n.path);
+ node.set("_pathDepth", depth);
+ int commitDepth = depth - Utils.pathDepth(commitRoot);
+ node.addMapEntry("_commitDepth", rev, commitDepth);
+
+ // the affected (direct) children of this revision
+ node.addMapEntry("_affectedChildren", rev, "");
+
+ node.increment("_changeCount", 1L);
+// setCommitRoot(path);
+
+
+ for (String p : n.properties.keySet()) {
+ node.addMapEntry(p, rev, n.properties.get(p));
+ }
+ Map<String, Object> map = store.createOrUpdate(DocumentStore.Collection.NODES, node);
+ if (map != null) {
+ // TODO rollback changes
+ throw new IllegalStateException("Node already exists: " + n.path);
+ }
+ }
+
+ private Node readNode(String path, String rev) {
+ Map<String, Object> map = store.find(DocumentStore.Collection.NODES, path);
+ if (map == null) {
+ return null;
+ }
+ Node n = new Node(path, rev);
+ for(String key : map.keySet()) {
+ Object v = map.get(key);
+
+ }
+ return n;
+ }
+
+ @Override
+ public String getHeadRevision() throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getRevisionHistory(long since, int maxEntries, String path)
+ throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String waitForCommit(String oldHeadRevisionId, long timeout)
+ throws MicroKernelException, InterruptedException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getJournal(String fromRevisionId, String toRevisionId,
+ String path) throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String diff(String fromRevisionId, String toRevisionId, String path,
+ int depth) throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean nodeExists(String path, String revisionId)
+ throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public long getChildNodeCount(String path, String revisionId)
+ throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String getNodes(String path, String revisionId, int depth,
+ long offset, int maxChildNodes, String filter)
+ throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String commit(String path, String jsonDiff, String revisionId,
+ String message) throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String branch(String trunkRevisionId) throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String merge(String branchRevisionId, String message)
+ throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ @Nonnull
+ public String rebase(@Nonnull String branchRevisionId, String newBaseRevisionId)
+ throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public long getLength(String blobId) throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public int read(String blobId, long pos, byte[] buff, int off, int length)
+ throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String write(InputStream in) throws MicroKernelException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ static class Cache<K, V> extends LinkedHashMap<K, V> {
+
+ private static final long serialVersionUID = 1L;
+ private int size;
+
+ Cache(int size) {
+ super(size, (float) 0.75, true);
+ this.size = size;
+ }
+
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+ return size() > size;
+ }
+
+ }
+
+}
Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java?rev=1445508&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Node.java Wed Feb 13 08:53:48 2013
@@ -0,0 +1,48 @@
+/*
+ * 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.mongomk.prototype;
+
+import java.util.Map;
+
+/**
+ * Represents a node held in memory (in the cache for example).
+ */
+public class Node {
+
+ final String path;
+ final String rev;
+ final Map<String, String> properties = Utils.newMap();
+
+ Node(String path, String rev) {
+ this.path = path;
+ this.rev = rev;
+ }
+
+ void setProperty(String propertyName, String value) {
+ properties.put(propertyName, value);
+ }
+
+ public String toString() {
+ StringBuilder buff = new StringBuilder();
+ buff.append("path: ").append(path).append('\n');
+ buff.append("rev: ").append(rev).append('\n');
+ buff.append(properties);
+ buff.append('\n');
+ return buff.toString();
+ }
+
+}
Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java?rev=1445508&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/UpdateOp.java Wed Feb 13 08:53:48 2013
@@ -0,0 +1,139 @@
+/*
+ * 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.mongomk.prototype;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A MongoDB "update" operation for one node.
+ */
+public class UpdateOp {
+
+ final String key;
+
+ final Map<String, Operation> changes = new TreeMap<String, Operation>();
+
+ /**
+ * Create an update operation for the given document. The commit root is assumed
+ * to be the path, unless this is changed later on.
+ *
+ * @param path the path
+ * @param rev the revision
+ */
+ UpdateOp(String key) {
+ this.key = key;
+ }
+
+ /**
+ * Add a new map entry for this revision.
+ *
+ * @param property the property
+ * @param value the value
+ */
+ void addMapEntry(String property, String subKey, Object value) {
+ Operation op = new Operation();
+ op.type = Operation.Type.ADD_MAP_ENTRY;
+ op.subKey = subKey;
+ op.value = value;
+ changes.put(property, op);
+ }
+
+ /**
+ * Set the property.
+ *
+ * @param property the property name
+ * @param value the value
+ */
+ void set(String property, Object value) {
+ Operation op = new Operation();
+ op.type = Operation.Type.SET;
+ op.value = value;
+ changes.put(property, op);
+ }
+
+ /**
+ * Increment the value.
+ *
+ * @param key the key
+ * @param value the increment
+ */
+ void increment(String property, long value) {
+ Operation op = new Operation();
+ op.type = Operation.Type.INCREMENT;
+ op.value = value;
+ changes.put(property, op);
+ }
+
+ /**
+ * A MongoDB operation for a given key within a document.
+ */
+ static class Operation {
+
+ /**
+ * The MongoDB operation type.
+ */
+ enum Type {
+
+ /**
+ * Set the value.
+ * The sub-key is not used.
+ */
+ SET,
+
+ /**
+ * Increment the Long value with the provided Long value.
+ * The sub-key is not used.
+ */
+ INCREMENT,
+
+ /**
+ * Add the sub-key / value pair.
+ * The value in the stored node is a map.
+ */
+ ADD_MAP_ENTRY,
+
+ /**
+ * Remove the sub-key / value pair.
+ * The value in the stored node is a map.
+ */
+ REMOVE_MAP_ENTRY
+ }
+
+
+ /**
+ * The operation type.
+ */
+ Type type;
+
+ /**
+ * The sub-key.
+ */
+ Object subKey;
+
+ /**
+ * The value, if any.
+ */
+ Object value;
+
+ public String toString() {
+ return type + ": " + subKey + " = " + value;
+ }
+
+ }
+
+}
Added: jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java?rev=1445508&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java (added)
+++ jackrabbit/oak/trunk/oak-mongomk/src/main/java/org/apache/jackrabbit/mongomk/prototype/Utils.java Wed Feb 13 08:53:48 2013
@@ -0,0 +1,49 @@
+package org.apache.jackrabbit.mongomk.prototype;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class Utils {
+
+ static long startTime = System.currentTimeMillis();
+ static AtomicInteger counter = new AtomicInteger();
+
+ static int pathDepth(String path) {
+ return path.equals("/") ? 0 : path.replaceAll("[^/]", "").length();
+ }
+
+ static <K, V> Map<K, V> newMap() {
+ return new TreeMap<K, V>();
+ }
+
+ /**
+ * Create a simple revision id. It consists of 3 hex characters for the
+ * seconds since startup, 3 characters for the cluster id, and 3 characters
+ * for the counter. The format is similar to MongoDB ObjectId.
+ *
+ * @param clusterId the unique machineId + processId
+ * @return the unique revision id
+ */
+ static String createRevision(int clusterId) {
+ int seconds = (int) ((System.currentTimeMillis() - startTime) / 1000);
+ int count = counter.getAndIncrement();
+ StringBuilder buff = new StringBuilder("r");
+ buff.append(Integer.toHexString(0x1000 + seconds).substring(1));
+ buff.append(Integer.toHexString(0x1000 + clusterId).substring(1));
+ buff.append(Integer.toHexString(0x1000 + count).substring(1));
+ return buff.toString();
+ }
+
+ /**
+ * Get the age of a revision in seconds.
+ *
+ * @param rev the revision
+ * @return the age in seconds
+ */
+ int getRevisionAge(String rev) {
+ String s = rev.substring(0, 3);
+ return Integer.parseInt(s, 16);
+ }
+
+}