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/09/09 18:49:50 UTC

svn commit: r1167288 - in /jackrabbit/sandbox/microkernel/src: main/java/org/apache/jackrabbit/mk/blobs/ main/java/org/apache/jackrabbit/mk/mem/ test/java/org/apache/jackrabbit/mk/concurrent/

Author: thomasm
Date: Fri Sep  9 16:49:50 2011
New Revision: 1167288

URL: http://svn.apache.org/viewvc?rev=1167288&view=rev
Log:
Fix concurrency problems, use the new cache

Added:
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentBlobTest.java
    jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentTest.java
Modified:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.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/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/mem/NodeList.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Revision.java

Modified: 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=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/AbstractBlobStore.java Fri Sep  9 16:49:50 2011
@@ -18,7 +18,7 @@ package org.apache.jackrabbit.mk.blobs;
 
 import org.apache.jackrabbit.mk.util.ExceptionFactory;
 import org.apache.jackrabbit.mk.util.IOUtils;
-import org.apache.jackrabbit.mk.util.SimpleLRUCache;
+import org.apache.jackrabbit.mk.util.Cache;
 import org.apache.jackrabbit.mk.util.StringUtils;
 
 import java.io.ByteArrayInputStream;
@@ -53,7 +53,7 @@ import java.security.MessageDigest;
  * long), size of data store id (variable size long), hash code length (variable
  * size int), hash code.
  */
-public abstract class AbstractBlobStore implements BlobStore {
+public abstract class AbstractBlobStore implements BlobStore, Cache.Backend<String, AbstractBlobStore.Data> {
 
     protected static final int TYPE_DATA = 0;
     protected static final int TYPE_HASH = 1;
@@ -73,8 +73,7 @@ public abstract class AbstractBlobStore 
      */
     private int blockSize = 2 * 1024 * 1024;
 
-    private SimpleLRUCache<String, byte[]> cache = SimpleLRUCache
-            .newInstance(5);
+    private Cache<String, Data> cache = Cache.newInstance(this, 8 * 1024 * 1024);
 
     public void setBlockSizeMin(int x) {
         this.blockSizeMin = x;
@@ -202,12 +201,15 @@ public abstract class AbstractBlobStore 
 
     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 cache.get(id).data;
+    }
+
+    public Data load(String id) {
+        try {
+            return new Data(readBlockFromBackend(StringUtils.convertHexToBytes(id)));
+        } catch (Exception e) {
+            throw ExceptionFactory.convert(e);
         }
-        return block;
     }
 
     protected abstract byte[] readBlockFromBackend(byte[] blockId) throws Exception;
@@ -247,4 +249,17 @@ public abstract class AbstractBlobStore 
 
     public abstract void clear();
 
+    public static class Data implements Cache.Value {
+
+        final byte[] data;
+
+        Data(byte[] data) {
+            this.data = data;
+        }
+
+        public int getMemory() {
+            return data.length;
+        }
+    }
+
 }

Modified: 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=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/DbBlobStore.java Fri Sep  9 16:49:50 2011
@@ -45,7 +45,7 @@ public class DbBlobStore extends Abstrac
     }
 
     @Override
