blob: 2292392b4fbf4c3d913ca84b513fb475f06e348a [file] [log] [blame]
/*
* 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.Collections;
import java.util.List;
import javax.jcr.PropertyType;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
import org.apache.jackrabbit.oak.query.NodeStateNodeTypeInfoProvider;
import org.apache.jackrabbit.oak.query.QueryEngineSettings;
import org.apache.jackrabbit.oak.query.ast.NodeTypeInfo;
import org.apache.jackrabbit.oak.query.ast.NodeTypeInfoProvider;
import org.apache.jackrabbit.oak.query.ast.Operator;
import org.apache.jackrabbit.oak.query.ast.SelectorImpl;
import org.apache.jackrabbit.oak.query.index.FilterImpl;
import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
import org.apache.jackrabbit.oak.spi.commit.EditorHook;
import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
import org.apache.jackrabbit.oak.spi.mount.Mounts;
import org.apache.jackrabbit.oak.spi.query.Cursor;
import org.apache.jackrabbit.oak.spi.query.Filter;
import org.apache.jackrabbit.oak.spi.query.QueryIndex;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static com.google.common.collect.ImmutableList.of;
import static com.google.common.collect.Lists.newArrayList;
import static org.apache.jackrabbit.JcrConstants.JCR_UUID;
import static org.apache.jackrabbit.JcrConstants.NT_BASE;
import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
import static org.apache.jackrabbit.oak.InitialContentHelper.INITIAL_CONTENT;
import static org.apache.jackrabbit.oak.plugins.memory.PropertyValues.newReference;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
public class ReferenceIndexTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void basicReferenceHandling() throws Exception{
NodeState root = INITIAL_CONTENT;
NodeBuilder builder = root.builder();
NodeState before = builder.getNodeState();
builder.child("a").setProperty(createProperty("foo", "u1", Type.REFERENCE));
builder.child("b").setProperty(createProperty("foo", "u1", Type.REFERENCE));
builder.child("c").setProperty(createProperty("foo", "u2", Type.WEAKREFERENCE));
NodeState after = builder.getNodeState();
EditorHook hook = new EditorHook(
new IndexUpdateProvider(new ReferenceEditorProvider()));
NodeState indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
FilterImpl f = createFilter(indexed, NT_BASE);
f.restrictProperty("*", Operator.EQUAL, newReference("u1"), PropertyType.REFERENCE);
assertFilter(f, new ReferenceIndex(), indexed, of("/a", "/b"));
FilterImpl f2 = createFilter(indexed, NT_BASE);
f2.restrictProperty("*", Operator.EQUAL, newReference("u2"), PropertyType.WEAKREFERENCE);
assertFilter(f2, new ReferenceIndex(), indexed, of("/c"));
}
@Test
public void referenceHandlingWithMounts() throws Exception{
NodeState root = INITIAL_CONTENT;
NodeBuilder builder = root.builder();
NodeState before = builder.getNodeState();
builder.child("a").child("x").setProperty(createProperty("foo", "u1", Type.REFERENCE));
builder.child("b").setProperty(createProperty("foo", "u1", Type.REFERENCE));
builder.child("c").setProperty(createProperty("foo", "u1", Type.WEAKREFERENCE));
builder.child("d").setProperty(createProperty("foo", "u2", Type.WEAKREFERENCE));
builder.child("a").child("y").setProperty(createProperty("foo", "u1", Type.WEAKREFERENCE));
NodeState after = builder.getNodeState();
MountInfoProvider mip = Mounts.newBuilder()
.mount("foo", "/a")
.build();
EditorHook hook = new EditorHook(
new IndexUpdateProvider(new ReferenceEditorProvider().with(mip)));
ReferenceIndex referenceIndex = new ReferenceIndex(mip);
NodeState indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
FilterImpl f = createFilter(indexed, NT_BASE);
f.restrictProperty("*", Operator.EQUAL, newReference("u1"), PropertyType.REFERENCE);
// System.out.println(NodeStateUtils.toString(NodeStateUtils.getNode(indexed, "/oak:index/reference")));
assertFilter(f, referenceIndex, indexed, of("/a/x", "/b"));
FilterImpl f2 = createFilter(indexed, NT_BASE);
f2.restrictProperty("*", Operator.EQUAL, newReference("u1"), PropertyType.WEAKREFERENCE);
assertFilter(f2, referenceIndex, indexed, of("/c", "/a/y"));
}
@Test
public void removeReferencedNode() throws Exception{
NodeState root = INITIAL_CONTENT;
NodeBuilder builder = root.builder();
NodeState before = builder.getNodeState();
builder.child("a").setProperty(createProperty(JCR_UUID, "u1", Type.STRING));
builder.child("b").setProperty(createProperty("foo", "u1", Type.REFERENCE));
NodeState after = builder.getNodeState();
EditorHook hook = new EditorHook(
new IndexUpdateProvider(new ReferenceEditorProvider()));
NodeState indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
builder = indexed.builder();
builder.getChildNode("a").remove();
thrown.expect(CommitFailedException.class);
thrown.expectMessage("OakIntegrity0001: Unable to delete referenced node: u1");
hook.processCommit(indexed, builder.getNodeState(), CommitInfo.EMPTY);
}
@Test
public void removeWeaklyReferencedNode() throws Exception{
NodeState root = INITIAL_CONTENT;
NodeBuilder builder = root.builder();
NodeState before = builder.getNodeState();
builder.child("a").setProperty(createProperty(JCR_UUID, "u1", Type.STRING));
builder.child("b").setProperty(createProperty("foo", "u1", Type.WEAKREFERENCE));
NodeState after = builder.getNodeState();
EditorHook hook = new EditorHook(
new IndexUpdateProvider(new ReferenceEditorProvider()));
NodeState indexed = hook.processCommit(before, after, CommitInfo.EMPTY);
builder = indexed.builder();
builder.getChildNode("a").remove();
hook.processCommit(indexed, builder.getNodeState(), CommitInfo.EMPTY);
}
@SuppressWarnings("Duplicates")
private static FilterImpl createFilter(NodeState root, String nodeTypeName) {
NodeTypeInfoProvider nodeTypes = new NodeStateNodeTypeInfoProvider(root);
NodeTypeInfo type = nodeTypes.getNodeTypeInfo(nodeTypeName);
SelectorImpl selector = new SelectorImpl(type, nodeTypeName);
return new FilterImpl(selector, "SELECT * FROM [" + nodeTypeName + "]", new QueryEngineSettings());
}
private static List<String> assertFilter(Filter filter, QueryIndex queryIndex,
NodeState indexed, List<String> expected) {
Cursor cursor = queryIndex.query(filter, indexed);
List<String> paths = newArrayList();
while (cursor.hasNext()) {
paths.add(cursor.next().getPath());
}
Collections.sort(paths);
for (String p : expected) {
assertTrue("Expected path " + p + " not found", paths.contains(p));
}
assertEquals("Result set size is different \nExpected: " +
expected + "\nActual: " + paths, expected.size(), paths.size());
return paths;
}
}