From d4d4ab8825ba09068ce3dfb39685974510910d89 Mon Sep 17 00:00:00 2001 From: Jukka Zitting Date: Mon, 15 Apr 2013 12:41:27 +0300 Subject: [PATCH 1/2] OAK-734: Refactor indexing code to use Editors Introduce an IndexEditor class that delegates indexing tasks to separate index update editors provided by implementations of the modified IndexHookProvider (to be renamed) interface. Note that this patch is incomplete, it doesn't yet adapt the existing index implementations to use this new mechanims. The intention is to outline the key IndexEditor class without complicating the patch with lots of other refactoring. --- .../jackrabbit/oak/plugins/index/IndexEditor.java | 200 +++++++++++++++++++++ .../oak/plugins/index/IndexEditorProvider.java | 43 +++++ .../oak/plugins/index/IndexHookProvider.java | 24 +-- 3 files changed, 251 insertions(+), 16 deletions(-) create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditor.java create mode 100644 oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditor.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditor.java new file mode 100644 index 0000000..278fc5c --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditor.java @@ -0,0 +1,200 @@ +/* + * 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; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Lists.newArrayListWithCapacity; +import static org.apache.jackrabbit.oak.api.Type.BOOLEAN; +import static org.apache.jackrabbit.oak.api.Type.STRING; +import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ASYNC_PROPERTY_NAME; +import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME; +import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME; +import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME; +import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE; + +import java.util.List; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.spi.commit.CompositeEditor; +import org.apache.jackrabbit.oak.spi.commit.Editor; +import org.apache.jackrabbit.oak.spi.commit.EditorDiff; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; + +class IndexEditor implements Editor { + + private final IndexHookProvider provider; + + private final boolean async; + + private final NodeState root; + + private final NodeBuilder builder; + + private final List editors = newArrayList(); + + IndexEditor( + IndexHookProvider provider, boolean async, + NodeState root, NodeBuilder builder) { + this.provider = checkNotNull(provider); + this.async = async; + this.root = checkNotNull(root); + this.builder = checkNotNull(builder); + } + + private IndexEditor(IndexEditor parent, String name) { + checkNotNull(parent); + this.provider = parent.provider; + this.async = parent.async; + this.root = parent.root; + this.builder = parent.builder.child(checkNotNull(name)); + } + + private static String getString(NodeBuilder builder, String name) { + PropertyState property = builder.getProperty(name); + if (property != null && property.getType() == STRING) { + return property.getValue(STRING); + } else { + return null; + } + } + + private static boolean getBoolean(NodeBuilder builder, String name) { + PropertyState property = builder.getProperty(name); + return property != null + && property.getType() == BOOLEAN + && property.getValue(BOOLEAN); + } + + @Override + public void enter(NodeState before, NodeState after) + throws CommitFailedException { + List reindex = newArrayList(); + + if (builder.hasChildNode(INDEX_DEFINITIONS_NAME)) { + NodeBuilder definitions = builder.child(INDEX_DEFINITIONS_NAME); + for (String name : definitions.getChildNodeNames()) { + NodeBuilder definition = definitions.child(name); + if (async == getBoolean(definition, ASYNC_PROPERTY_NAME)) { + String type = getString(definition, TYPE_PROPERTY_NAME); + Editor editor = provider.getIndexHook(type, definition); + if (editor == null) { + // trigger reindexing when an indexer becomes available + definition.setProperty(REINDEX_PROPERTY_NAME, true); + } else if (getBoolean(definition, REINDEX_PROPERTY_NAME)) { + definition.setProperty(REINDEX_PROPERTY_NAME, false); + definition.removeNode(":index"); + reindex.add(editor); + } else { + editors.add(editor); + } + } + } + } + + // no-op when reindex is empty + CommitFailedException exception = EditorDiff.process( + CompositeEditor.compose(reindex), MISSING_NODE, after); + if (exception != null) { + throw exception; + } + + for (Editor editor : editors) { + editor.enter(before, after); + } + } + + @Override + public void leave(NodeState before, NodeState after) + throws CommitFailedException { + for (Editor editor : editors) { + editor.leave(before, after); + } + } + + @Override + public void propertyAdded(PropertyState after) + throws CommitFailedException { + for (Editor editor : editors) { + editor.propertyAdded(after); + } + } + + @Override + public void propertyChanged(PropertyState before, PropertyState after) + throws CommitFailedException { + for (Editor editor : editors) { + editor.propertyChanged(before, after); + } + } + + @Override + public void propertyDeleted(PropertyState before) + throws CommitFailedException { + for (Editor editor : editors) { + editor.propertyDeleted(before); + } + } + + @Override @Nonnull + public Editor childNodeAdded(String name, NodeState after) + throws CommitFailedException { + List children = newArrayListWithCapacity(1 + editors.size()); + children.add(new IndexEditor(this, name)); + for (Editor editor : editors) { + Editor child = editor.childNodeAdded(name, after); + if (child != null) { + children.add(child); + } + } + return CompositeEditor.compose(children); + } + + @Override @Nonnull + public Editor childNodeChanged( + String name, NodeState before, NodeState after) + throws CommitFailedException { + List children = newArrayListWithCapacity(1 + editors.size()); + children.add(new IndexEditor(this, name)); + for (Editor editor : editors) { + Editor child = editor.childNodeChanged(name, before, after); + if (child != null) { + children.add(child); + } + } + return CompositeEditor.compose(children); + } + + @Override @CheckForNull + public Editor childNodeDeleted(String name, NodeState before) + throws CommitFailedException { + List children = newArrayListWithCapacity(editors.size()); + for (Editor editor : editors) { + Editor child = editor.childNodeDeleted(name, before); + if (child != null) { + children.add(child); + } + } + return CompositeEditor.compose(children); + } + +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java new file mode 100644 index 0000000..f5191e6 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexEditorProvider.java @@ -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; + +import javax.annotation.CheckForNull; + +import org.apache.jackrabbit.oak.spi.commit.Editor; +import org.apache.jackrabbit.oak.spi.commit.EditorProvider; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; + +public class IndexEditorProvider implements EditorProvider { + + private final IndexHookProvider provider; + + private final boolean async; + + public IndexEditorProvider(IndexHookProvider provider, boolean async) { + this.provider = provider; + this.async = async; + } + + @Override @CheckForNull + public Editor getRootEditor( + NodeState before, NodeState after, NodeBuilder builder) { + return new IndexEditor(provider, async, after, builder); + } + +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java index adc7296..223c485 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/IndexHookProvider.java @@ -16,40 +16,32 @@ */ package org.apache.jackrabbit.oak.plugins.index; -import java.util.List; - import javax.annotation.Nonnull; +import org.apache.jackrabbit.oak.spi.commit.Editor; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; /** * Extension point for plugging in different kinds of IndexHook providers. - * - * @see IndexHook */ public interface IndexHookProvider { /** - * * Each provider knows how to produce a certain type of index. If the * type param is of an unknown value, the provider is expected - * to return an empty list. - * + * to return {@code null}. *

- * The builder must point to the repository content node, not - * the index content node. Each IndexHook implementation will - * have to drill down to its specific index content, and possibly deal with - * multiple indexes of the same type. - *

+ * The builder must points to the index definition node + * under which the indexer is expected to store the index content. * * @param type * the index type * @param builder - * the node state builder of the content node that will be used - * for updates - * @return a list of index hooks of the given type + * the node state builder of the index definition node that + * will be used for updates + * @return index update editor, or {@code null} if type is unknown */ @Nonnull - List getIndexHooks(String type, NodeBuilder builder); + Editor getIndexHook(String type, NodeBuilder builder); } -- 1.8.0.msysgit.0