Return-Path: X-Original-To: apmail-jackrabbit-commits-archive@www.apache.org Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id D91967946 for ; Thu, 25 Aug 2011 08:19:41 +0000 (UTC) Received: (qmail 63319 invoked by uid 500); 25 Aug 2011 08:19:41 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 63216 invoked by uid 500); 25 Aug 2011 08:19:38 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 63016 invoked by uid 99); 25 Aug 2011 08:19:35 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 25 Aug 2011 08:19:35 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 25 Aug 2011 08:19:24 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id A787E2388900; Thu, 25 Aug 2011 08:19:01 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1161434 [1/3] - in /jackrabbit/sandbox/microkernel: ./ src/main/java/org/apache/jackrabbit/mk/ src/main/java/org/apache/jackrabbit/mk/api/ src/main/java/org/apache/jackrabbit/mk/blobs/ src/main/java/org/apache/jackrabbit/mk/datastore/ src/... Date: Thu, 25 Aug 2011 08:18:59 -0000 To: commits@jackrabbit.apache.org From: stefan@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110825081901.A787E2388900@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: stefan Date: Thu Aug 25 08:18:57 2011 New Revision: 1161434 URL: http://svn.apache.org/viewvc?rev=1161434&view=rev Log: some refactoring: extracting blob handling interface from loose object store interface Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/ - copied from r1159622, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/datastore/ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/PersistenceManager.java - copied, changed from r1159622, jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/ObjectStore.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/ - copied from r1159622, jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/datastore/ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/DbBlobStoreTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/FileBlobStoreTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStoreTest.java Removed: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/Store.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/StoreInputStream.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/datastore/ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/DataStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/ObjectStore.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/DbStoreTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/FileStoreTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/MemoryStoreTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/datastore/ Modified: jackrabbit/sandbox/microkernel/pom.xml jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java 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/main/java/org/apache/jackrabbit/mk/store/Blob.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/Commit.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/CommitBuilder.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/Constants.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/H2RevisionStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MutableCommit.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/MutableNode.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/Node.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/NodeDelta.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/NodeUtils.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/PersistentId.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/RevisionStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/store/SimpleRevisionStore.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/CommitGate.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/IOUtils.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/MicroKernelInputStream.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/PathUtils.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/SmallLRUCache.java jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/StringUtils.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/ConcurrentWriteTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/HelloWorld.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/LargeObjectTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/Profiler.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/blobs/DataStoreTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsonBuilderTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/json/JsopTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/CommitGateTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/IOUtilsTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/NonDescendingClockTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/PathTest.java jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/util/StringUtilsTest.java Modified: jackrabbit/sandbox/microkernel/pom.xml URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/pom.xml?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/pom.xml (original) +++ jackrabbit/sandbox/microkernel/pom.xml Thu Aug 25 08:18:57 2011 @@ -27,16 +27,9 @@ microkernel 0.1-SNAPSHOT MicroKernel - Hierarchical MVCC-based persistence store + Hierarchical MVCC-based persistence blobStore - - - com.googlecode.json-simple - json-simple - 1.1 - - com.h2database h2 @@ -45,11 +38,18 @@ - junit - junit - 4.5 - test - + com.googlecode.json-simple + json-simple + 1.1 + test + + + + junit + junit + 4.5 + test + @@ -67,17 +67,17 @@ --> org.pitest - pitest-maven - 0.20 - - - org.apache.jackrabbit.mk.util.Path* - - - org.apache.jackrabbit.mk.util.Path* - - - + pitest-maven + 0.20 + + + org.apache.jackrabbit.mk.util.Path* + + + org.apache.jackrabbit.mk.util.Path* + + + Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Constants.java Thu Aug 25 08:18:57 2011 @@ -41,7 +41,7 @@ public class Constants { public static final int MEM_MAP_ENTRY = 32; public static final int MEM_EVENT = 64; public static final int MEM_NODE_DATA = 48; - public static final int MEM_VAL_LONG = 32; + public static final int MEM_VAL_LONG = 32; public static final int MEM_VAL_BINARY = 32; public static final int MEM_VAL_STRING = 64; public static final int MEM_VAL_OBJ = 16; Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelFactory.java Thu Aug 25 08:18:57 2011 @@ -16,10 +16,11 @@ */ package org.apache.jackrabbit.mk; -import java.io.File; import org.apache.jackrabbit.mk.api.MicroKernel; import org.apache.jackrabbit.mk.mem.MemoryKernelImpl; +import java.io.File; + /** * A factory to create a MicroKernel instance. */ Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/MicroKernelImpl.java Thu Aug 25 08:18:57 2011 @@ -107,9 +107,9 @@ public class MicroKernelImpl implements for (int i = history.size() - 1; i >= 0; i--) { Commit commit = history.get(i); buff.object(). - key("id").value(commit.getId()). - key("ts").value(commit.getCommitTS()). - endObject(); + key("id").value(commit.getId()). + key("ts").value(commit.getCommitTS()). + endObject(); } return buff.endArray().toString(); } @@ -150,33 +150,33 @@ public class MicroKernelImpl implements continue; } commitBuff.object(). - key("id").value(commit.getId()). - key("ts").value(commit.getCommitTS()). - key("msg").value(commit.getMsg()); + key("id").value(commit.getId()). + key("ts").value(commit.getCommitTS()). + key("msg").value(commit.getMsg()); final JsopBuilder buff = new JsopBuilder(); // maps (key: id of target node, value: path/to/target) // for tracking added/removed nodes; this allows us // to detect 'move' operations - final HashMap addedNodes = new HashMap(); - final HashMap removedNodes = new HashMap(); + final HashMap addedNodes = new HashMap(); + final HashMap removedNodes = new HashMap(); try { String path = "/"; Node node1 = rep.getNode(commit.getParentId(), path); Node node2 = rep.getNode(commit.getId(), path); - NodeUtils.diff(path, node1, node2, true, rep.getStore(), new NodeDiffHandler() { + NodeUtils.diff(path, node1, node2, true, rep.getPersistenceManager(), new NodeDiffHandler() { public void propAdded(String nodePath, String propName, String value) { buff.append("+ "). - key(PathUtils.concat(nodePath, propName)). - encodedValue(value). - appendWhitespace("\n"); + key(PathUtils.concat(nodePath, propName)). + encodedValue(value). + appendWhitespace("\n"); } public void propChanged(String nodePath, String propName, String oldValue, String newValue) { buff.append("^ "). - key(PathUtils.concat(nodePath, propName)). - encodedValue(newValue). - appendWhitespace("\n"); + key(PathUtils.concat(nodePath, propName)). + encodedValue(newValue). + appendWhitespace("\n"); } public void propDeleted(String nodePath, String propName, String value) { @@ -184,17 +184,17 @@ public class MicroKernelImpl implements // using the "- " notation we're representing // property deletions as "^ :null" buff.append("^ "). - key(PathUtils.concat(nodePath, propName)). - encodedValue("null"). - appendWhitespace("\n"); + key(PathUtils.concat(nodePath, propName)). + encodedValue("null"). + appendWhitespace("\n"); } public void childNodeAdded(String nodePath, String childName, String id) { addedNodes.put(id, PathUtils.concat(nodePath, childName)); buff.append("+ "). - key(PathUtils.concat(nodePath, childName)).object(); + key(PathUtils.concat(nodePath, childName)).object(); try { - toJson(buff, rep.getStore().getNode(id), childName, Integer.MAX_VALUE, 0, -1); + toJson(buff, rep.getPersistenceManager().getNode(id), childName, Integer.MAX_VALUE, 0, -1); } catch (Exception e) { buff.value("ERROR: failed to retrieve node " + id); } @@ -225,19 +225,19 @@ public class MicroKernelImpl implements buff.reset(); // TODO refactor code, avoid duplication - NodeUtils.diff(path, node1, node2, true, rep.getStore(), new NodeDiffHandler() { + NodeUtils.diff(path, node1, node2, true, rep.getPersistenceManager(), new NodeDiffHandler() { public void propAdded(String nodePath, String propName, String value) { buff.append("+ "). - key(PathUtils.concat(nodePath, propName)). - encodedValue(value). - append("\n"); + key(PathUtils.concat(nodePath, propName)). + encodedValue(value). + append("\n"); } public void propChanged(String nodePath, String propName, String oldValue, String newValue) { buff.append("^ "). - key(PathUtils.concat(nodePath, propName)). - encodedValue(newValue). - append("\n"); + key(PathUtils.concat(nodePath, propName)). + encodedValue(newValue). + append("\n"); } public void propDeleted(String nodePath, String propName, String value) { @@ -245,9 +245,9 @@ public class MicroKernelImpl implements // using the "- " notation we're representing // property deletions as "^ :null" buff.append("^ "). - key(PathUtils.concat(nodePath, propName)). - encodedValue("null"). - append("\n"); + key(PathUtils.concat(nodePath, propName)). + encodedValue("null"). + append("\n"); } public void childNodeAdded(String nodePath, String childName, String id) { @@ -256,9 +256,9 @@ public class MicroKernelImpl implements return; } buff.append("+ "). - key(PathUtils.concat(nodePath, childName)).object(); + key(PathUtils.concat(nodePath, childName)).object(); try { - toJson(buff, rep.getStore().getNode(id), childName, Integer.MAX_VALUE, 0, -1); + toJson(buff, rep.getPersistenceManager().getNode(id), childName, Integer.MAX_VALUE, 0, -1); } catch (Exception e) { buff.value("ERROR: failed to retrieve node " + id); } @@ -282,11 +282,11 @@ public class MicroKernelImpl implements // finally process moved nodes for (Map.Entry entry : addedNodes.entrySet()) { buff.append("> "). - // path/to/deleted/node - key(removedNodes.get(entry.getKey())). - // path/to/added/node - value(entry.getValue()). - append("\n"); + // path/to/deleted/node + key(removedNodes.get(entry.getKey())). + // path/to/added/node + value(entry.getValue()). + append("\n"); } } } catch (Exception e) { @@ -331,49 +331,49 @@ public class MicroKernelImpl implements break; } switch (r) { - case '+': { - String relPath = t.readString(); - t.read(':'); - t.read('{'); - String nodePath = PathUtils.concat(path, relPath); - String parentPath = PathUtils.getParentPath(nodePath); - String nodeName = PathUtils.getName(nodePath); - // build the list of added nodes recursively - LinkedList list = new LinkedList(); - addNode(list, parentPath, nodeName, t); - for (AddNodeOperation op : list) { - cb.addNode(op.path, op.name, op.props); + case '+': { + String relPath = t.readString(); + t.read(':'); + t.read('{'); + String nodePath = PathUtils.concat(path, relPath); + String parentPath = PathUtils.getParentPath(nodePath); + String nodeName = PathUtils.getName(nodePath); + // build the list of added nodes recursively + LinkedList list = new LinkedList(); + addNode(list, parentPath, nodeName, t); + for (AddNodeOperation op : list) { + cb.addNode(op.path, op.name, op.props); + } + break; } - break; - } - case '-': { - String relPath = t.readString(); - cb.removeNode(PathUtils.concat(path, relPath)); - break; - } - case '^': { - String relPath = t.readString(); - t.read(':'); - String value; - if (t.matches(JsopTokenizer.NULL)) { - value = null; - } else { - value = t.readRawValue().trim(); + case '-': { + String relPath = t.readString(); + cb.removeNode(PathUtils.concat(path, relPath)); + break; } - String nodePath = PathUtils.concat(path, relPath); - String parentPath = PathUtils.getParentPath(nodePath); - String propName = PathUtils.getName(nodePath); - cb.setProperty(parentPath, propName, value); - break; - } - case '>': { - String relPath = t.readString(); - t.read(':'); - String targetPath = t.readString(); - if (! PathUtils.isAbsolute(targetPath)) { - targetPath = PathUtils.concat(path, targetPath); + case '^': { + String relPath = t.readString(); + t.read(':'); + String value; + if (t.matches(JsopTokenizer.NULL)) { + value = null; + } else { + value = t.readRawValue().trim(); + } + String nodePath = PathUtils.concat(path, relPath); + String parentPath = PathUtils.getParentPath(nodePath); + String propName = PathUtils.getName(nodePath); + cb.setProperty(parentPath, propName, value); + break; } - cb.moveNode(PathUtils.concat(path, relPath), targetPath); + case '>': { + String relPath = t.readString(); + t.read(':'); + String targetPath = t.readString(); + if (!PathUtils.isAbsolute(targetPath)) { + targetPath = PathUtils.concat(path, targetPath); + } + cb.moveNode(PathUtils.concat(path, relPath), targetPath); /* path = t.readString(); String from = PathUtils.concat(fromRoot, path); @@ -432,10 +432,10 @@ public class MicroKernelImpl implements headRoot = headRoot.cloneAndRemoveChildNode(from, headRevId); } */ - break; - } - default: - throw new AssertionError("token type: " + t.getTokenType()); + break; + } + default: + throw new AssertionError("token type: " + t.getTokenType()); } } String newHead = cb.doCommit(); @@ -451,7 +451,7 @@ public class MicroKernelImpl implements throw new IllegalStateException("this instance has already been disposed"); } try { - return rep.getStore().getBlobLength(blobId); + return rep.getPersistenceManager().getBlobLength(blobId); } catch (Exception e) { throw new MicroKernelException(e); } @@ -462,7 +462,7 @@ public class MicroKernelImpl implements throw new IllegalStateException("this instance has already been disposed"); } try { - return rep.getStore().readBlob(blobId, pos, buff, off, length); + return rep.getPersistenceManager().readBlob(blobId, pos, buff, off, length); } catch (Exception e) { throw new MicroKernelException(e); } @@ -473,7 +473,7 @@ public class MicroKernelImpl implements throw new IllegalStateException("this instance has already been disposed"); } try { - return rep.getStore().writeBlob(in); + return rep.getPersistenceManager().writeBlob(in); } catch (Exception e) { throw new MicroKernelException(e); } @@ -497,7 +497,7 @@ public class MicroKernelImpl implements builder.key(childName).object(); if (depth > 0) { String childId = child.getValue(); - toJson(builder, rep.getStore().getNode(childId), childName, depth - 1, 0, -1); + toJson(builder, rep.getPersistenceManager().getNode(childId), childName, depth - 1, 0, -1); } builder.endObject(); } Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/Repository.java Thu Aug 25 08:18:57 2011 @@ -16,10 +16,14 @@ */ package org.apache.jackrabbit.mk; +import org.apache.jackrabbit.mk.blobs.BlobStore; +import org.apache.jackrabbit.mk.blobs.FileBlobStore; import org.apache.jackrabbit.mk.store.Commit; import org.apache.jackrabbit.mk.store.CommitBuilder; +import org.apache.jackrabbit.mk.store.H2RevisionStore; import org.apache.jackrabbit.mk.store.Node; -import org.apache.jackrabbit.mk.store.ObjectStore; +import org.apache.jackrabbit.mk.store.PersistenceManager; +import org.apache.jackrabbit.mk.store.RevisionStore; import org.apache.jackrabbit.mk.util.PathUtils; import java.io.File; @@ -32,11 +36,11 @@ public class Repository { final String homeDir; boolean initialized; - final ObjectStore store; + final PersistenceManager pm; public Repository(String homeDir) { this.homeDir = homeDir == null ? "." : homeDir; - store = new ObjectStore(); + pm = new PersistenceManager(); initialized = false; } @@ -45,7 +49,20 @@ public class Repository { return; } - store.initialize(new File(homeDir)); + File home = new File(homeDir); + + RevisionStore revStore = new H2RevisionStore(); + //RevisionStore revStore = new SimpleRevisionStore(); + revStore.initialize(home); + + BlobStore blobStore; + if (revStore instanceof BlobStore) { + blobStore = (BlobStore) revStore; + } else { + blobStore = new FileBlobStore(new File(home, "blobs").getCanonicalPath()); + } + + pm.initialize(revStore, blobStore); initialized = true; } @@ -55,38 +72,38 @@ public class Repository { return; } - store.close(); + pm.close(); initialized = false; } - public ObjectStore getStore() { + public PersistenceManager getPersistenceManager() { if (!initialized) { throw new IllegalStateException("not initialized"); } - return store; + return pm; } public String getHeadRevision() throws Exception { if (!initialized) { throw new IllegalStateException("not initialized"); } - return store.getHeadCommit().getId(); + return pm.getHeadCommit().getId(); } public Commit getHeadCommit() throws Exception { if (!initialized) { throw new IllegalStateException("not initialized"); } - return store.getHeadCommit(); + return pm.getHeadCommit(); } public Commit getCommit(String id) throws Exception { if (!initialized) { throw new IllegalStateException("not initialized"); } - return store.getCommit(id); + return pm.getCommit(id); } public Node getNode(String revId, String path) throws Exception { @@ -95,10 +112,10 @@ public class Repository { } String[] ids = resolvePath(revId, path); - return store.getNode(ids[ids.length - 1]); + return pm.getNode(ids[ids.length - 1]); } - public boolean nodeExists(String revId, String path) { + public boolean nodeExists(String revId, String path) { if (!initialized) { throw new IllegalStateException("not initialized"); } @@ -109,13 +126,13 @@ public class Repository { try { String[] names = PathUtils.split(path); - Node parent = store.getNode(store.getCommit(revId).getRootNodeId()); + Node parent = pm.getNode(pm.getCommit(revId).getRootNodeId()); for (int i = 0; i < names.length; i++) { String id = parent.getChildNodeEntries().get(names[i]); if (id == null) { return false; } - parent = store.getNode(id); + parent = pm.getNode(id); } return true; } catch (Exception e) { @@ -134,25 +151,25 @@ public class Repository { throw new IllegalArgumentException("illegal path"); } - Commit commit = store.getCommit(revId); + Commit commit = pm.getCommit(revId); if (PathUtils.denotesRoot(path)) { - return new String[] { commit.getRootNodeId() }; + return new String[]{commit.getRootNodeId()}; } String[] names = PathUtils.split(path); String[] ids = new String[names.length + 1]; // get root node ids[0] = commit.getRootNodeId(); - Node parent = store.getNode(ids[0]); - // traverse path and store id of each element + Node parent = pm.getNode(ids[0]); + // traverse path and pm id of each element for (int i = 0; i < names.length; i++) { String id = parent.getChildNodeEntries().get(names[i]); if (id == null) { throw new Exception("path not found: " + path); } ids[i + 1] = id; - parent = store.getNode(id); + parent = pm.getNode(id); } return ids; } Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/api/MicroKernel.java Thu Aug 25 08:18:57 2011 @@ -29,10 +29,10 @@ import java.io.InputStream; *
  • predefined timeout (e.g. 50ms) for reading methods
  • *
  • portable to C
  • *
  • efficient support for large number of child nodes
  • - *
  • integrated API for storing/retrieving large binaries (similar to DataStore API)
  • + *
  • integrated API for storing/retrieving large binaries (similar to existing DataStore API)
  • *
  • human-readable data serialization (e.g. JSON, YAML)
  • * - * + *

    * The MicroKernel Data Model: *

      *
    • simple JSON-inspired data model: just nodes and properties
    • @@ -47,45 +47,45 @@ import java.io.InputStream; *
    • properties and child nodes share the same namespace, i.e. a property and * a child node, sharing the same parent node, cannot have the same name
    • *
    - * + *

    * Architecture (overview): *

      *
    1. JCR (full TCK-compliant implementation)
    2. *
    3. SPI (node types, workspaces, namespaces, access control, search, locking, ...)
    4. *
    5. MicroKernel
    6. *
    - * + *

    * TBD: *

      *
    • The concept of JCR workspaces is decorated by the SPI layer as top-level nodes - * in the MicroKernel (e.g. /default. /system); there's no need for - * inter-workspace operations, allows simplified versioning implementation
    • + * in the MicroKernel (e.g. /default. /system); there's no need for + * inter-workspace operations, allows simplified versioning implementation *
    • should the MicroKernel support (polling) observation? - * => getRevisions/getJournal should be sufficient
    • + * => getRevisions/getJournal should be sufficient *
    • nodes are primarily accessed by path. node identifiers (nodeId) are - * optional. if a new node is committed with a special marker property - * (e.g. ":id" : null), a system-generated unique identifier will be - * assigned and exposed through the same :id property; - * the MicroKernel maintains an id<->path index
    • + * optional. if a new node is committed with a special marker property + * (e.g. ":id" : null), a system-generated unique identifier will be + * assigned and exposed through the same :id property; + * the MicroKernel maintains an id<->path index *
    • should the API provide methods for looking up id/path mappings - * (getId/getPath) or should every node with an assigned - * id expose a :path property (in addition to :id)?
    • + * (getId/getPath) or should every node with an assigned + * id expose a :path property (in addition to :id)? *
    • jcr:uuid is decorated by the SPI on top of MicroKernel; - * jcr:uuid is equivalent to the MicroKernel node identifier (:id)
    • + * jcr:uuid is equivalent to the MicroKernel node identifier (:id) *
    • should the MicroKernel provide built-in support for shareable nodes? - * => no (tentative)
    • + * => no (tentative) *
    - * + *

    * JSON/JSOP: *

      *
    • do we need to specify a 'reorder' op in jsop? perhaps based on 'move'? - * => check full JSOP spec and davex
    • + * => check full JSOP spec and davex *
    • do we need to specify a 'copy' op in jsop? - * => check full JSOP spec and davex
    • + * => check full JSOP spec and davex *
    • how should an incomplete list of child nodes be represented in json? - * => length(array) != :childNodeCount
    • + * => length(array) != :childNodeCount *
    • consider YAML as an alternative to the JSON representation => - * supports ordered sequence of name:value pairs, richer set of data/collection types
    • + * supports ordered sequence of name:value pairs, richer set of data/collection types *
    * * @todo check DavEx protocol for potential overlap/input (e.g. JSON format) @@ -116,26 +116,25 @@ public interface MicroKernel { * [ { "id" : "", "ts" : }, ... ] * * - * @todo provide a per revision size hint? (TBD) - * - * @param since timestamp (ms) of earliest revision to be returned + * @param since timestamp (ms) of earliest revision to be returned * @param maxEntries maximum #entries to be returned; * if < 0, no limit will be applied. * @return a chronological list of revisions in JSON format. * @throws MicroKernelException if an error occurs + * @todo provide a per revision size hint? (TBD) */ String /* jsonArray */ getRevisions(long since, int maxEntries) throws MicroKernelException; /** * Wait for a commit to occur that is newer than the given revision number. - * + *

    * This method is useful efficient polling. The method will return the current head revision * if it is newer than the given old revision number, or wait until the given number of * milliseconds passed or a new head revision is available. * * @param maxWaitMillis the maximum number of milliseconds to wait (0 if the - * method should not wait). + * method should not wait). * @return the current head revision * @throws MicroKernelException if an error occurs * @throws InterruptedException if the thread was interrupted @@ -153,7 +152,7 @@ public interface MicroKernel { * * * @param fromRevisionId first revision to be returned in journal - * @param toRevisionId last revision to be returned in journal + * @param toRevisionId last revision to be returned in journal * @return a chronological list of revisions in JSON format * @throws MicroKernelException if an error occurs */ @@ -166,7 +165,7 @@ public interface MicroKernel { /** * Determines whether the specified node exists. * - * @param path path denoting node + * @param path path denoting node * @param revisionId revision * @return true if the specified node exists, otherwise false * @throws MicroKernelException if an error occurs @@ -194,7 +193,7 @@ public interface MicroKernel { * * The collection of name/value pairs denoting child nodes is assumed to be * ordered. - * + *

    * Remarks: *

      *
    • If the property :childNodeCount is 0, then the @@ -207,7 +206,7 @@ public interface MicroKernel { * This method is a convenience method for * getNodes(path, revisionId, 1, 0, -1) * - * @param path path denoting root of node tree to be retrieved + * @param path path denoting root of node tree to be retrieved * @param revisionId revision * @return node tree in JSON format * @throws MicroKernelException if an error occurs @@ -236,13 +235,13 @@ public interface MicroKernel { * * Offset and count only affect the returned child node list of this node. * - * @param path path denoting root of node tree to be retrieved + * @param path path denoting root of node tree to be retrieved * @param revisionId revision - * @param depth maximum depth of returned tree - * @param offset start position in child node list (0 to start at the - * beginning) - * @param count maximum number of child nodes to retrieve (-1 for as many as - * possible) + * @param depth maximum depth of returned tree + * @param offset start position in child node list (0 to start at the + * beginning) + * @param count maximum number of child nodes to retrieve (-1 for as many as + * possible) * @return node tree in JSON format * @throws MicroKernelException if an error occurs */ @@ -253,10 +252,10 @@ public interface MicroKernel { /** * Applies the specified changes on the specified target node. * - * @param path path denoting target node - * @param jsonDiff changes to be applied in JSON diff format. + * @param path path denoting target node + * @param jsonDiff changes to be applied in JSON diff format. * @param revisionId revision the changes are based on - * @param message commit message + * @param message commit message * @return id of newly created revision * @throws MicroKernelException if an error occurs */ @@ -269,11 +268,10 @@ public interface MicroKernel { /** * Returns the length of the specified blob. * - * @todo encode length in id? - * * @param blobId blob identifier * @return length of the specified blob * @throws MicroKernelException if an error occurs + * @todo encode length in id? */ long getLength(String blobId) throws MicroKernelException; @@ -284,10 +282,10 @@ public interface MicroKernel { * The number of bytes actually read is returned as an integer. * * @param blobId blob identifier - * @param pos the offset within the blob - * @param buff the buffer into which the data is read. - * @param off the start offset in array buff - * at which the data is written. + * @param pos the offset within the blob + * @param buff the buffer into which the data is read. + * @param off the start offset in array buff + * at which the data is written. * @param length the maximum number of bytes to read * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of @@ -303,7 +301,7 @@ public interface MicroKernel { *

      * If identical stream content has been stored previously, then the existing * identifier will be returned instead of storing a redundant copy. - + * * @param in InputStream providing the blob content * @return blob identifier associated with the given content * @throws MicroKernelException if an error occurs Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java?rev=1161434&view=auto ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java (added) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java Thu Aug 25 08:18:57 2011 @@ -0,0 +1,235 @@ +/* + * 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.blobs; + +import org.apache.jackrabbit.mk.util.IOUtils; +import org.apache.jackrabbit.mk.util.StringUtils; +import org.h2.util.SmallLRUCache; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.DigestOutputStream; +import java.security.MessageDigest; + +/** + * An abstract data store that splits the binaries in relatively small blocks. + * Each data store id a list of zero or more entries. Each entry is either + *

        + *
      • data (a number of bytes), or
      • + *
      • the hash code of the content of a number of bytes, or
      • + *
      • the hash code of the content of a data store id (indirect hash)
      • + *
      + * Thanks to the indirection, blocks can be kept relatively small, so that + * caching is simpler, and so that the storage backend doesn't need to support + * arbitrary size blobs (some storage backends buffer blobs in memory) and fast + * seeks (some storage backends re-read the whole blob when seeking). + *

      + * The the format of a 'data' entry is: type (one byte; 0 for data), length + * (variable size int), data (bytes). + *

      + * The format of a 'hash of content' entry is: type (one byte; 1 for hash), + * level (variable size int, 0 meaning not nested), size (variable size long), + * hash code length (variable size int), hash code. + *

      + * The format of a 'hash of data store id' entry is: type (one byte; 1 for + * hash), level (variable size int, nesting level), total size (variable size + * long), size of data store id (variable size long), hash code length (variable + * size int), hash code. + */ +public abstract class AbstractBlobStore implements BlobStore { + + protected static final int TYPE_DATA = 0; + protected static final int TYPE_HASH = 1; + protected static final int TYPE_HASH_COMPRESSED = 2; + + /** + * The minimum size of a block. Smaller blocks are stored (the data store id + * is the data itself). + */ + private int blockSizeMin = 256; + + /** + * The size of a block. This number has been found to be as fast as larger + * values, and faster than smaller values. + */ + private int blockSize = 128 * 1024; + + private static final String HASH_ALGORITHM = "SHA-1"; + + public void setBlockSizeMin(int x) { + this.blockSizeMin = x; + } + + public void setBlockSize(int x) { + this.blockSize = x; + } + + private SmallLRUCache cache = SmallLRUCache.newInstance(5); + + public String writeBlob(InputStream in) throws Exception { + ByteArrayOutputStream idStream = new ByteArrayOutputStream(); + convertBlobToId(in, idStream, 0, 0); + byte[] id = idStream.toByteArray(); + // System.out.println(" write blob " + StringUtils.convertBytesToHex(id)); + return StringUtils.convertBytesToHex(id); + } + + private void convertBlobToId(InputStream in, ByteArrayOutputStream idStream, int level, long totalLength) throws Exception { + byte[] block = new byte[blockSize]; + int count = 0; + while (true) { + MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM); + ByteArrayOutputStream buff = new ByteArrayOutputStream(); + DigestOutputStream dout = new DigestOutputStream(buff, digest); + int blockLen = IOUtils.readFully(in, block, 0, block.length); + count++; + if (blockLen == 0) { + break; + } else if (blockLen < blockSizeMin) { + idStream.write(TYPE_DATA); + IOUtils.writeVarInt(idStream, blockLen); + idStream.write(block, 0, blockLen); + totalLength += blockLen; + } else { + dout.write(block, 0, blockLen); + byte[] blockId = digest.digest(); + idStream.write(TYPE_HASH); + IOUtils.writeVarInt(idStream, level); + if (level > 0) { + IOUtils.writeVarLong(idStream, totalLength); + } + IOUtils.writeVarLong(idStream, blockLen); + totalLength += blockLen; + IOUtils.writeVarInt(idStream, blockId.length); + idStream.write(blockId); + byte[] data = buff.toByteArray(); + storeBlock(blockId, level, data); + } + if (idStream.size() > blockSize / 2) { + // convert large ids to a block, but ensure it can be stored as + // one block (otherwise the indirection no longer works) + byte[] idBlock = idStream.toByteArray(); + idStream.reset(); + convertBlobToId(new ByteArrayInputStream(idBlock), idStream, level + 1, totalLength); + count = 1; + } + } + if (count > 0 && idStream.size() > blockSizeMin) { + // at the very end, convert large ids to a block, + // because large block ids are not handy + // (specially if they are used to read data in small chunks) + byte[] idBlock = idStream.toByteArray(); + idStream.reset(); + convertBlobToId(new ByteArrayInputStream(idBlock), idStream, level + 1, totalLength); + } + } + + protected abstract void storeBlock(byte[] blockId, int level, byte[] data) throws Exception; + + public int readBlob(String blobId, long pos, byte[] buff, int off, int length) throws Exception { + byte[] id = StringUtils.convertHexToBytes(blobId); + ByteArrayInputStream idStream = new ByteArrayInputStream(id); + while (true) { + int type = idStream.read(); + if (type == -1) { + return -1; + } else if (type == TYPE_DATA) { + int len = IOUtils.readVarInt(idStream); + if (pos < len) { + IOUtils.skipFully(idStream, (int) pos); + len -= pos; + if (length < len) { + len = length; + } + IOUtils.readFully(idStream, buff, off, len); + return len; + } + IOUtils.skipFully(idStream, len); + pos -= len; + } else if (type == TYPE_HASH) { + int level = IOUtils.readVarInt(idStream); + long totalLength = IOUtils.readVarLong(idStream); + if (level > 0) { + // block length (ignored) + IOUtils.readVarLong(idStream); + } + byte[] digest = new byte[IOUtils.readVarInt(idStream)]; + IOUtils.readFully(idStream, digest, 0, digest.length); + if (pos >= totalLength) { + pos -= totalLength; + } else { + byte[] block = readBlock(digest); + if (level > 0) { + idStream = new ByteArrayInputStream(block); + } else { + ByteArrayInputStream in = new ByteArrayInputStream(block); + IOUtils.skipFully(in, (int) pos); + return IOUtils.readFully(in, buff, off, length); + } + } + } else { + throw new IOException("Unknown blobs id type " + type + " for blob " + blobId); + } + } + } + + private byte[] readBlock(byte[] digest) throws Exception { + String id = StringUtils.convertBytesToHex(digest); + byte[] block = cache.get(id); + if (block == null) { + block = readBlockFromBackend(digest); + cache.put(id, block); + } + return block; + } + + protected abstract byte[] readBlockFromBackend(byte[] blockId) throws Exception; + + public long getBlobLength(String blobId) throws IOException { + byte[] id = StringUtils.convertHexToBytes(blobId); + ByteArrayInputStream idStream = new ByteArrayInputStream(id); + long totalLength = 0; + while (true) { + int type = idStream.read(); + if (type == -1) { + break; + } + if (type == TYPE_DATA) { + int len = IOUtils.readVarInt(idStream); + IOUtils.skipFully(idStream, len); + totalLength += len; + } else if (type == TYPE_HASH) { + int level = IOUtils.readVarInt(idStream); + totalLength += IOUtils.readVarLong(idStream); + if (level > 0) { + // block length (ignored) + IOUtils.readVarLong(idStream); + } + int digestLength = IOUtils.readVarInt(idStream); + IOUtils.skipFully(idStream, digestLength); + } else { + throw new IOException("Datastore id type " + type + " for blob " + blobId); + } + } + return totalLength; + } + + public abstract void clear(); + +} Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java?rev=1161434&view=auto ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java (added) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/BlobStoreInputStream.java Thu Aug 25 08:18:57 2011 @@ -0,0 +1,73 @@ +/* + * 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.blobs; + +import org.apache.jackrabbit.mk.util.IOUtils; + +import java.io.IOException; +import java.io.InputStream; + +/** + * An input stream to simplify reading from a store. + * See also MicroKernelInputStream. + */ +public class BlobStoreInputStream extends InputStream { + + private final AbstractBlobStore store; + private final String id; + private long pos; + private byte[] oneByteBuff; + + public BlobStoreInputStream(AbstractBlobStore store, String id) { + this.store = store; + this.id = id; + } + + public int read(byte[] b, int off, int len) throws IOException { + int l; + try { + l = store.readBlob(id, pos, b, off, len); + } catch (Exception e) { + throw new IOException(e); + } + if (l < 0) { + return l; + } + pos += l; + return l; + } + + public int read() throws IOException { + if (oneByteBuff == null) { + oneByteBuff = new byte[1]; + } + int len = read(oneByteBuff, 0, 1); + if (len < 0) { + return len; + } + return oneByteBuff[0] & 0xff; + } + + public static byte[] readFully(AbstractBlobStore store, String id) throws IOException { + int len = (int) store.getBlobLength(id); + byte[] buff = new byte[len]; + BlobStoreInputStream in = new BlobStoreInputStream(store, id); + IOUtils.readFully(in, buff, 0, len); + return buff; + } + +} \ No newline at end of file Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java?rev=1161434&view=auto ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java (added) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java Thu Aug 25 08:18:57 2011 @@ -0,0 +1,126 @@ +/* + * 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.blobs; + +import org.apache.jackrabbit.mk.util.StringUtils; +import org.h2.jdbcx.JdbcConnectionPool; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * A database blob store. + */ +public class DbBlobStore extends AbstractBlobStore { + + private JdbcConnectionPool cp; + + public void setConnectionPool(JdbcConnectionPool cp) throws SQLException { + this.cp = cp; + Connection conn = cp.getConnection(); + Statement stat = conn.createStatement(); + stat.execute("create table if not exists datastore_meta" + + "(id varchar primary key, level int, lastMod bigint)"); + stat.execute("create table if not exists datastore_data" + + "(id varchar primary key, data binary)"); + conn.close(); + } + + @Override + protected void storeBlock(byte[] blockId, int level, byte[] data) throws SQLException { + Connection conn = cp.getConnection(); + try { + String id = StringUtils.convertBytesToHex(blockId); + long now = System.currentTimeMillis(); + PreparedStatement prep = conn.prepareStatement( + "update datastore_meta set lastMod = ? where id = ?"); + int count; + try { + prep.setLong(1, now); + prep.setString(2, id); + count = prep.executeUpdate(); + } finally { + prep.close(); + } + if (count == 0) { + try { + prep = conn.prepareStatement( + "insert into datastore_meta(id, level, lastMod) values(?, ?, ?)"); + try { + prep.setString(1, id); + prep.setInt(2, level); + prep.setLong(3, now); + prep.execute(); + } finally { + prep.close(); + } + } catch (SQLException e) { + // already exists - ok + } + try { + prep = conn.prepareStatement( + "insert into datastore_data(id, data) values(?, ?)"); + try { + prep.setString(1, id); + prep.setBytes(2, data); + prep.execute(); + } finally { + prep.close(); + } + } catch (SQLException e) { + // already exists - ok + } + } + } finally { + conn.close(); + } + } + + @Override + protected byte[] readBlockFromBackend(byte[] blockId) throws Exception { + Connection conn = cp.getConnection(); + try { + PreparedStatement prep = conn.prepareStatement( + "select data from datastore_data where id = ?"); + try { + String id = StringUtils.convertBytesToHex(blockId); + prep.setString(1, id); + ResultSet rs = prep.executeQuery(); + if (!rs.next()) { + throw new IOException("Datastore block " + id + " not found"); + } + byte[] data = rs.getBytes(1); + // System.out.println(" read block " + id + " blockLen: " + data.length + " [0]: " + data[0]); + return data; + } finally { + prep.close(); + } + } finally { + conn.close(); + } + } + + @Override + public void clear() { + // TODO currently not cleared + } + +} Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java?rev=1161434&view=auto ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java (added) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java Thu Aug 25 08:18:57 2011 @@ -0,0 +1,82 @@ +/* + * 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.blobs; + +import org.apache.jackrabbit.mk.util.StringUtils; +import org.h2.util.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * A file blob store. + */ +public class FileBlobStore extends AbstractBlobStore { + + final private File baseDir; + + public FileBlobStore(String dir) { + baseDir = new File(dir); + baseDir.mkdirs(); + } + + @Override + protected void storeBlock(byte[] blockId, int level, byte[] data) throws IOException { + File f = getFile(blockId); + if (f.exists()) { + return; + } + File parent = f.getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } + File temp = new File(parent, f.getName() + ".temp"); + FileOutputStream out = new FileOutputStream(temp); + out.write(data); + out.close(); + temp.renameTo(f); + } + + private File getFile(byte[] blockId) { + String id = StringUtils.convertBytesToHex(blockId) + ".dat"; + File dir = new File(baseDir, id.substring(0, 2)); + File f = new File(dir, id); + return f; + } + + @Override + protected byte[] readBlockFromBackend(byte[] blockId) throws IOException { + File f = getFile(blockId); + int length = (int) f.length(); + byte[] data = new byte[length]; + FileInputStream in = new FileInputStream(f); + try { + IOUtils.readFully(in, data, 0, length); + } finally { + in.close(); + } + return data; + } + + @Override + public void clear() { + // TODO currently not cleared + } + +} \ No newline at end of file Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java?rev=1161434&view=auto ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java (added) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java Thu Aug 25 08:18:57 2011 @@ -0,0 +1,49 @@ +/* + * 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.blobs; + +import org.apache.jackrabbit.mk.util.StringUtils; + +import java.util.HashMap; + +/** + * A memory blob store. Useful for testing. + */ +public class MemoryBlobStore extends AbstractBlobStore { + + private HashMap map = new HashMap(); + + @Override + protected byte[] readBlockFromBackend(byte[] blockId) { + return map.get(getId(blockId)); + } + + private String getId(byte[] blockId) { + return StringUtils.convertBytesToHex(blockId); + } + + @Override + protected void storeBlock(byte[] blockId, int level, byte[] data) { + map.put(getId(blockId), data); + } + + @Override + public void clear() { + map.clear(); + } + +} Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsonBuilder.java Thu Aug 25 08:18:57 2011 @@ -146,8 +146,7 @@ public final class JsonBuilder { private void optionalComma() throws IOException { if (hasKeys) { writer.append(','); - } - else { + } else { hasKeys = true; } } @@ -233,8 +232,7 @@ public final class JsonBuilder { private void optionalComma() throws IOException { if (hasValues) { writer.append(','); - } - else { + } else { hasValues = true; } } @@ -244,14 +242,14 @@ public final class JsonBuilder { * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters (U+0000 through U+001F). */ public static String escape(String string) { - if(string == null) { + if (string == null) { return null; } StringBuilder sb = new StringBuilder(); - for(int i = 0; i < string.length(); i++) { + for (int i = 0; i < string.length(); i++) { char ch = string.charAt(i); - switch(ch) { + switch (ch) { case '"': sb.append("\\\""); break; @@ -285,8 +283,7 @@ public final class JsonBuilder { sb.append('0'); } sb.append(ss.toUpperCase()); - } - else { + } else { sb.append(ch); } } @@ -314,15 +311,15 @@ public final class JsonBuilder { public static String encode(float value) { // TODO silently losing data, should probably throw an exception instead return Float.isInfinite(value) || Float.isNaN(value) - ? "null" - : Float.toString(value); + ? "null" + : Float.toString(value); } public static String encode(double value) { // TODO silently losing data, should probably throw an exception instead return Double.isInfinite(value) || Double.isNaN(value) - ? "null" - : Double.toString(value); + ? "null" + : Double.toString(value); } public static String encode(Number value) { Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopBuilder.java Thu Aug 25 08:18:57 2011 @@ -215,63 +215,63 @@ public class JsopBuilder { /** * Escape a string into the target buffer. * - * @param s the string to escape + * @param s the string to escape * @param length the number of characters. - * @param buff the target buffer + * @param buff the target buffer */ public static void escape(String s, int length, StringBuilder buff) { for (int i = 0; i < length; i++) { char c = s.charAt(i); switch (c) { - case '"': - // quotation mark - buff.append("\\\""); - break; - case '\\': - // backslash - buff.append("\\\\"); - break; - case '\b': - // backspace - buff.append("\\b"); - break; - case '\f': - // formfeed - buff.append("\\f"); - break; - case '\n': - // newline - buff.append("\\n"); - break; - case '\r': - // carriage return - buff.append("\\r"); - break; - case '\t': - // horizontal tab - buff.append("\\t"); - break; - default: - int ch = c; - if (ch < ' ') { - // guaranteed to be 1 or 2 hex digits only - buff.append("\\u00"); - String hex = Integer.toHexString(c); - if (hex.length() == 1) { - buff.append('0'); + case '"': + // quotation mark + buff.append("\\\""); + break; + case '\\': + // backslash + buff.append("\\\\"); + break; + case '\b': + // backspace + buff.append("\\b"); + break; + case '\f': + // formfeed + buff.append("\\f"); + break; + case '\n': + // newline + buff.append("\\n"); + break; + case '\r': + // carriage return + buff.append("\\r"); + break; + case '\t': + // horizontal tab + buff.append("\\t"); + break; + default: + int ch = c; + if (ch < ' ') { + // guaranteed to be 1 or 2 hex digits only + buff.append("\\u00"); + String hex = Integer.toHexString(c); + if (hex.length() == 1) { + buff.append('0'); + } + buff.append(hex); + } else if (ch >= 127) { + // ascii only mode + buff.append("\\u"); + String hex = Integer.toHexString(c); + for (int len = hex.length(); len < 4; len++) { + buff.append('0'); + } + buff.append(hex); + } else { + buff.append(c); } - buff.append(hex); - } else if (ch >= 127) { - // ascii only mode - buff.append("\\u"); - String hex = Integer.toHexString(c); - for (int len = hex.length(); len < 4; len++) { - buff.append('0'); - } - buff.append(hex); - } else { - buff.append(c); - } } } } Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java?rev=1161434&r1=1161433&r2=1161434&view=diff ============================================================================== --- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java (original) +++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/json/JsopTokenizer.java Thu Aug 25 08:18:57 2011 @@ -25,7 +25,7 @@ public class JsopTokenizer { public final static int COMMENT = 5, ERROR = 6, END = 7; private static final String[] TYPE = { - "string", "number", "true", "false", "null", "error", "end" + "string", "number", "true", "false", "null", "error", "end" }; private final String jsop; @@ -153,63 +153,54 @@ public class JsopTokenizer { } int start = pos++; switch (ch) { - case '\"': { - while (true) { - ch = jsop.charAt(pos++); - if (ch == '\"') { - break; - } else if (ch == '\\') { - currentEscaped = true; - pos++; + case '\"': { + while (true) { + ch = jsop.charAt(pos++); + if (ch == '\"') { + break; + } else if (ch == '\\') { + currentEscaped = true; + pos++; + } } + currentToken = jsop.substring(start + 1, pos - 1); + return STRING; } - currentToken = jsop.substring(start + 1, pos - 1); - return STRING; - } - case '{': - case '}': - case '[': - case ']': - case '+': - case ':': - case ',': - case '>': - case '^': - return ch; - case '/': { - ch = jsop.charAt(pos); - if (ch != '*') { - return '/'; - } - pos++; - while (true) { - ch = jsop.charAt(pos++); - if (ch == '*' && jsop.charAt(pos) == '/') { - break; + case '{': + case '}': + case '[': + case ']': + case '+': + case ':': + case ',': + case '>': + case '^': + return ch; + case '/': { + ch = jsop.charAt(pos); + if (ch != '*') { + return '/'; } - } - currentToken = jsop.substring(start + 2, pos - 1); - pos += 2; - return COMMENT; - } - case '-': - ch = jsop.charAt(pos); - if (ch < '0' || ch > '9') { - // lookahead - return '-'; - } - // else fall though - default: - if (ch >= '0' && ch <= '9') { - while (pos < length) { - ch = jsop.charAt(pos); - if (ch < '0' || ch > '9') { + pos++; + while (true) { + ch = jsop.charAt(pos++); + if (ch == '*' && jsop.charAt(pos) == '/') { break; } - pos++; } - if (ch == '.') { - pos++; + currentToken = jsop.substring(start + 2, pos - 1); + pos += 2; + return COMMENT; + } + case '-': + ch = jsop.charAt(pos); + if (ch < '0' || ch > '9') { + // lookahead + return '-'; + } + // else fall though + default: + if (ch >= '0' && ch <= '9') { while (pos < length) { ch = jsop.charAt(pos); if (ch < '0' || ch > '9') { @@ -217,44 +208,53 @@ public class JsopTokenizer { } pos++; } - } - if (ch == 'e' || ch == 'E') { - ch = jsop.charAt(++pos); - if (ch == '+' || ch == '-') { + if (ch == '.') { + pos++; + while (pos < length) { + ch = jsop.charAt(pos); + if (ch < '0' || ch > '9') { + break; + } + pos++; + } + } + if (ch == 'e' || ch == 'E') { ch = jsop.charAt(++pos); + if (ch == '+' || ch == '-') { + ch = jsop.charAt(++pos); + } + while (pos < length) { + ch = jsop.charAt(pos); + if (ch < '0' || ch > '9') { + break; + } + pos++; + } } + currentToken = jsop.substring(start, pos); + return NUMBER; + } else if (ch >= 'a' && ch <= 'z') { while (pos < length) { ch = jsop.charAt(pos); - if (ch < '0' || ch > '9') { + if (ch < 'a' || ch > 'z') { break; } pos++; } - } - currentToken = jsop.substring(start, pos); - return NUMBER; - } else if (ch >= 'a' && ch <= 'z') { - while (pos < length) { - ch = jsop.charAt(pos); - if (ch < 'a' || ch > 'z') { - break; + String s = jsop.substring(start, pos); + if ("null".equals(s)) { + return NULL; + } else if ("true".equals(s)) { + return TRUE; + } else if ("false".equals(s)) { + return FALSE; + } else { + // currentToken = s; + // return STRING; + throw getFormatException(jsop, pos); } - pos++; - } - String s = jsop.substring(start, pos); - if ("null".equals(s)) { - return NULL; - } else if ("true".equals(s)) { - return TRUE; - } else if ("false".equals(s)) { - return FALSE; - } else { - // currentToken = s; - // return STRING; - throw getFormatException(jsop, pos); } - } - throw getFormatException(jsop, pos); + throw getFormatException(jsop, pos); } } @@ -275,42 +275,42 @@ public class JsopTokenizer { } c = s.charAt(++i); switch (c) { - case '"': - buff.append('"'); - break; - case '\\': - buff.append('\\'); - break; - case '/': - buff.append('/'); - break; - case 'b': - buff.append('\b'); - break; - case 'f': - buff.append('\f'); - break; - case 'n': - buff.append('\n'); - break; - case 'r': - buff.append('\r'); - break; - case 't': - buff.append('\t'); - break; - case 'u': { - try { - c = (char) (Integer.parseInt(s.substring(i + 1, i + 5), 16)); - } catch (NumberFormatException e) { - throw getFormatException(s, i); + case '"': + buff.append('"'); + break; + case '\\': + buff.append('\\'); + break; + case '/': + buff.append('/'); + break; + case 'b': + buff.append('\b'); + break; + case 'f': + buff.append('\f'); + break; + case 'n': + buff.append('\n'); + break; + case 'r': + buff.append('\r'); + break; + case 't': + buff.append('\t'); + break; + case 'u': { + try { + c = (char) (Integer.parseInt(s.substring(i + 1, i + 5), 16)); + } catch (NumberFormatException e) { + throw getFormatException(s, i); + } + i += 4; + buff.append(c); + break; } - i += 4; - buff.append(c); - break; - } - default: - throw getFormatException(s, i); + default: + throw getFormatException(s, i); } } else { buff.append(c); @@ -335,7 +335,7 @@ public class JsopTokenizer { * Add an asterisk ('[*]') at the given position. This format is used to * show where parsing failed in a statement. * - * @param s the text + * @param s the text * @param index the position * @return the text with asterisk */ @@ -360,36 +360,36 @@ public class JsopTokenizer { private void skipRawValue() { switch (currentType) { - case '[': - read(); - if (!matches(']')) { - do { - skipRawValue(); - } while (matches(',')); - read(']'); - } - break; - case '{': - read(); - if (!matches('}')) { - do { - readString(); - read(':'); - skipRawValue(); - } while (matches(',')); - read('}'); - } - break; - case NULL: - case NUMBER: - case TRUE: - case FALSE: - case COMMENT: - case STRING: - read(); - break; - default: - throw getFormatException(jsop, pos, "value"); + case '[': + read(); + if (!matches(']')) { + do { + skipRawValue(); + } while (matches(',')); + read(']'); + } + break; + case '{': + read(); + if (!matches('}')) { + do { + readString(); + read(':'); + skipRawValue(); + } while (matches(',')); + read('}'); + } + break; + case NULL: + case NUMBER: + case TRUE: + case FALSE: + case COMMENT: + case STRING: + read(); + break; + default: + throw getFormatException(jsop, pos, "value"); } }