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 ca...@apache.org on 2019/03/18 00:38:01 UTC

svn commit: r1855723 - /jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/index/IndexDefinitionUpdater.java

Author: catholicon
Date: Mon Mar 18 00:38:01 2019
New Revision: 1855723

URL: http://svn.apache.org/viewvc?rev=1855723&view=rev
Log:
OAK-8137: Oak run tooling to run a RepositoryInitializer on top a list index definitions

Added:
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/index/IndexDefinitionUpdater.java   (with props)

Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/index/IndexDefinitionUpdater.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/index/IndexDefinitionUpdater.java?rev=1855723&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/index/IndexDefinitionUpdater.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/index/IndexDefinitionUpdater.java Mon Mar 18 00:38:01 2019
@@ -0,0 +1,295 @@
+/*
+ * 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.index;
+
+import com.google.common.base.Preconditions;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.apache.commons.io.FileUtils;
+import org.apache.felix.inventory.Format;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.plugins.index.IndexPathService;
+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.lifecycle.RepositoryInitializer;
+import org.apache.jackrabbit.oak.spi.state.*;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static java.util.Comparator.comparing;
+import static java.util.stream.StreamSupport.stream;
+import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.apache.jackrabbit.oak.api.Type.*;
+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.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
+
+public class IndexDefinitionUpdater {
+
+    private final NodeStore store;
+
+    private IndexDefinitionUpdater() {
+        store = new MemoryNodeStore();
+        // initialize as a JCR repo to register namespaces and create initial index structure (/oak:index for our use)
+        new Jcr(store).createRepository();
+    }
+
+    public static void main(String [] args) throws Exception {
+        OptionParser parser = new OptionParser();
+        parser.accepts("in", "input index definition file").withRequiredArg().required();
+        parser.accepts("out", "output index definition file").withRequiredArg();
+        parser.accepts("initializer", "implementation of RepositoryInitializer to update index definitions").withRequiredArg().required();
+
+        OptionSet optionSet = parser.parse(args);
+
+        Preconditions.checkArgument(optionSet.has("in"), "input index definition must be provided");
+        String inFilePath = optionSet.valueOf("in").toString();
+
+        Preconditions.checkArgument(optionSet.has("initializer"), "initializer class must be provided");
+        String initializerClassName = optionSet.valueOf("initializer").toString();
+        Class repoInitClazz = Class.forName(initializerClassName);
+        Object obj = repoInitClazz.newInstance();
+        Preconditions.checkArgument(obj instanceof RepositoryInitializer, repoInitClazz + " is not a RepositoryInitializer");
+
+        String outFilePath = optionSet.has("out") ? optionSet.valueOf("out").toString() : null;
+
+        new IndexDefinitionUpdater().process(inFilePath, outFilePath, (RepositoryInitializer)obj);
+    }
+
+    private void process(String inFilePath, String outFilePath, RepositoryInitializer initializer) throws Exception {
+        NodeState before = setupBaseState(inFilePath);
+        NodeState after = applyInitializer(before.builder(), initializer);
+
+        List<String> reindexingDefPaths = getReindexIndexPaths();
+        List<String> reindexingLuceneDefPaths = getLuceneIndexPaths(reindexingDefPaths);
+
+        if (outFilePath != null) {
+            writeReindexingLuceneDefs(outFilePath, reindexingLuceneDefPaths);
+        }
+        dumpIndexDiff(before, after, reindexingDefPaths);
+    }
+
+    private NodeState setupBaseState(String inFilePath) throws Exception {
+        org.apache.jackrabbit.oak.plugins.index.importer.IndexDefinitionUpdater indexDefinitionUpdater = new org.apache.jackrabbit.oak.plugins.index.importer.IndexDefinitionUpdater(new File(inFilePath));
+
+        NodeBuilder builder = store.getRoot().builder();
+        NodeBuilder oiBuilder = builder.getChildNode(INDEX_DEFINITIONS_NAME);
+
+        for (String indexPath : indexDefinitionUpdater.getIndexPaths()) {
+            oiBuilder.setChildNode(PathUtils.getName(indexPath), indexDefinitionUpdater.getIndexState(indexPath));
+        }
+
+        return merge(builder);
+    }
+
+    private NodeState applyInitializer(NodeBuilder builder, RepositoryInitializer initializer) throws CommitFailedException {
+        initializer.initialize(builder);
+        return merge(builder);
+    }
+
+    private void writeReindexingLuceneDefs(String outFilePath, List<String> reindexingLuceneDefPaths) throws IOException {
+        IndexPathService indexPathService = () -> reindexingLuceneDefPaths;
+
+        IndexDefinitionPrinter printer = new IndexDefinitionPrinter(store, indexPathService);
+
+        try (PrintWriter pw = new PrintWriter(
+                new BufferedOutputStream(
+                        FileUtils.openOutputStream(new File(outFilePath))))) {
+            printer.print(pw, Format.JSON, false);
+        }
+    }
+
+    private List<String> getReindexIndexPaths() {
+        return stream(store.getRoot().getChildNode(INDEX_DEFINITIONS_NAME).getChildNodeEntries().spliterator(), false)
+                .filter(cne -> !cne.getNodeState().hasProperty(REINDEX_PROPERTY_NAME) || cne.getNodeState().getBoolean(REINDEX_PROPERTY_NAME))
+                .filter(cne -> cne.getNodeState().hasProperty(TYPE_PROPERTY_NAME))
+                .sorted(comparing(cne -> cne.getNodeState().getString(TYPE_PROPERTY_NAME)))
+                .map(cne -> "/" + INDEX_DEFINITIONS_NAME + "/" + cne.getName()).collect(Collectors.toList())
+                ;
+    }
+
+    private List<String> getLuceneIndexPaths(List<String> indexDefPaths) {
+        NodeState root = store.getRoot();
+        return indexDefPaths.stream()
+                .filter(path -> "lucene".equals(NodeStateUtils.getNode(root, path).getString(TYPE_PROPERTY_NAME)))
+                .collect(Collectors.toList());
+    }
+
+    private NodeState merge(NodeBuilder builder) throws CommitFailedException {
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        return store.getRoot();
+    }
+
+    private void dumpIndexDiff(NodeState beforeRoot, NodeState afterRoot, List<String> reindexingLuceneDefPaths) {
+        try (PrintWriter pw = new PrintWriter(System.out)) {
+            for (String indexPath : reindexingLuceneDefPaths) {
+                NodeState before = NodeStateUtils.getNode(beforeRoot, indexPath);
+                NodeState after = NodeStateUtils.getNode(afterRoot, indexPath);
+
+                pw.println("");
+                pw.println("------ " + indexPath + " --------");
+                after.compareAgainstBaseState(before, new VisibleChangesDiff(pw));
+            }
+        }
+    }
+
+    static class VisibleChangesDiff implements NodeStateDiff {
+        private static final String INDENT = "  ";
+
+        private static final Function<Blob, String> BLOB_LENGTH = new Function<Blob, String>() {
+
+            @Override
+            public String apply(Blob b) {
+                return safeGetLength(b);
+            }
+
+            private String safeGetLength(Blob b) {
+                try {
+                    return byteCountToDisplaySize(b.length());
+                } catch (IllegalStateException e) {
+                    // missing BlobStore probably
+                }
+                return "[N/A]";
+            }
+
+        };
+
+        private final PrintWriter pw;
+        private final String indent;
+        private boolean isRoot;
+
+        VisibleChangesDiff(PrintWriter pw) {
+            this(pw, INDENT);
+            isRoot = true;
+        }
+
+        private VisibleChangesDiff(PrintWriter pw, String indent) {
+            this.pw = pw;
+            this.indent = indent;
+        }
+
+        @Override
+        public boolean propertyAdded(PropertyState after) {
+            if (!after.getName().startsWith(":")) {
+                pw.println(indent + "+ " + toString(after));
+            }
+            return true;
+        }
+
+        @Override
+        public boolean propertyChanged(PropertyState before, PropertyState after) {
+            if (before.getName().equals("reindex") && isRoot) {
+                // don't care if reindex changed on top level
+                return true;
+            }
+
+            if (before.getName().startsWith(":")) {
+                return propertyAdded(after);
+            } else if (after.getName().startsWith(":")) {
+                return propertyDeleted(before);
+            } else {
+                pw.println(indent + "^ " + before.getName());
+                pw.println(indent + "  - " + toString(before));
+                pw.println(indent + "  + " + toString(after));
+            }
+            return true;
+        }
+
+        @Override
+        public boolean propertyDeleted(PropertyState before) {
+            if (!before.getName().startsWith(":")) {
+                pw.println(indent + "- " + toString(before));
+            }
+            return true;
+        }
+
+        @Override
+        public boolean childNodeAdded(String name, NodeState after) {
+            if (name.startsWith(":")) {
+                return true;
+            }
+            pw.println(indent + "+ " + name);
+            return after.compareAgainstBaseState(EMPTY_NODE, new VisibleChangesDiff(
+                    pw, indent + INDENT));
+        }
+
+        @Override
+        public boolean childNodeChanged(String name, NodeState before, NodeState after) {
+            if (name.startsWith(":")) {
+                return true;
+            }
+
+            StringWriter sw = new StringWriter();
+            boolean ret;
+            try (PrintWriter subPw = new PrintWriter(sw)) {
+                ret = after.compareAgainstBaseState(before,
+                        new VisibleChangesDiff(subPw, indent + INDENT));
+            }
+
+            if (sw.getBuffer().length() > 0) {
+                pw.println(indent + "^ " + name);
+                pw.print(sw.toString());
+            }
+
+            return ret;
+        }
+
+        @Override
+        public boolean childNodeDeleted(String name, NodeState before) {
+            if (name.startsWith(":")) {
+                return true;
+            }
+            pw.println(indent + "- " + name);
+            return MISSING_NODE.compareAgainstBaseState(before, new VisibleChangesDiff(pw, indent + INDENT));
+        }
+
+        private static String toString(PropertyState ps) {
+            StringBuilder val = new StringBuilder();
+            val.append(ps.getName()).append("<").append(ps.getType()).append(">");
+            if (ps.getType() == BINARY) {
+                String v = BLOB_LENGTH.apply(ps.getValue(BINARY));
+                val.append(" = {").append(v).append("}");
+            } else if (ps.getType() == BINARIES) {
+                String v = stream(ps.getValue(BINARIES).spliterator(), false)
+                        .map(BLOB_LENGTH)
+                        .collect(Collectors.toList()).toString();
+                val.append("[").append(ps.count()).append("] = ").append(v);
+            } else if (ps.isArray()) {
+                val.append("[").append(ps.count()).append("] = ").append(ps.getValue(STRINGS));
+            } else {
+                val.append(" = ").append(ps.getValue(STRING));
+            }
+            return ps.getName() + "<" + ps.getType() + ">" + val.toString();
+        }
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/index/IndexDefinitionUpdater.java
------------------------------------------------------------------------------
    svn:eol-style = native