You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by al...@apache.org on 2013/11/19 11:41:23 UTC

svn commit: r1543371 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/core/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strateg...

Author: alexparvulescu
Date: Tue Nov 19 10:41:23 2013
New Revision: 1543371

URL: http://svn.apache.org/r1543371
Log:
OAK-1137 Node.getReferences() is slow due to missing property index


Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractTree.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProvider.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
    jackrabbit/oak/trunk/oak-jcr/pom.xml
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractTree.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractTree.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractTree.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/core/AbstractTree.java Tue Nov 19 10:41:23 2013
@@ -28,6 +28,7 @@ import org.apache.jackrabbit.mk.api.Micr
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.reference.NodeReferenceConstants;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
@@ -57,6 +58,8 @@ public abstract class AbstractTree imple
     // TODO: make this configurable
     private static final String[] INTERNAL_NODE_NAMES = {
             IndexConstants.INDEX_CONTENT_NODE_NAME,
+            NodeReferenceConstants.REF_NAME,
+            NodeReferenceConstants.WEAK_REF_NAME,
             MicroKernel.CONFLICT_NAME};
 
     /**

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/identifier/IdentifierManager.java Tue Nov 19 10:41:23 2013
@@ -27,7 +27,6 @@ import java.text.ParseException;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 import javax.annotation.CheckForNull;
@@ -38,7 +37,6 @@ import javax.jcr.query.Query;
 import com.google.common.base.Charsets;
 import com.google.common.base.Function;
 import com.google.common.collect.Iterators;
-import com.google.common.collect.Sets;
 import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.PropertyValue;
@@ -51,6 +49,7 @@ import org.apache.jackrabbit.oak.commons
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState;
 import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager;
+import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -188,23 +187,21 @@ public class IdentifierManager {
      *         specified {@code tree} and matching the constraints.
      */
     @Nonnull
