Index: oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java (revision 1446067) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java (working copy) @@ -39,6 +39,7 @@ import org.apache.jackrabbit.mk.json.JsopTokenizer; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState; import org.apache.jackrabbit.oak.plugins.memory.BooleanPropertyState; import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder; @@ -69,7 +70,7 @@ private final String path; - private final String revision; + private String revision; private Map properties; @@ -100,44 +101,58 @@ this.cache = checkNotNull(cache); } - private synchronized void init() { - if (properties == null) { - String json = kernel.getNodes( - path, revision, 0, 0, MAX_CHILD_NODE_NAMES, - "{\"properties\":[\"*\",\":hash\"]}"); + private void init() { + boolean initialized = false; + synchronized (this) { + if (properties == null) { + String json = kernel.getNodes( + path, revision, 0, 0, MAX_CHILD_NODE_NAMES, + "{\"properties\":[\"*\",\":hash\"]}"); - JsopReader reader = new JsopTokenizer(json); - reader.read('{'); - properties = new LinkedHashMap(); - childPaths = new LinkedHashMap(); - do { - String name = StringCache.get(reader.readString()); - reader.read(':'); - if (":childNodeCount".equals(name)) { - childNodeCount = - Long.valueOf(reader.read(JsopReader.NUMBER)); - } else if (":hash".equals(name)) { - hash = new String(reader.read(JsopReader.STRING)); - } else if (reader.matches('{')) { - reader.read('}'); - String childPath = path + '/' + name; - if ("/".equals(path)) { - childPath = '/' + name; + JsopReader reader = new JsopTokenizer(json); + reader.read('{'); + properties = new LinkedHashMap(); + childPaths = new LinkedHashMap(); + do { + String name = StringCache.get(reader.readString()); + reader.read(':'); + if (":childNodeCount".equals(name)) { + childNodeCount = + Long.valueOf(reader.read(JsopReader.NUMBER)); + } else if (":hash".equals(name)) { + hash = new String(reader.read(JsopReader.STRING)); + } else if (reader.matches('{')) { + reader.read('}'); + String childPath = path + '/' + name; + if ("/".equals(path)) { + childPath = '/' + name; + } + childPaths.put(name, childPath); + } else if (reader.matches('[')) { + properties.put(name, readArrayProperty(name, reader)); + } else { + properties.put(name, readProperty(name, reader)); } - childPaths.put(name, childPath); - } else if (reader.matches('[')) { - properties.put(name, readArrayProperty(name, reader)); - } else { - properties.put(name, readProperty(name, reader)); + } while (reader.matches(',')); + reader.read('}'); + reader.read(JsopReader.END); + // optimize for empty childNodes + if (childPaths.isEmpty()) { + childPaths = Collections.emptyMap(); } - } while (reader.matches(',')); - reader.read('}'); - reader.read(JsopReader.END); - // optimize for empty childNodes - if (childPaths.isEmpty()) { - childPaths = Collections.emptyMap(); + initialized = true; } } + if (initialized && !PathUtils.denotesRoot(path) && hash != null) { + // check if we can re-use a previous revision + KernelNodeState cached = cache.getIfPresent(hash); + if (cached != null && cached.path.equals(this.path)) { + this.revision = cached.revision; + } else { + // store under hash key + cache.put(hash, this); + } + } } @Override Index: oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepeatedRead.java =================================================================== --- oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepeatedRead.java (revision 0) +++ oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/RepeatedRead.java (revision 0) @@ -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.oak.jcr; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.jackrabbit.commons.iterator.NodeIterable; +import org.apache.jackrabbit.test.AbstractJCRTest; + +/** + * Repeatedly reads the same content while another thread modifies a node in + * a different location. + */ +public class RepeatedRead extends AbstractJCRTest { + + private Node tmp; + + @Override + protected void setUp() throws Exception { + super.setUp(); + Node test = superuser.getRootNode().addNode("test"); + for (int i = 0; i < 10; i++) { + test.addNode("node-" + i); + } + tmp = superuser.getRootNode().addNode("tmp"); + superuser.save(); + } + + public void testRepeatedRead() throws Exception { + final AtomicBoolean stop = new AtomicBoolean(false); + Thread reader = new Thread(new Runnable() { + @Override + public void run() { + while (!stop.get()) { + try { + readTestTree(); + } catch (RepositoryException e) { + e.printStackTrace(); + } + } + } + }); + reader.start(); + for (int i = 0; i < 20; i++) { + modifyProperty(); + } + stop.set(true); + reader.join(); + } + + public void testSimple() throws Exception { + readTestTree(); + modifyProperty(); + readTestTree(); + readTestTree(); + } + + private void modifyProperty() throws RepositoryException { + tmp.setProperty("prop", Math.random()); + superuser.save(); + } + + private void readTestTree() throws RepositoryException { + Session s = getHelper().getSuperuserSession(); + try { + Node test = s.getRootNode().getNode("test"); + for (Node child : new NodeIterable(test.getNodes())) { + child.getProperties(); + } + } finally { + s.logout(); + } + } +} Property changes on: oak-jcr\src\test\java\org\apache\jackrabbit\oak\jcr\RepeatedRead.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Rev URL Added: svn:eol-style + native