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 ch...@apache.org on 2017/07/25 06:59:32 UTC
svn commit: r1802888 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdater.java
test/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdaterTest.java
Author: chetanm
Date: Tue Jul 25 06:59:31 2017
New Revision: 1802888
URL: http://svn.apache.org/viewvc?rev=1802888&view=rev
Log:
OAK-6471 - Support adding or updating index definitions via oak-run
IndexDefinitionUpdater enables updating existing index definitions
from provided json file
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdater.java (with props)
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdaterTest.java (with props)
Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdater.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdater.java?rev=1802888&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdater.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdater.java Tue Jul 25 06:59:31 2017
@@ -0,0 +1,139 @@
+/*
+ * 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.importer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Maps;
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.commons.json.JsopReader;
+import org.apache.jackrabbit.oak.commons.json.JsopTokenizer;
+import org.apache.jackrabbit.oak.json.Base64BlobSerializer;
+import org.apache.jackrabbit.oak.json.JsonDeserializer;
+import org.apache.jackrabbit.oak.json.JsopDiff;
+import org.apache.jackrabbit.oak.spi.state.ApplyDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static org.apache.jackrabbit.oak.plugins.index.importer.NodeStoreUtils.childBuilder;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+
+public class IndexDefinitionUpdater {
+ /**
+ * Name of file which would be check for presence of index-definitions
+ */
+ public static final String INDEX_DEFINITIONS_JSON = "index-definitions.json";
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final File file;
+
+ public IndexDefinitionUpdater(File file) {
+ checkArgument(file.exists() && file.canRead(), "File [%s] cannot be read", file);
+ this.file = file;
+ }
+
+ public void apply(NodeState root, NodeBuilder builder) throws IOException, CommitFailedException {
+ for (Map.Entry<String, NodeState> cne : getIndexDefnStates().entrySet()) {
+ String indexPath = cne.getKey();
+
+ if (!indexPath.startsWith("/")) {
+ String msg = String.format("Invalid format of index definitions. The key name [%s] should " +
+ "be index path ", indexPath);
+ throw new IllegalArgumentException(msg);
+ }
+
+ String indexNodeName = PathUtils.getName(indexPath);
+
+ NodeState newDefinition = cne.getValue();
+ String parentPath = PathUtils.getParentPath(indexPath);
+ NodeState parent = NodeStateUtils.getNode(root, parentPath);
+
+ checkState(parent.exists(), "Parent node at path [%s] not found while " +
+ "adding new index definition for [%s]. Intermediate paths node must exist for new index " +
+ "nodes to be created", parentPath,indexPath);
+
+ NodeState existing = parent.getChildNode(indexNodeName);
+
+
+ if (!existing.exists()) {
+ log.info("Adding new index definition at path [{}]", indexPath);
+ } else {
+ log.info("Updating index definition at path [{}]. Changes are ", indexPath);
+ String diff = JsopDiff.diffToJsop(cloneVisibleState(existing), cloneVisibleState(newDefinition));
+ log.info(diff);
+ }
+
+
+ NodeBuilder indexBuilder = childBuilder(builder, parentPath);
+ indexBuilder.setChildNode(indexNodeName, newDefinition);
+ }
+ }
+
+ private Map<String, NodeState> getIndexDefnStates() throws IOException {
+ String json = FileUtils.readFileToString(file, Charsets.UTF_8);
+ Base64BlobSerializer blobHandler = new Base64BlobSerializer();
+ Map<String, NodeState> indexDefns = Maps.newHashMap();
+ JsopReader reader = new JsopTokenizer(json);
+ reader.read('{');
+ if (!reader.matches('}')) {
+ do {
+ String indexPath = reader.readString();
+ reader.read(':');
+ if (reader.matches('{')) {
+ JsonDeserializer deserializer = new JsonDeserializer(blobHandler);
+ NodeState idxState = deserializer.deserialize(reader);
+ indexDefns.put(indexPath, idxState);
+ }
+ } while (reader.matches(','));
+ reader.read('}');
+ }
+ return indexDefns;
+ }
+
+ private static NodeState cloneVisibleState(NodeState state){
+ NodeBuilder builder = EMPTY_NODE.builder();
+ new ApplyVisibleDiff(builder).apply(state);
+ return builder.getNodeState();
+ }
+
+ private static class ApplyVisibleDiff extends ApplyDiff {
+ public ApplyVisibleDiff(NodeBuilder builder) {
+ super(builder);
+ }
+
+ @Override
+ public boolean childNodeAdded(String name, NodeState after) {
+ if (NodeStateUtils.isHidden(name)){
+ return true;
+ }
+ return after.compareAgainstBaseState(
+ EMPTY_NODE, new ApplyVisibleDiff(builder.child(name)));
+ }
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdater.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdaterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdaterTest.java?rev=1802888&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdaterTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdaterTest.java Tue Jul 25 06:59:31 2017
@@ -0,0 +1,144 @@
+/*
+ * 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.importer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+import org.apache.felix.inventory.Format;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.plugins.index.inventory.IndexDefinitionPrinter;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.json.simple.JSONObject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static com.google.common.base.Charsets.*;
+import static java.util.Arrays.asList;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.junit.Assert.*;
+
+public class IndexDefinitionUpdaterTest {
+
+ @Rule
+ public TemporaryFolder folder = new TemporaryFolder(new File("target"));
+
+ private NodeStore store = new MemoryNodeStore();
+
+ @Test
+ public void update() throws Exception{
+ NodeBuilder builder = store.getRoot().builder();
+ builder.child("oak:index").child("fooIndex").setProperty("foo", "bar");
+ builder.child("oak:index").child("fooIndex").child("a").setProperty("foo2", 2);
+
+ store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ String json = createIndexDefn();
+ applyJson(json);
+
+ NodeState root = store.getRoot();
+
+ assertTrue(NodeStateUtils.getNode(root,"/oak:index/fooIndex").exists());
+ assertTrue(NodeStateUtils.getNode(root,"/oak:index/fooIndex/b").exists());
+ assertFalse(NodeStateUtils.getNode(root,"/oak:index/fooIndex/a").exists());
+
+ assertTrue(NodeStateUtils.getNode(root,"/oak:index/barIndex").exists());
+ }
+
+ @Test (expected = IllegalStateException.class)
+ public void updateNonExistingParent() throws Exception{
+ NodeBuilder builder = store.getRoot().builder();
+ builder.child("oak:index").child("fooIndex").setProperty("foo", "bar");
+
+ store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ NodeBuilder builder2 = EMPTY_NODE.builder();
+ builder2.child("a").child("oak:index").child("barIndex").setProperty("foo", "bar");
+ String json = createIndexDefn(builder2.getNodeState(), "/oak:index/fooIndex", "/a/oak:index/barIndex");
+ applyJson(json);
+ }
+
+ @Test (expected = IllegalArgumentException.class)
+ public void invalidJson() throws Exception{
+ Map<String, Object> map = new HashMap<>();
+ map.put("a", ImmutableMap.of("a2", "b2"));
+ String json = JSONObject.toJSONString(map);
+ applyJson(json);
+
+ }
+
+ private void applyJson(String json) throws IOException, CommitFailedException {
+ NodeBuilder builder;File file = folder.newFile();
+ Files.write(json, file, UTF_8);
+
+ IndexDefinitionUpdater update = new IndexDefinitionUpdater(file);
+
+ builder = store.getRoot().builder();
+ update.apply(store.getRoot(), builder);
+ store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+ }
+
+ private String createIndexDefn() throws CommitFailedException {
+ NodeBuilder builder = EMPTY_NODE.builder();
+ builder.child("oak:index").child("fooIndex").setProperty("foo", "bar");
+ builder.child("oak:index").child("fooIndex").setProperty("foo3", "bar3");
+ builder.child("oak:index").child("fooIndex").child("b").setProperty("foo4", 4);
+
+ builder.child("oak:index").child("barIndex").setProperty("foo", "bar");
+ builder.child("oak:index").child("barIndex").child("c").setProperty("foo5", 5);
+ return createIndexDefn(builder.getNodeState(), "/oak:index/fooIndex", "/oak:index/barIndex");
+ }
+
+ private String createIndexDefn(NodeState nodeState, String... indexPaths) throws CommitFailedException {
+ NodeStore store = new MemoryNodeStore();
+ NodeBuilder builder = store.getRoot().builder();
+ for (ChildNodeEntry cne : nodeState.getChildNodeEntries()) {
+ builder.setChildNode(cne.getName(), cne.getNodeState());
+ }
+
+ store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ IndexDefinitionPrinter printer = new IndexDefinitionPrinter(store,
+ () -> asList(indexPaths));
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ printer.print(pw, Format.JSON, false);
+
+ pw.flush();
+
+ return sw.toString();
+ }
+
+}
\ No newline at end of file
Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/importer/IndexDefinitionUpdaterTest.java
------------------------------------------------------------------------------
svn:eol-style = native