-    public Set<String> getReferences(boolean weak, Tree tree, final String propertyName, final String... nodeTypeNames) {
+    public Iterable<String> getReferences(boolean weak, Tree tree, final String propertyName, final String... nodeTypeNames) {
         if (!nodeTypeManager.isNodeType(tree, JcrConstants.MIX_REFERENCEABLE)) {
             return Collections.emptySet(); // shortcut
         }
 
-        try {
-            final String uuid = getIdentifier(tree);
-            String reference = weak ? PropertyType.TYPENAME_WEAKREFERENCE : PropertyType.TYPENAME_REFERENCE;
-            String pName = propertyName == null ? "*" : propertyName;   // TODO: sanitize against injection attacks!?
-            Map<String, ? extends PropertyValue> bindings = Collections.singletonMap("uuid", PropertyValues.newString(uuid));
+        final String uuid = getIdentifier(tree);
+        String reference = weak ? PropertyType.TYPENAME_WEAKREFERENCE : PropertyType.TYPENAME_REFERENCE;
+        String pName = propertyName == null ? "*" : propertyName;   // TODO: sanitize against injection attacks!?
+        Map<String, ? extends PropertyValue> bindings = Collections.singletonMap("uuid", PropertyValues.newString(uuid));
 
+        try {
             Result result = root.getQueryEngine().executeQuery(
                     "SELECT * FROM [nt:base] WHERE PROPERTY([" + pName + "], '" + reference + "') = $uuid",
                     Query.JCR_SQL2, Long.MAX_VALUE, 0, bindings, new NamePathMapper.Default());
-
-            Iterable<String> paths = findPaths(result, uuid, propertyName, nodeTypeNames);
-            return Sets.newHashSet(paths);
+            return findPaths(result, uuid, propertyName, nodeTypeNames);
         } catch (ParseException e) {
             log.error("query failed", e);
             return Collections.emptySet();
@@ -242,7 +239,7 @@ public class IdentifierManager {
                     }
 
                     // skip references from the version storage (OAK-1196)
-                    if (!rowPath.startsWith("/jcr:system/jcr:versionStorage/")) {
+                    if (!rowPath.startsWith(VersionConstants.VERSION_STORE_PATH)) { 
                         Tree tree = root.getTree(rowPath);
                         if (nodeTypeNames.length == 0 || containsNodeType(tree, nodeTypeNames)) {
                             if (propertyName == null) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/ContentMirrorStoreStrategy.java Tue Nov 19 10:41:23 2013
@@ -115,10 +115,10 @@ public class ContentMirrorStoreStrategy 
         builder.setProperty("match", true);
     }
 
-    @Override
-    public Iterable<String> query(final Filter filter, final String indexName, 
-            final NodeState indexMeta, final Iterable<String> values) {
-        final NodeState index = indexMeta.getChildNode(INDEX_CONTENT_NODE_NAME);
+    public Iterable<String> query(final Filter filter, final String indexName,
+            final NodeState indexMeta, final String indexStorageNodeName,
+            final Iterable<String> values) {
+        final NodeState index = indexMeta.getChildNode(indexStorageNodeName);
         return new Iterable<String>() {
             @Override
             public Iterator<String> iterator() {
@@ -142,8 +142,19 @@ public class ContentMirrorStoreStrategy 
     }
 
     @Override
+    public Iterable<String> query(final Filter filter, final String indexName, 
+            final NodeState indexMeta, final Iterable<String> values) {
+        return query(filter, indexName, indexMeta, INDEX_CONTENT_NODE_NAME, values);
+    }
+
+    @Override
     public long count(NodeState indexMeta, Set<String> values, int max) {
-        NodeState index = indexMeta.getChildNode(INDEX_CONTENT_NODE_NAME);
+        return count(indexMeta, INDEX_CONTENT_NODE_NAME, values, max);
+    }
+
+    public long count(NodeState indexMeta, final String indexStorageNodeName,
+            Set<String> values, int max) {
+        NodeState index = indexMeta.getChildNode(indexStorageNodeName);
         int count = 0;
         if (values == null) {
             PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceEditor.java Tue Nov 19 10:41:23 2013
@@ -16,8 +16,10 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.reference;
 
+import static com.google.common.collect.ImmutableSet.of;
 import static com.google.common.collect.Maps.newHashMap;
 import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
 import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
 import static org.apache.jackrabbit.oak.api.CommitFailedException.INTEGRITY;
 import static org.apache.jackrabbit.oak.api.Type.REFERENCE;
@@ -28,8 +30,10 @@ import static org.apache.jackrabbit.oak.
 import static org.apache.jackrabbit.oak.api.Type.WEAKREFERENCES;
 import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
 import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.commons.PathUtils.isAbsolute;
 import static org.apache.jackrabbit.oak.plugins.index.reference.NodeReferenceConstants.REF_NAME;
 import static org.apache.jackrabbit.oak.plugins.index.reference.NodeReferenceConstants.WEAK_REF_NAME;
+import static org.apache.jackrabbit.oak.plugins.version.VersionConstants.SYSTEM_PATHS;
 
 import java.util.Map;
 import java.util.Map.Entry;
@@ -37,12 +41,11 @@ import java.util.Set;
 
 import javax.annotation.Nonnull;
 
-import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.core.ImmutableRoot;
 import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
-import org.apache.jackrabbit.oak.plugins.version.VersionConstants;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -54,9 +57,7 @@ import org.apache.jackrabbit.oak.spi.sta
  */
 class ReferenceEditor extends DefaultEditor {
 
-    // TODO
-    // - look into using a storage strategy (trees)
-    // - what happens when you move a node? who updates the backlinks?
+    private static ContentMirrorStoreStrategy STORE = new ContentMirrorStoreStrategy();
 
     /** Parent editor, or {@code null} if this is the root editor. */
     private final ReferenceEditor parent;
@@ -111,6 +112,13 @@ class ReferenceEditor extends DefaultEdi
      */
     private final Set<String> discardedIds;
 
+    /**
+     * set of ids that were added during this commit. we need it to reconcile
+     * moves
+     * 
+     */
+    private final Set<String> newIds;
+
     public ReferenceEditor(NodeBuilder builder) {
         this.parent = null;
         this.name = null;
@@ -123,6 +131,7 @@ class ReferenceEditor extends DefaultEdi
         this.rmWeakRefs = newHashMap();
         this.rmIds = newHashSet();
         this.discardedIds = newHashSet();
+        this.newIds = newHashSet();
     }
 
     private ReferenceEditor(ReferenceEditor parent, String name, String uuid) {
@@ -137,6 +146,7 @@ class ReferenceEditor extends DefaultEdi
         this.rmWeakRefs = parent.rmWeakRefs;
         this.rmIds = parent.rmIds;
         this.discardedIds = parent.discardedIds;
+        this.newIds = parent.newIds;
     }
 
     /**
@@ -174,6 +184,7 @@ class ReferenceEditor extends DefaultEdi
         if (parent == null) {
             Set<String> offending = newHashSet(rmIds);
             offending.removeAll(rmRefs.keySet());
+            offending.removeAll(newIds);
             if (!offending.isEmpty()) {
                 throw new CommitFailedException(INTEGRITY, 1,
                         "Unable to delete referenced node");
@@ -198,7 +209,7 @@ class ReferenceEditor extends DefaultEdi
                 if (newRefs.containsKey(uuid)) {
                     add = newRefs.remove(uuid);
                 }
-                set(child, REF_NAME, add, rm);
+                update(child, REF_NAME, uuid, add, rm);
             }
             for (Entry<String, Set<String>> ref : newRefs.entrySet()) {
                 String uuid = ref.getKey();
@@ -213,7 +224,7 @@ class ReferenceEditor extends DefaultEdi
                 }
                 Set<String> add = ref.getValue();
                 Set<String> rm = newHashSet();
-                set(child, REF_NAME, add, rm);
+                update(child, REF_NAME, uuid, add, rm);
             }
             for (Entry<String, Set<String>> ref : rmWeakRefs.entrySet()) {
                 String uuid = ref.getKey();
@@ -231,7 +242,7 @@ class ReferenceEditor extends DefaultEdi
                 if (newWeakRefs.containsKey(uuid)) {
                     add = newWeakRefs.remove(uuid);
                 }
-                set(child, WEAK_REF_NAME, add, rm);
+                update(child, WEAK_REF_NAME, uuid, add, rm);
             }
             for (Entry<String, Set<String>> ref : newWeakRefs.entrySet()) {
                 String uuid = ref.getKey();
@@ -246,7 +257,7 @@ class ReferenceEditor extends DefaultEdi
                 }
                 Set<String> add = ref.getValue();
                 Set<String> rm = newHashSet();
-                set(child, WEAK_REF_NAME, add, rm);
+                update(child, WEAK_REF_NAME, uuid, add, rm);
             }
         }
     }
@@ -260,11 +271,13 @@ class ReferenceEditor extends DefaultEdi
     public void propertyChanged(PropertyState before, PropertyState after) {
         if (before != null) {
             if (before.getType() == REFERENCE || before.getType() == REFERENCES) {
-                put(rmRefs, before.getValue(STRINGS), concat(getPath(), before.getName()));
+                put(rmRefs, before.getValue(STRINGS),
+                        concat(getPath(), before.getName()));
             }
             if (before.getType() == WEAKREFERENCE
                     || before.getType() == WEAKREFERENCES) {
-                put(rmWeakRefs, before.getValue(STRINGS), concat(getPath(), before.getName()));
+                put(rmWeakRefs, before.getValue(STRINGS),
+                        concat(getPath(), before.getName()));
             }
             if (JCR_UUID.equals(before.getName())) {
                 // node remove + add -> changed uuid
@@ -276,11 +289,13 @@ class ReferenceEditor extends DefaultEdi
         }
         if (after != null) {
             if (after.getType() == REFERENCE || after.getType() == REFERENCES) {
-                put(newRefs, after.getValue(STRINGS), concat(getPath(), after.getName()));
+                put(newRefs, after.getValue(STRINGS),
+                        concat(getPath(), after.getName()));
             }
             if (after.getType() == WEAKREFERENCE
                     || after.getType() == WEAKREFERENCES) {
-                put(newWeakRefs, after.getValue(STRINGS), concat(getPath(), after.getName()));
+                put(newWeakRefs, after.getValue(STRINGS),
+                        concat(getPath(), after.getName()));
             }
         }
     }
@@ -296,7 +311,11 @@ class ReferenceEditor extends DefaultEdi
         if (isVersionStorePath(path)) {
             return null;
         }
-        return new ReferenceEditor(this, name, after.getString(JCR_UUID));
+        String uuid = after.getString(JCR_UUID);
+        if (uuid != null) {
+            newIds.add(uuid);
+        }
+        return new ReferenceEditor(this, name, uuid);
     }
 
     @Override
@@ -317,10 +336,8 @@ class ReferenceEditor extends DefaultEdi
             return null;
         }
         String uuid = before.getString(JCR_UUID);
-        if (before.hasProperty(REF_NAME)) {
-            if (uuid != null) {
-                rmIds.add(uuid);
-            }
+        if (uuid != null && check(before, REF_NAME, uuid)) {
+            rmIds.add(uuid);
         }
         return new ReferenceEditor(this, name, uuid);
     }
@@ -354,8 +371,8 @@ class ReferenceEditor extends DefaultEdi
     }
 
     private static boolean isVersionStorePath(@Nonnull String oakPath) {
-        if (oakPath.indexOf(JcrConstants.JCR_SYSTEM) == 1) {
-            for (String p : VersionConstants.SYSTEM_PATHS) {
+        if (oakPath.indexOf(JCR_SYSTEM) == 1) {
+            for (String p : SYSTEM_PATHS) {
                 if (oakPath.startsWith(p)) {
                     return true;
                 }
@@ -366,35 +383,32 @@ class ReferenceEditor extends DefaultEdi
 
     private static void put(Map<String, Set<String>> map,
             Iterable<String> keys, String value) {
+        String asRelative = isAbsolute(value) ? value.substring(1) : value;
         for (String key : keys) {
             Set<String> values = map.get(key);
             if (values == null) {
                 values = newHashSet();
             }
-            values.add(value);
+            values.add(asRelative);
             map.put(key, values);
         }
     }
 
-    private static void set(NodeBuilder child, String name, Set<String> add,
-            Set<String> rm) {
-        // TODO should we optimize for the remove/add case? intersect the
-        // sets, work on the diffs?
-
-        Set<String> vals;
-        PropertyState ref = child.getProperty(name);
-        if (ref != null) {
-            vals = newHashSet(ref.getValue(STRINGS));
-        } else {
-            vals = newHashSet();
-        }
-        vals.addAll(add);
-        vals.removeAll(rm);
-        if (!vals.isEmpty()) {
-            child.setProperty(name, vals, STRINGS);
-        } else {
-            child.removeProperty(name);
+    private static void update(NodeBuilder child, String name, String key,
+            Set<String> add, Set<String> rm) {
+        NodeBuilder index = child.child(name);
+        Set<String> empty = of();
+        for (String p : add) {
+            // TODO do we still need to encode the values?
+            STORE.update(index, p, empty, of(key));
+        }
+        for (String p : rm) {
+            STORE.update(index, p, of(key), empty);
         }
     }
 
+    private static boolean check(NodeState ns, String name, String key) {
+        return ns.hasChildNode(name) && STORE.count(ns, name, of(key), 1) > 0;
+    }
+
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java?rev=1543371&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java Tue Nov 19 10:41:23 2013
@@ -0,0 +1,151 @@
+/*
+ * 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.plugins.index.reference;
+
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.transform;
+import static java.lang.Double.POSITIVE_INFINITY;
+import static javax.jcr.PropertyType.REFERENCE;
+import static javax.jcr.PropertyType.WEAKREFERENCE;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getName;
+import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
+import static org.apache.jackrabbit.oak.plugins.index.reference.NodeReferenceConstants.REF_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.reference.NodeReferenceConstants.WEAK_REF_NAME;
+import static org.apache.jackrabbit.oak.spi.query.Cursors.newPathCursor;
+
+import java.util.ArrayList;
+
+import org.apache.jackrabbit.oak.core.ImmutableRoot;
+import org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
+import org.apache.jackrabbit.oak.query.index.FilterImpl;
+import org.apache.jackrabbit.oak.spi.query.Cursor;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+/**
+ * Provides a QueryIndex that does lookups for node references based on a custom
+ * index saved on hidden property names
+ * 
+ */
+class ReferenceIndex implements QueryIndex {
+
+    private static ContentMirrorStoreStrategy STORE = new ContentMirrorStoreStrategy();
+
+    @Override
+    public String getIndexName() {
+        return "reference";
+    }
+
+    @Override
+    public double getCost(Filter filter, NodeState root) {
+        // TODO don't call getCost for such queries
+        if (filter.getFullTextConstraint() != null) {
+            // not an appropriate index for full-text search
+            return POSITIVE_INFINITY;
+        }
+        for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
+            if (pr.propertyType == REFERENCE
+                    || pr.propertyType == WEAKREFERENCE) {
+                return 1;
+            }
+        }
+        // not an appropriate index
+        return POSITIVE_INFINITY;
+    }
+
+    @Override
+    public Cursor query(Filter filter, NodeState root) {
+        for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
+            if (pr.propertyType == REFERENCE) {
+                String uuid = pr.first.getValue(STRING);
+                String name = pr.propertyName;
+                return lookup(root, uuid, name, REF_NAME);
+            }
+            if (pr.propertyType == WEAKREFERENCE) {
+                String uuid = pr.first.getValue(STRING);
+                String name = pr.propertyName;
+                return lookup(root, uuid, name, WEAK_REF_NAME);
+            }
+        }
+        return newPathCursor(new ArrayList<String>());
+    }
+
+    private static Cursor lookup(NodeState state, String uuid,
+            final String name, String index) {
+        String path = getIdManager(state).resolveUUID(uuid);
+        NodeState child = state;
+        for (String p : elements(path)) {
+            child = child.getChildNode(p);
+        }
+        if (!child.exists()) {
+            return newPathCursor(new ArrayList<String>());
+        }
+        Iterable<String> paths = STORE.query(new FilterImpl(), index + "("
+                + uuid + ")", child, index, null);
+
+        if (!"*".equals(name)) {
+            paths = filter(paths, new Predicate<String>() {
+                @Override
+                public boolean apply(String path) {
+                    return name.equals(getName(path));
+                }
+            });
+        }
+        paths = transform(paths, new Function<String, String>() {
+            @Override
+            public String apply(String path) {
+                return getParentPath(path);
+            }
+        });
+        return newPathCursor(paths);
+    }
+
+    private static IdentifierManager getIdManager(NodeState state) {
+        return new IdentifierManager(new ImmutableRoot(state));
+    }
+
+    @Override
+    public String getPlan(Filter filter, NodeState root) {
+        StringBuilder buff = new StringBuilder("reference");
+        for (PropertyRestriction pr : filter.getPropertyRestrictions()) {
+            if (pr.propertyType == REFERENCE) {
+                buff.append(" PROPERTY([");
+                buff.append(pr.propertyName);
+                buff.append("], 'Reference') = ");
+                buff.append(pr.first.getValue(STRING));
+                return buff.toString();
+            }
+            if (pr.propertyType == WEAKREFERENCE) {
+                buff.append(" PROPERTY([");
+                buff.append(pr.propertyName);
+                buff.append("], 'WeakReference') = ");
+                buff.append(pr.first.getValue(STRING));
+                return buff.toString();
+            }
+        }
+        return buff.toString();
+    }
+
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndex.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java?rev=1543371&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java Tue Nov 19 10:41:23 2013
@@ -0,0 +1,43 @@
+/*
+ * 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.plugins.index.reference;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A provider for node references indices.
+ */
+@Component
+@Service(QueryIndexProvider.class)
+public class ReferenceIndexProvider implements QueryIndexProvider {
+
+    @Override
+    @Nonnull
+    public List<QueryIndex> getQueryIndexes(NodeState state) {
+        return ImmutableList.<QueryIndex> of(new ReferenceIndex());
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/reference/ReferenceIndexProvider.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProvider.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/MembershipProvider.java Tue Nov 19 10:41:23 2013
@@ -101,7 +101,7 @@ class MembershipProvider extends Authori
     @Nonnull
     Iterator<String> getMembership(Tree authorizableTree, boolean includeInherited) {
         Set<String> groupPaths = new HashSet<String>();
-        Set<String> refPaths = identifierManager.getReferences(true, authorizableTree, REP_MEMBERS, NT_REP_GROUP, NT_REP_MEMBERS);
+        Iterable<String> refPaths = identifierManager.getReferences(true, authorizableTree, REP_MEMBERS, NT_REP_GROUP, NT_REP_MEMBERS);
         for (String propPath : refPaths) {
             int index = propPath.indexOf('/' + REP_MEMBERS);
             if (index > 0) {

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java Tue Nov 19 10:41:23 2013
@@ -44,6 +44,8 @@ import org.apache.jackrabbit.oak.api.Roo
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceIndexProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationEditorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
 import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
@@ -78,6 +80,8 @@ public abstract class AbstractSecurityTe
     public void before() throws Exception {
         Oak oak = new Oak()
                 .with(new InitialContent())
+                .with(new ReferenceEditorProvider())
+                .with(new ReferenceIndexProvider())
                 .with(new PropertyIndexEditorProvider())
                 .with(new PropertyIndexProvider())
                 .with(new RegistrationEditorProvider())

Modified: jackrabbit/oak/trunk/oak-jcr/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/pom.xml?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-jcr/pom.xml Tue Nov 19 10:41:23 2013
@@ -50,9 +50,6 @@
       org.apache.jackrabbit.test.api.WorkspaceMoveSameNameSibsTest#testMoveNodesOrderingSupportedByParent <!-- OAK-118 -->
       org.apache.jackrabbit.test.api.WorkspaceMoveTest#testMoveNodesLocked                             <!-- OAK-118 -->
 
-      org.apache.jackrabbit.oak.jcr.ReferencesTest#testMovedReferences <!-- OAK-1195 -->
-      org.apache.jackrabbit.oak.jcr.ReferencesTest#testMovedVersionedReferences <!-- OAK-1195 -->
-
       <!-- Locking : not fully implemented -->
       org.apache.jackrabbit.test.api.lock.LockTest#testNodeLocked
       org.apache.jackrabbit.test.api.lock.LockTest#testParentChildDeepLock
@@ -96,7 +93,6 @@
       org.apache.jackrabbit.test.api.version.RestoreTest#testRestoreLabel
       org.apache.jackrabbit.test.api.version.RestoreTest#testRestoreLabelJcr2
       org.apache.jackrabbit.test.api.version.WorkspaceRestoreTest
-      org.apache.jackrabbit.test.api.version.GetReferencesNodeTest#testGetReferencesNeverFromVersions <!-- Node.getReferences must not return references from version storage -->
       org.apache.jackrabbit.test.api.version.MergeCancelMergeTest
       org.apache.jackrabbit.test.api.version.MergeCheckedoutSubNodeTest
       org.apache.jackrabbit.test.api.version.MergeDoneMergeTest
@@ -176,9 +172,6 @@
 
       <!-- Node Types -->
       org.apache.jackrabbit.oak.jcr.nodetype.MixinTest#testRemoveAddMixVersionable1                  <!-- OAK-1118 -->
-
-      org.apache.jackrabbit.test.api.SessionUUIDTest#testSaveReferentialIntegrityException           <!-- OAK-685 -->
-      org.apache.jackrabbit.test.api.NodeUUIDTest#testSaveReferentialIntegrityException              <!-- OAK-685 -->
     </known.issues>
   </properties>
 

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/Jcr.java Tue Nov 19 10:41:23 2013
@@ -32,6 +32,8 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.nodetype.NodeTypeIndexProvider;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceIndexProvider;
 import org.apache.jackrabbit.oak.plugins.name.NameValidatorProvider;
 import org.apache.jackrabbit.oak.plugins.name.NamespaceValidatorProvider;
 import org.apache.jackrabbit.oak.plugins.nodetype.RegistrationEditorProvider;
@@ -70,6 +72,8 @@ public class Jcr {
         with(new TypeEditorProvider());
         with(new RegistrationEditorProvider());
         with(new ConflictValidatorProvider());
+        with(new ReferenceEditorProvider());
+        with(new ReferenceIndexProvider());
 
         with(new PropertyIndexEditorProvider());
 

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java?rev=1543371&r1=1543370&r2=1543371&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/NodeImpl.java Tue Nov 19 10:41:23 2013
@@ -748,7 +748,7 @@ public class NodeImpl<T extends NodeDele
             public PropertyIterator perform() throws InvalidItemStateException {
                 IdentifierManager idManager = sessionDelegate.getIdManager();
 
-                Set<String> propertyOakPaths = idManager.getReferences(weak, node.getTree(), name); // TODO: oak name?
+                Iterable<String> propertyOakPaths = idManager.getReferences(weak, node.getTree(), name); // TODO: oak name?
                 Iterable<Property> properties = Iterables.transform(
                         propertyOakPaths,
                         new Function<String, Property>() {
@@ -760,7 +760,7 @@ public class NodeImpl<T extends NodeDele
                         }
                 );
 
-                return new PropertyIteratorAdapter(properties.iterator(), propertyOakPaths.size());
+                return new PropertyIteratorAdapter(properties.iterator());
             }
         });
     }