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