-    protected void storeBlock(byte[] blockId, int level, byte[] data) throws SQLException {
+    protected synchronized void storeBlock(byte[] blockId, int level, byte[] data) throws SQLException {
         Connection conn = cp.getConnection();
         try {
             String id = StringUtils.convertBytesToHex(blockId);

Modified: 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=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/FileBlobStore.java Fri Sep  9 16:49:50 2011
@@ -37,7 +37,7 @@ public class FileBlobStore extends Abstr
     }
 
     @Override
-    protected void storeBlock(byte[] blockId, int level, byte[] data) throws IOException {
+    protected synchronized void storeBlock(byte[] blockId, int level, byte[] data) throws IOException {
         File f = getFile(blockId);
         if (f.exists()) {
             return;

Modified: 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=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/blobs/MemoryBlobStore.java Fri Sep  9 16:49:50 2011
@@ -28,7 +28,7 @@ public class MemoryBlobStore extends Abs
     private HashMap<String, byte[]> map = new HashMap<String, byte[]>();
 
     @Override
-    protected byte[] readBlockFromBackend(byte[] blockId) {
+    protected synchronized byte[] readBlockFromBackend(byte[] blockId) {
         return map.get(getId(blockId));
     }
 
@@ -37,12 +37,12 @@ public class MemoryBlobStore extends Abs
     }
 
     @Override
-    protected void storeBlock(byte[] blockId, int level, byte[] data) {
+    protected synchronized void storeBlock(byte[] blockId, int level, byte[] data) {
         map.put(getId(blockId), data);
     }
 
     @Override
-    public void clear() {
+    public synchronized void clear() {
         map.clear();
     }
 

Modified: 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=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java Fri Sep  9 16:49:50 2011
@@ -22,11 +22,11 @@ import org.apache.jackrabbit.mk.blobs.Fi
 import org.apache.jackrabbit.mk.blobs.MemoryBlobStore;
 import org.apache.jackrabbit.mk.json.JsopBuilder;
 import org.apache.jackrabbit.mk.json.JsopTokenizer;
+import org.apache.jackrabbit.mk.util.Cache;
 import org.apache.jackrabbit.mk.util.CommitGate;
 import org.apache.jackrabbit.mk.util.ExceptionFactory;
 import org.apache.jackrabbit.mk.util.NonDescendingClock;
 import org.apache.jackrabbit.mk.util.PathUtils;
-import org.apache.jackrabbit.mk.util.SimpleLRUCache;
 
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -65,8 +65,8 @@ public class MemoryKernelImpl implements
     private final AbstractBlobStore ds;
     private final NonDescendingClock clock = new NonDescendingClock(System.currentTimeMillis());
     private final CommitGate gate = new CommitGate();
-    private final SimpleLRUCache<Long, Revision> revisionCache = SimpleLRUCache.newInstance(1000);
-    private long headRevId;
+    private final Cache<Long, Revision> revisionCache = Cache.newInstance(null, 1024 * 1024);
+    private volatile long headRevId;
     private NodeMap nodeMap;
     private String lastJournalRevRange, lastJournal;
 
@@ -100,7 +100,7 @@ public class MemoryKernelImpl implements
         return instance;
     }
 
-    public void clear() {
+    public synchronized void clear() {
         if (DEBUG) {
             log("clear");
         }
@@ -308,13 +308,13 @@ public class MemoryKernelImpl implements
         }
         NodeImpl node = getRoot();
         ArrayList<Revision> revisions = new ArrayList<Revision>();
-        Revision r = Revision.get(revisionCache, node.getNode("head"));
+        Revision r = Revision.get(node.getNode("head"));
         if (since < r.getTime()) {
             revisions.add(r);
             while (node.exists("old")) {
                 node = node.getNode("old");
                 for (Iterator<String> it = node.getChildNodeNames(Integer.MAX_VALUE); it.hasNext();) {
-                    r = Revision.get(revisionCache, node.getNode(it.next()));
+                    r = Revision.get(node.getNode(it.next()));
                     if (r != null) {
                         revisions.add(r);
                     }
@@ -356,15 +356,16 @@ public class MemoryKernelImpl implements
         long toRev = Revision.parseId(toRevisionId);
         NodeImpl node = getRoot();
         ArrayList<Revision> revisions = new ArrayList<Revision>();
-        Revision r = Revision.get(revisionCache, node.getNode("head"));
+        Revision r = Revision.get(node.getNode("head"));
         if (r.getId() >= fromRev) {
             revisions.add(r);
         }
         while (r.getId() > fromRev && node.exists("old")) {
             node = node.getNode("old");
             for (Iterator<String> it = node.getChildNodeNames(Integer.MAX_VALUE); it.hasNext();) {
-                r = Revision.get(revisionCache, node.getNode(it.next()));
+                r = Revision.get(node.getNode(it.next()));
                 if (r != null && r.getId() >= fromRev && r.getId() <= toRev) {
+                    r = revisionCache.replace(r.getId(), r);
                     revisions.add(r);
                 }
             }
@@ -434,10 +435,13 @@ public class MemoryKernelImpl implements
     }
 
     private NodeImpl getRevision(String revisionId) {
-        long revId = Revision.parseId(revisionId);
         NodeImpl node = getRoot();
-        if (revId == headRevId) {
-            node = node.getNode("head");
+        NodeImpl head = node.getNode("head");
+        String headRev = head.getProperty("rev");
+        headRev = headRev == null ? null : JsopTokenizer.decodeQuoted(headRev);
+        // we can't rely on headRevId, as it's a volatile field
+        if (revisionId.equals(headRev)) {
+            node = head;
         } else {
             if (DEBUG) {
                 log("getRevision " + revisionId);
@@ -492,7 +496,7 @@ public class MemoryKernelImpl implements
         return ds.writeBlob(in);
     }
 
-    public void dispose() {
+    public synchronized void dispose() {
         if (DEBUG) {
             log("dispose");
         }

Modified: 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=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java Fri Sep  9 16:49:50 2011
@@ -24,11 +24,12 @@ import org.apache.jackrabbit.mk.json.Jso
 import org.apache.jackrabbit.mk.util.ExceptionFactory;
 import org.apache.jackrabbit.mk.util.PathUtils;
 import org.apache.jackrabbit.mk.util.StringCache;
+import org.apache.jackrabbit.mk.util.Cache;
 
 /**
  * An in-memory node, including all child nodes.
  */
-public class NodeImpl {
+public class NodeImpl implements Cache.Value {
 
     private final long revId;
     private final NodeMap map;
@@ -36,6 +37,7 @@ public class NodeImpl {
     private NodeList childNodes;
     private String path;
     private long id;
+    private int memory;
 
     public NodeImpl(NodeMap map, long revId) {
         this.map = map;
@@ -485,4 +487,19 @@ public class NodeImpl {
         void accept(long childId);
     }
 
+    public int getMemory() {
+        if (memory == 0) {
+            String[] pv = propertyValuePairs;
+            if (pv != null) {
+                for (int i = 0; i < pv.length; i++) {
+                    memory += pv[i].length() * 2;
+                }
+            }
+            if (childNodes != null) {
+                memory += childNodes.getMemory();
+            }
+        }
+        return memory;
+    }
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java?rev=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeList.java Fri Sep  9 16:49:50 2011
@@ -45,4 +45,6 @@ interface NodeList {
 
     byte[] getNameFilter(NodeMap map);
 
+    int getMemory();
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java?rev=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListLarge.java Fri Sep  9 16:49:50 2011
@@ -270,4 +270,8 @@ public class NodeListLarge implements No
         return data;
     }
 
+    public int getMemory() {
+        return children.size() * 100;
+    }
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java?rev=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java Fri Sep  9 16:49:50 2011
@@ -182,4 +182,12 @@ public class NodeListSmall implements No
         return data;
     }
 
+    public int getMemory() {
+        int memory = 100;
+        for (int i = 0; i < names.length; i++) {
+            memory += names[i].length() * 2 + 8;
+        }
+        return memory;
+    }
+
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java?rev=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeMap.java Fri Sep  9 16:49:50 2011
@@ -16,7 +16,10 @@
  */
 package org.apache.jackrabbit.mk.mem;
 
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 import org.apache.jackrabbit.mk.util.ExceptionFactory;
 
 public class NodeMap {
@@ -24,15 +27,15 @@ public class NodeMap {
     private static final String MAX_MEMORY_CHILDREN = "maxMemoryChildren";
     private static final int DEFAULT_MAX_MEMORY_CHILDREN = Integer.MAX_VALUE;
 
-    private HashMap<Long, NodeImpl> nodes = new HashMap<Long, NodeImpl>();
-    private long nextId = 1;
-    private long rootId;
+    private Map<Long, NodeImpl> nodes = Collections.synchronizedMap(new HashMap<Long, NodeImpl>());
+    private AtomicLong nextId = new AtomicLong();
+    private AtomicLong rootId = new AtomicLong();
     private int maxMemoryChildren = DEFAULT_MAX_MEMORY_CHILDREN;
 
     public long addNode(NodeImpl node) {
         long x = node.getId();
         if (x == 0) {
-            x = nextId++;
+            x = nextId.incrementAndGet();
             node.setId(x);
             nodes.put(x, node);
         }
@@ -45,7 +48,7 @@ public class NodeMap {
 
     public void clear() {
         nodes.clear();
-        nextId = 1;
+        nextId.set(0);
     }
 
     public void setSetting(String key, String value) {
@@ -73,11 +76,13 @@ public class NodeMap {
     }
 
     public long commit(NodeImpl root) {
-        return rootId = addNode(root);
+        long x = addNode(root);
+        rootId.set(x);
+        return x;
     }
 
     public long getRootId() {
-        return rootId;
+        return rootId.get();
     }
 
     public String formatId(long id) {

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Revision.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Revision.java?rev=1167288&r1=1167287&r2=1167288&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Revision.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/Revision.java Fri Sep  9 16:49:50 2011
@@ -17,13 +17,13 @@
 package org.apache.jackrabbit.mk.mem;
 
 import org.apache.jackrabbit.mk.json.JsopBuilder;
+import org.apache.jackrabbit.mk.util.Cache;
 import org.apache.jackrabbit.mk.json.JsopTokenizer;
-import org.apache.jackrabbit.mk.util.SimpleLRUCache;
 
 /**
  * A revision, including pointer to the root node of that revision.
  */
-public class Revision implements Comparable<Revision> {
+public class Revision implements Comparable<Revision>, Cache.Value {
 
     private NodeImpl node;
     private final long id;
@@ -38,24 +38,20 @@ public class Revision implements Compara
         this.msg = msg == null ? "" : msg;
     }
 
-    private Revision(NodeImpl node, long id) {
-        this.node = node;
+    Revision(long id, long time, NodeImpl node) {
         this.id = id;
-        time = Long.parseLong(node.getProperty("time"));
+        this.time = time;
+        this.node = node;
     }
 
-    static Revision get(SimpleLRUCache<Long, Revision>cache, NodeImpl node) {
+    static Revision get(NodeImpl node) {
         String rev = node.getProperty("rev");
         if (rev == null) {
             return null;
         }
         long id = parseId(JsopTokenizer.decodeQuoted(rev));
-        Revision r = cache.get(id);
-        if (r == null) {
-            r = new Revision(node, id);
-            cache.put(id, r);
-        }
-        return r;
+        long time = Long.parseLong(node.getProperty("time"));
+        return new Revision(id, time, node);
     }
 
     long getId() {
@@ -128,4 +124,8 @@ public class Revision implements Compara
         endObject().newline();
     }
 
+    public int getMemory() {
+        return (getDiff().length() + getMsg().length()) * 2;
+    }
+
 }

Added: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentBlobTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentBlobTest.java?rev=1167288&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentBlobTest.java (added)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentBlobTest.java Fri Sep  9 16:49:50 2011
@@ -0,0 +1,59 @@
+/*
+ * 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.concurrent;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import org.apache.jackrabbit.mk.blobs.MemoryBlobStore;
+import org.apache.jackrabbit.mk.util.IOUtils;
+import org.apache.jackrabbit.mk.util.IOUtilsTest;
+
+/**
+ * Test concurrent access to the blob store.
+ */
+public class ConcurrentBlobTest extends ConcurrentTest {
+
+    private static final byte[] EMPTY = new byte[50];
+
+    MemoryBlobStore store = new MemoryBlobStore();
+
+    public void setUp() throws Exception {
+        store.setBlockSizeMin(50);
+    }
+
+    public void tearDown() throws InterruptedException {
+        store.clear();
+    }
+
+    void access() {
+        try {
+            int i = id.getAndIncrement();
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            IOUtils.writeLong(out, i);
+            out.write(EMPTY);
+            byte[] data = out.toByteArray();
+            String id = store.writeBlob(new ByteArrayInputStream(data));
+            assertEquals(58, store.getBlobLength(id));
+            byte[] test = out.toByteArray();
+            assertEquals(8, store.readBlob(id, 0, test, 0, 8));
+            IOUtilsTest.assertEquals(data, test);
+        } catch (Throwable t) {
+            lastException = new Exception(t);
+        }
+    }
+
+}

Added: jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentTest.java?rev=1167288&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentTest.java (added)
+++ jackrabbit/sandbox/microkernel/src/test/java/org/apache/jackrabbit/mk/concurrent/ConcurrentTest.java Fri Sep  9 16:49:50 2011
@@ -0,0 +1,92 @@
+/*
+ * 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.concurrent;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import junit.framework.TestCase;
+import org.apache.jackrabbit.mk.MicroKernelFactory;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+
+/**
+ * Test concurrent access to nodes, the journal, and revision.
+ */
+public class ConcurrentTest extends TestCase {
+
+    // private static final String URL = "fs:{homeDir}/target;clean";
+    private static final String URL = "mem:";
+    // private static final String URL = "mem:fs:target/temp;clean";
+
+    protected MicroKernel mk;
+    protected AtomicInteger id = new AtomicInteger();
+    protected Exception lastException;
+    boolean stop;
+
+    public void setUp() throws Exception {
+        mk = MicroKernelFactory.getInstance(URL);
+    }
+
+    public void tearDown() throws InterruptedException {
+        mk.dispose();
+    }
+
+    public void test() throws Exception {
+        Thread t = new Thread("Test") {
+            public void run() {
+                while (!stop) {
+                    access();
+                }
+            }
+        };
+        t.start();
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < 100000; i++) {
+            if (i % 100 == 0 && System.currentTimeMillis() - start > 1000) {
+                break;
+            }
+            access();
+        }
+        stop = true;
+        t.join();
+        if (lastException != null) {
+            throw lastException;
+        }
+
+    }
+
+    void access() {
+        try {
+            long start = System.currentTimeMillis();
+            String rev = mk.getHeadRevision();
+            int i = id.getAndIncrement();
+            String newRev = mk.commit("/", "+\"" + i + "\":{\"x\": " + i + "}", rev, "");
+            assertTrue(!newRev.equals(rev));
+            mk.getJournal(rev, newRev);
+            mk.getRevisions(start, 100);
+            mk.getNodes("/" + i, newRev);
+            mk.getNodes("/" + i, newRev, 0, 0, 0);
+            assertFalse(mk.nodeExists("/" + i, rev));
+            assertTrue(mk.nodeExists("/" + i, newRev));
+            rev = newRev;
+            newRev = mk.commit("/", "-\"" + i + "\"", rev, "");
+            assertTrue(mk.nodeExists("/" + i, rev));
+            assertFalse(mk.nodeExists("/" + i, newRev));
+        } catch (Throwable t) {
+            lastException = new Exception(t);
+        }
+    }
+
+}