Author: thomasm
Date: Wed Jan 25 15:46:25 2017
New Revision: 1780222

URL: http://svn.apache.org/viewvc?rev=1780222&view=rev
Log:
OAK-5324 Enable property index reindexing via oak-run

Added:
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreOpener.java
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/IndexCommand.java
    
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/IndexTest.java
Modified:
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreFixture.java
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java

Modified: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreFixture.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreFixture.java?rev=1780222&r1=1780221&r2=1780222&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreFixture.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreFixture.java
 Wed Jan 25 15:46:25 2017
@@ -21,7 +21,7 @@ import java.io.Closeable;
 
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 
-interface NodeStoreFixture extends Closeable {
+public interface NodeStoreFixture extends Closeable {
 
     NodeStore getStore();
 

Added: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreOpener.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreOpener.java?rev=1780222&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreOpener.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/NodeStoreOpener.java
 Wed Jan 25 15:46:25 2017
@@ -0,0 +1,262 @@
+/*
+ * 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.console;
+
+import static java.util.Arrays.asList;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.sql.DataSource;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+import org.apache.jackrabbit.core.data.FileDataStore;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory;
+import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
+import org.apache.jackrabbit.oak.plugins.index.lucene.IndexTracker;
+import 
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexProvider;
+import org.apache.jackrabbit.oak.plugins.index.lucene.hybrid.DocumentQueue;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+
+import com.google.common.util.concurrent.MoreExecutors;
+import com.mongodb.MongoClientURI;
+import com.mongodb.MongoURI;
+
+/**
+ * A tool to open a node store from command line options
+ */
+public class NodeStoreOpener {
+    
+    private static final long MB = 1024 * 1024;    
+    
+    public static NodeStoreFixture open(OptionParser parser, boolean 
writeMode, String... args) throws Exception {
+        OptionSpec<Integer> clusterId = parser.accepts("clusterId", "MongoMK 
clusterId")
+                .withRequiredArg().ofType(Integer.class).defaultsTo(0);
+        OptionSpec<Void> readWriteOption = parser.accepts("read-write", 
"connect to repository in read-write mode");
+        OptionSpec<String> fdsPathSpec = parser.accepts("fds-path", "Path to 
FDS store").withOptionalArg().defaultsTo("");
+        OptionSpec<Void> segment = parser.accepts("segment", "Use oak-segment 
instead of oak-segment-tar");
+        OptionSpec<Void> help = parser.acceptsAll(asList("h", "?", "help"), 
"show help").forHelp();
+    
+        // RDB specific options
+        OptionSpec<String> rdbjdbcuser = parser.accepts("rdbjdbcuser", "RDB 
JDBC user").withOptionalArg().defaultsTo("");
+        OptionSpec<String> rdbjdbcpasswd = parser.accepts("rdbjdbcpasswd", 
"RDB JDBC password").withOptionalArg().defaultsTo("");
+    
+        OptionSpec<String> nonOption = 
parser.nonOptions("{<path-to-repository> | <mongodb-uri>}");
+        OptionSpec<Void> disableBranchesSpec = parser.
+                accepts("disableBranches", "disable branches");    
+        OptionSpec<Integer> cacheSizeSpec = parser.
+                accepts("cacheSize", "cache size").withRequiredArg().
+                ofType(Integer.class).defaultsTo(0);         
+    
+        OptionSet options = parser.parse(args);
+        List<String> nonOptions = nonOption.values(options);
+    
+        if (options.has(help)) {
+            parser.printHelpOn(System.out);
+            System.exit(0);
+        }
+    
+        if (nonOptions.isEmpty()) {
+            return new NodeStoreFixture() {
+                @Override
+                public void close() throws IOException {
+                    // ignore
+                }
+                @Override
+                public NodeStore getStore() {
+                    return null;
+                }
+            };
+        }
+    
+        BlobStore blobStore = null;
+        String fdsPath = fdsPathSpec.value(options);
+        if (!"".equals(fdsPath)) {
+            File fdsDir = new File(fdsPath);
+            if (fdsDir.exists()) {
+                FileDataStore fds = new FileDataStore();
+                fds.setPath(fdsDir.getAbsolutePath());
+                fds.init(null);
+    
+                blobStore = new DataStoreBlobStore(fds);
+            }
+        }
+        
+        boolean readOnly = !writeMode && !options.has(readWriteOption);
+    
+        NodeStoreFixture fixture;
+        String nodeStore = nonOptions.get(0);
+        if (nodeStore.startsWith(MongoURI.MONGODB_PREFIX)) {
+            MongoClientURI uri = new MongoClientURI(nodeStore);
+            if (uri.getDatabase() == null) {
+                System.err.println("Database missing in MongoDB URI: " + 
uri.getURI());
+                System.exit(1);
+            }
+            MongoConnection mongo = new MongoConnection(uri.getURI());
+    
+            DocumentMK.Builder builder = new DocumentMK.Builder()
+                    .setBlobStore(blobStore)
+                    .setMongoDB(mongo.getDB()).
+                    setClusterId(clusterId.value(options));
+            if (readOnly) {
+                builder.setReadOnlyMode();
+            }
+            DocumentNodeStore store = builder.getNodeStore();
+            if (options.has(disableBranchesSpec)) {
+                builder.disableBranches();
+            }
+            int cacheSize = cacheSizeSpec.value(options);
+            if (cacheSize != 0) {
+                builder.memoryCacheSize(cacheSize * MB);
+            }            
+            fixture = new MongoFixture(store);
+        } else if (nodeStore.startsWith("jdbc")) {
+            DataSource ds = RDBDataSourceFactory.forJdbcUrl(nodeStore, 
rdbjdbcuser.value(options),
+                    rdbjdbcpasswd.value(options));
+            DocumentMK.Builder builder = new DocumentMK.Builder()
+                    .setBlobStore(blobStore)
+                    .setRDBConnection(ds).
+                    setClusterId(clusterId.value(options));
+            if (readOnly) {
+                builder.setReadOnlyMode();
+            }
+            DocumentNodeStore store = builder.getNodeStore();
+            if (options.has(disableBranchesSpec)) {
+                builder.disableBranches();
+            }
+            int cacheSize = cacheSizeSpec.value(options);
+            if (cacheSize != 0) {
+                builder.memoryCacheSize(cacheSize * MB);
+            }            
+            fixture = new MongoFixture(store);
+        } else if (options.has(segment)) {
+            FileStore.Builder fsBuilder = FileStore.builder(new 
File(nodeStore))
+                    .withMaxFileSize(256).withDefaultMemoryMapping();
+            if (blobStore != null) {
+                fsBuilder.withBlobStore(blobStore);
+            }
+            FileStore store;
+            if (readOnly) {
+                store = fsBuilder.buildReadOnly();
+            } else {
+                store = fsBuilder.build();
+            }
+            fixture = new SegmentFixture(store);
+        } else {
+            fixture = SegmentTarFixture.create(new File(nodeStore), readOnly, 
blobStore);
+        }
+        return fixture;
+    }
+    
+    public static class MongoFixture implements NodeStoreFixture {
+        private final DocumentNodeStore nodeStore;
+
+        private MongoFixture(DocumentNodeStore nodeStore) {
+            this.nodeStore = nodeStore;
+        }
+
+        @Override
+        public NodeStore getStore() {
+            return nodeStore;
+        }
+
+        @Override
+        public void close() throws IOException {
+            nodeStore.dispose();
+        }
+    }
+
+    @Deprecated
+    public static class SegmentFixture implements NodeStoreFixture {
+        private final SegmentStore segmentStore;
+        private final SegmentNodeStore nodeStore;
+
+        private SegmentFixture(SegmentStore segmentStore) {
+            this.segmentStore = segmentStore;
+            this.nodeStore = SegmentNodeStore.builder(segmentStore).build();
+        }
+
+        @Override
+        public NodeStore getStore() {
+            return nodeStore;
+        }
+
+        @Override
+        public void close() throws IOException {
+            segmentStore.close();
+        }
+    }
+
+    public static Session openSession(NodeStore nodeStore) throws 
RepositoryException {
+        if (nodeStore == null) {
+            return null;
+        }
+        StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
+        Oak oak = new 
Oak(nodeStore).with(ManagementFactory.getPlatformMBeanServer());
+        oak.getWhiteboard().register(StatisticsProvider.class, 
statisticsProvider, Collections.emptyMap());
+        LuceneIndexProvider provider = 
NodeStoreOpener.createLuceneIndexProvider();
+        oak.with((QueryIndexProvider) provider)
+                .with((Observer) provider)
+                .with(NodeStoreOpener.createLuceneIndexEditorProvider());
+        Jcr jcr = new Jcr(oak);
+        Repository repository = jcr.createRepository();
+        return repository.login(new SimpleCredentials("admin", 
"admin".toCharArray()));
+    }
+
+    private static LuceneIndexEditorProvider createLuceneIndexEditorProvider() 
{
+        LuceneIndexEditorProvider ep = new LuceneIndexEditorProvider();
+        ScheduledExecutorService executorService = 
MoreExecutors.getExitingScheduledExecutorService(
+                (ScheduledThreadPoolExecutor) 
Executors.newScheduledThreadPool(5));
+        StatisticsProvider statsProvider = StatisticsProvider.NOOP;
+        int queueSize = Integer.getInteger("queueSize", 1000);
+        IndexTracker tracker = new IndexTracker();
+        DocumentQueue queue = new DocumentQueue(queueSize, tracker, 
executorService, statsProvider);
+        ep.setIndexingQueue(queue);
+        return ep;
+    }
+
+    private static LuceneIndexProvider createLuceneIndexProvider() {
+        return new LuceneIndexProvider();
+    }
+    
+}

Added: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/IndexCommand.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/IndexCommand.java?rev=1780222&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/IndexCommand.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/IndexCommand.java
 Wed Jan 25 15:46:25 2017
@@ -0,0 +1,460 @@
+/*
+ * 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.run;
+
+import static java.util.Arrays.asList;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.commons.json.JsonObject;
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
+import org.apache.jackrabbit.oak.commons.json.JsopReader;
+import org.apache.jackrabbit.oak.commons.json.JsopTokenizer;
+import org.apache.jackrabbit.oak.console.NodeStoreFixture;
+import org.apache.jackrabbit.oak.console.NodeStoreOpener;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+public class IndexCommand implements Command {
+
+    PrintStream output = System.out;
+    Session session;
+    private boolean interactive;
+    private final Map<String, Object> data = new HashMap<String, Object>();
+
+    @Override
+    public void execute(String... args) throws Exception {
+        OptionParser parser = new OptionParser();
+        OptionSpec<String> scriptOption = parser
+                .accepts("script", "Path to Script").withOptionalArg()
+                .defaultsTo("");
+        OptionSpec<?> helpSpec = parser.acceptsAll(
+                asList("h", "?", "help"), "show help").forHelp();
+        System.out.println("Opening nodestore...");
+        NodeStoreFixture nodeStoreFixture = NodeStoreOpener.open(parser, true, 
args);
+        OptionSet options = parser.parse(args);
+        if (options.has(helpSpec)
+                || options.nonOptionArguments().isEmpty()) {
+            System.out.println("Mode: " + Mode.INDEX);
+            System.out.println();
+            parser.printHelpOn(System.out);
+            return;
+        }
+        NodeStore nodeStore = nodeStoreFixture.getStore();
+        String script = scriptOption.value(options);
+        LineNumberReader reader = openScriptReader(script);
+        try {
+            process(nodeStore, reader);
+        } finally {
+            nodeStoreFixture.close();
+            reader.close();
+        }
+    }
+
+    private LineNumberReader openScriptReader(String script)
+            throws IOException {
+        Reader reader;
+        if ("-".equals(script)) {
+            reader = new InputStreamReader(System.in);
+            interactive = true;
+        } else {
+            reader = new FileReader(script);
+        }
+        return new LineNumberReader(new BufferedReader(reader));
+    }
+
+    public void process(NodeStore nodeStore, LineNumberReader reader)
+            throws Exception {
+        session = NodeStoreOpener.openSession(nodeStore);
+        System.out.println("Nodestore is open");
+        if (interactive) {
+            System.out.println("Type \"exit\" to quit");
+        }
+        while (true) {
+            try {
+                String json = readJson(reader);
+                if (json == null || json.trim().equals("exit")) {
+                    break;
+                }
+                execute(json);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        if (session != null) {
+            session.logout();
+        }
+    }
+
+    private static String readJson(LineNumberReader reader) throws IOException 
{
+        StringBuilder buff = new StringBuilder();
+        int level = 0;
+        while (true) {
+            String line = reader.readLine();
+            if (line == null) {
+                return null;
+            } else if (line.trim().startsWith("//")) {
+                continue;
+            }
+            buff.append(line).append('\n');
+            for (int i = 0; i < line.length(); i++) {
+                char c = line.charAt(i);
+                if (c == '\"') {
+                    while (true) {
+                        c = line.charAt(++i);
+                        if (c == '\"') {
+                            break;
+                        } else if (c == '\\') {
+                            ++i;
+                        }
+                    }
+                } else if (c == '{') {
+                    level++;
+                } else if (c == '}') {
+                    level--;
+                }
+            }
+            if (level == 0 && !buff.toString().trim().isEmpty()) {
+                return buff.toString();
+            }
+        }
+    }
+    
+    void execute(String command) throws RepositoryException {
+        JsopTokenizer t = new JsopTokenizer(command);
+        t.read('{');
+        JsonObject json = JsonObject.create(t);
+        Map<String, String> properties = json.getProperties();
+        if (properties.containsKey("if")) {
+            Object value = getValueOrVariable(properties.get("if"));
+            Object equals = getValueOrVariable(properties.get("="));
+            if (value == null) {
+                if (equals != null) {
+                    return;
+                }
+            } else if (!value.equals(equals)) {
+                return;
+            }
+        }
+        for (Entry<String, String> e : properties.entrySet()) {
+            String k = e.getKey();
+            Object value = getValueOrVariable(e.getValue());
+            if ("addNode".equals(k)) {
+                String nodePath = value.toString();
+                String parent = PathUtils.getParentPath(nodePath);
+                if (session.nodeExists(parent)) {
+                    Node p = session.getNode(parent);
+                    String nodeName = PathUtils.getName(nodePath);
+                    if (!p.hasNode(nodeName)) {
+                        JsonObject node = json.getChildren().get("node");
+                        addNode(p, nodeName, node);
+                    }
+                }
+            } else if ("removeNode".equals(k)) {
+                String path = value.toString();
+                if (session.nodeExists(path)) {
+                    session.getNode(path).remove();
+                }
+            } else if ("setProperty".equals(k)) {
+                String itemPath = value.toString();
+                String nodePath = PathUtils.getParentPath(itemPath);
+                if (session.nodeExists(nodePath)) {
+                    String propertyName = PathUtils.getName(itemPath);
+                    Object propertyValue = getValueOrVariable(properties
+                            .get("value"));
+                    setProperty(session.getNode(nodePath), propertyName,
+                            propertyValue);
+                }
+            } else if ("session".equals(k)) {
+                if ("save".equals(value)) {
+                    session.save();
+                }
+            } else if ("xpath".equals(k) || "sql".equals(k)) {
+                String language = "xpath".equals(k) ? k : Query.JCR_SQL2;
+                String columnName = "xpath".equals(k) ? "jcr:path" : null;
+                boolean quiet = properties.containsKey("quiet");
+                int depth = properties.containsKey("depth") ? Integer
+                        .parseInt(properties.get("depth")) : 0;
+                runQuery(value.toString(), language, columnName, quiet, depth);
+            } else if ("print".equals(k)) {
+                output.println(value);
+            } else if ("for".equals(k)) {
+                String name = JsopTokenizer.decodeQuoted(properties.get(k));
+                Object old = data.get(name);
+                String[] commands = (String[]) getValueOrVariable(properties
+                        .get("do"));
+                for (String x : (String[]) value) {
+                    data.put(name, x);
+                    for (String c : commands) {
+                        execute(c);
+                    }
+                }
+                data.put(name, old);
+            } else if ("loop".equals(k)) {
+                while (true) {
+                    for (String c : (String[]) value) {
+                        execute(c);
+                        if (data.remove("$break") != null) {
+                            return;
+                        }
+                    }
+                }
+            } else if (k.startsWith("$")) {
+                setVariable(properties, k, value);
+            }
+        }
+    }
+
+    private void setVariable(Map<String, String> properties, String k,
+            Object value) {
+        if (k.startsWith("$$")) {
+            k = "$" + getValueOrVariable("\"" + k.substring(1) + "\"");
+        }
+        if (properties.containsKey("+")) {
+            Object v2 = getValueOrVariable(properties.get("+"));
+            if (value == null) {
+                value = v2;
+            } else if (v2 == null) {
+                // keep value
+            } else if (v2 instanceof Long && value instanceof Long) {
+                value = (Long) value + (Long) v2;
+            } else {
+                value = value.toString() + v2.toString();
+            }
+        }
+        data.put(k, value);
+    }
+
+    private Object getValueOrVariable(String jsonValue) {
+        Object v = getValue(jsonValue);
+        if (v == null || !v.toString().startsWith("$")) {
+            return v;
+        }
+        String value = v.toString();
+        if (value.startsWith("$$")) {
+            value = "$" + getValueOrVariable("\"" + value.substring(1) + "\"");
+        }
+        return data.get(value.toString());
+    }
+
+    private void addNode(Node p, String nodeName, JsonObject json)
+            throws RepositoryException {
+        Map<String, String> properties = json.getProperties();
+        Map<String, JsonObject> children = json.getChildren();
+        String primaryType = properties.get("jcr:primaryType");
+        Node n;
+        if (primaryType == null) {
+            n = p.addNode(nodeName);
+        } else {
+            n = p.addNode(nodeName, 
getValueOrVariable(primaryType).toString());
+        }
+        for (Entry<String, String> e : properties.entrySet()) {
+            String propertyName = e.getKey();
+            if (!"jcr:primaryType".equals(propertyName)) {
+                Object value = 
getValueOrVariable(properties.get(propertyName));
+                setProperty(n, propertyName, value);
+            }
+        }
+        for (Entry<String, JsonObject> e : children.entrySet()) {
+            String k = e.getKey();
+            JsonObject v = e.getValue();
+            addNode(n, k, v);
+        }
+    }
+
+    private static Object getValue(String jsonValue) {
+        if (jsonValue == null) {
+            return null;
+        }
+        JsopTokenizer t = new JsopTokenizer(jsonValue);
+        if (t.matches(JsopReader.NULL)) {
+            return null;
+        } else if (t.matches(JsopReader.NUMBER)) {
+            String n = t.getToken();
+            if (n.indexOf('.') < 0) {
+                return Long.parseLong(n);
+            }
+            return Double.parseDouble(n);
+        } else if (t.matches(JsopReader.TRUE)) {
+            return true;
+        } else if (t.matches(JsopReader.FALSE)) {
+            return false;
+        } else if (t.matches(JsopReader.STRING)) {
+            return t.getToken();
+        } else if (t.matches('[')) {
+            ArrayList<String> list = new ArrayList<String>();
+            if (!t.matches(']')) {
+                while (true) {
+                    list.add(t.readRawValue());
+                    if (t.matches(']')) {
+                        break;
+                    }
+                    t.read(',');
+                }
+            }
+            return list.toArray(new String[0]);
+        }
+        throw new IllegalArgumentException(jsonValue);
+    }
+
+    private static void setProperty(Node n, String propertyName, Object value)
+            throws RepositoryException {
+        int type = PropertyType.UNDEFINED;
+        if (propertyName.startsWith("{")) {
+            String t = propertyName.substring(1, propertyName.indexOf('}'));
+            propertyName = propertyName.substring(t.length() + 2);
+            type = PropertyType.valueFromName(t);
+        }
+        if (value == null) {
+            n.setProperty(propertyName, (String) null);
+            return;
+        }
+        if (type == PropertyType.UNDEFINED) {
+            if (value instanceof Boolean) {
+                type = PropertyType.BOOLEAN;
+            } else if (value instanceof Long) {
+                type = PropertyType.LONG;
+            } else if (value instanceof Double) {
+                type = PropertyType.DOUBLE;
+            } else {
+                type = PropertyType.STRING;
+            }
+        }
+        if (value instanceof String[]) {
+            String[] list = (String[]) value;
+            for (int i = 0; i < list.length; i++) {
+                list[i] = getValue(list[i]).toString();
+            }
+            n.setProperty(propertyName, list, type);
+        } else {
+            n.setProperty(propertyName, value.toString(), type);
+        }
+    }
+
+    private void runQuery(String query, String language, String columnName,
+            boolean quiet, int depth) throws RepositoryException {
+        ArrayList<String> list = new ArrayList<String>();
+        columnName = query.startsWith("explain") ? "plan" : columnName;
+        QueryManager qm = session.getWorkspace().getQueryManager();
+        Query q = qm.createQuery(query, language);
+        for (String b : q.getBindVariableNames()) {
+            ValueFactory vf = session.getValueFactory();
+            q.bindValue(b, vf.createValue(data.get("$" + b).toString()));
+        }
+        QueryResult result = q.execute();
+        if (depth != 0) {
+            NodeIterator ni = result.getNodes();
+            JsopBuilder builder = new JsopBuilder().array();
+            while (ni.hasNext()) {
+                Node n = ni.nextNode();
+                builder.key(n.getPath());
+                appendNode(builder, n, depth - 1);
+            }
+            
output.println(JsopBuilder.prettyPrint(builder.endArray().toString()));
+            return;
+        }
+        RowIterator ri = result.getRows();
+        while (ri.hasNext()) {
+            Row r = ri.nextRow();
+            if (columnName != null) {
+                String x = r.getValue(columnName).getString();
+                list.add(x);
+                if (!quiet) {
+                    output.println(x);
+                }
+            } else {
+                String[] columnNames = result.getColumnNames();
+                for (String cn : columnNames) {
+                    Value v = r.getValue(cn);
+                    String x = v == null ? null : v.getString();
+                    if (columnNames.length == 1) {
+                        list.add(x);
+                        if (!quiet) {
+                            output.println(x);
+                        }
+                    } else {
+                        list.add(x);
+                        if (!quiet) {
+                            output.println(cn + ": " + x);
+                        }
+                    }
+                }
+            }
+        }
+        data.put("$resultSize", (long) list.size());
+        data.put("$result", list.toArray(new String[0]));
+    }
+
+    private void appendNode(JsopBuilder builder, Node n, int depth)
+            throws RepositoryException {
+        builder.object();
+        for (PropertyIterator it = n.getProperties(); depth != 0 &&
+                it.hasNext();) {
+            Property p = it.nextProperty();
+            String name = (p.getType() == PropertyType.STRING ||
+                    p.getName().equals("jcr:primaryType") ? "" : "{" +
+                    PropertyType.nameFromValue(p.getType()) + "}") +
+                    p.getName();
+            builder.key(name);
+            if (p.isMultiple()) {
+                builder.array();
+                for (Value v : p.getValues()) {
+                    builder.value(v.getString());
+                }
+                builder.endArray();
+            } else {
+                builder.value(p.getValue().getString());
+            }
+        }
+        for (NodeIterator it = n.getNodes(); depth != 0 && it.hasNext();) {
+            Node n2 = it.nextNode();
+            builder.key(n2.getName());
+            appendNode(builder, n2, depth - 1);
+        }
+        builder.endObject();
+    }
+
+}

Modified: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java?rev=1780222&r1=1780221&r2=1780222&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java
 Wed Jan 25 15:46:25 2017
@@ -44,6 +44,7 @@ enum Mode {
     PERSISTENTCACHE("persistentcache", new PersistentCacheCommand()),
     THREADDUMP("threaddump", new ThreadDumpCommand()),
     DATASTORECACHEUPGRADE("datastorecacheupgrade", new 
DataStoreCacheUpgradeCommand()),
+    INDEX("index", new IndexCommand()),
     HELP("help", new HelpCommand());
 
     private final String name;

Modified: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java?rev=1780222&r1=1780221&r2=1780222&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Utils.java
 Wed Jan 25 15:46:25 2017
@@ -54,6 +54,8 @@ import org.apache.jackrabbit.oak.spi.blo
 import org.apache.jackrabbit.oak.spi.state.NodeStore;
 
 class Utils {
+    
+    private static final long MB = 1024 * 1024;
 
     public static NodeStore bootstrapNodeStore(String[] args, Closer closer, 
String h) throws IOException, InvalidFileStoreVersionException {
         //TODO add support for other NodeStore flags
@@ -61,7 +63,12 @@ class Utils {
         OptionSpec<Integer> clusterId = parser
                 .accepts("clusterId", "MongoMK clusterId").withRequiredArg()
                 .ofType(Integer.class).defaultsTo(0);
-        OptionSpec segmentTar = parser.accepts("segment-tar", "Use 
oak-segment-tar instead of oak-segment");
+        OptionSpec<Void> disableBranchesSpec = parser.
+                accepts("disableBranches", "disable branches");    
+        OptionSpec<Integer> cacheSizeSpec = parser.
+                accepts("cacheSize", "cache size").withRequiredArg().
+                ofType(Integer.class).defaultsTo(0);         
+        OptionSpec<?> segmentTar = parser.accepts("segment-tar", "Use 
oak-segment-tar instead of oak-segment");
         OptionSpec<?> help = parser.acceptsAll(asList("h", "?", "help"),
                 "show help").forHelp();
         OptionSpec<String> nonOption = parser
@@ -90,10 +97,19 @@ class Utils {
             }
             MongoConnection mongo = new MongoConnection(uri.getURI());
             closer.register(asCloseable(mongo));
-            DocumentNodeStore store = new DocumentMK.Builder()
-                    .setMongoDB(mongo.getDB())
-                    .setLeaseCheck(false)
-                    .setClusterId(clusterId.value(options)).getNodeStore();
+            DocumentMK.Builder builder = new DocumentMK.Builder();
+            builder.
+                setMongoDB(mongo.getDB()).
+                setLeaseCheck(false).
+                setClusterId(clusterId.value(options));
+            if (options.has(disableBranchesSpec)) {
+                builder.disableBranches();
+            }
+            int cacheSize = cacheSizeSpec.value(options);
+            if (cacheSize != 0) {
+                builder.memoryCacheSize(cacheSize * MB);
+            }
+            DocumentNodeStore store = builder.getNodeStore();
             closer.register(asCloseable(store));
             return store;
         }

Added: 
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/IndexTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/IndexTest.java?rev=1780222&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/IndexTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-run/src/test/java/org/apache/jackrabbit/oak/run/IndexTest.java
 Wed Jan 25 15:46:25 2017
@@ -0,0 +1,189 @@
+/*
+ * 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.run;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.apache.jackrabbit.oak.console.NodeStoreFixture;
+import org.apache.jackrabbit.oak.console.NodeStoreOpener;
+import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders;
+import org.apache.jackrabbit.oak.segment.memory.MemoryStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Test;
+
+public class IndexTest {
+
+    @Test
+    public void simple() throws Exception {
+        assertCommand(
+                "hello", 
+                "{'print':'hello'}");
+        assertCommand(
+                "false", 
+                "{'print':false}");
+        assertCommand("1\n2\n3", 
+                "{'$x':[1, 2, 3]}",
+                "{'for':'$x', 'do': [{'print': '$x'}]}");
+        assertCommand("x1\nx2\nx3", 
+                "{'$myFunction':[{'$y': 'x', '+': '$x'}, {'print':'$y'}]}",    
            
+                "{'$x':[1, 2, 3]}",
+                "{'for':'$x', 'do': '$myFunction'}");
+        assertCommand("2\n4\n8", 
+                "{'$x':1}",
+                "{'loop':[{'$x': '$x', '+':'$x'}, {'print': '$x'}, {'$break': 
true, 'if': '$x', '=': 8}]}");
+        assertCommand("b\nd", 
+                "{'$x':1}",
+                "{'print':'a', 'if':'$x', '=':null}",
+                "{'print':'b', 'if':'$x', '=':1}",
+                "{'print':'c', 'if':null, '=':1}",
+                "{'print':'d', 'if':null, '=':null}");
+        assertCommand("10\n10", 
+                "{'$x':1}",
+                "{'$$x':10}",
+                "{'print':'$1'}",
+                "{'print':'$$x'}");
+        assertCommand("1\nnull\n1\n2\na1", 
+                "{'$x':1, '+':null}",
+                "{'print':'$x'}",
+                "{'$x':null, '+':null}",
+                "{'print':'$x'}",
+                "{'$x':null, '+':1}",
+                "{'print':'$x'}",
+                "{'$x':1, '+':1}",
+                "{'print':'$x'}",
+                "{'$x':'a', '+':'1'}",
+                "{'print':'$x'}");
+    }
+    
+    private static NodeStoreFixture memoryFixture() {
+        return new NodeStoreFixture() {
+
+            @Override
+            public void close() throws IOException {
+                // ignore
+            }
+
+            @Override
+            public NodeStore getStore() {
+                try {
+                    return SegmentNodeStoreBuilders.builder(new 
MemoryStore()).build();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            
+        };
+    }
+    
+    @Test
+    public void readWrite() throws Exception {
+        IndexCommand index = new IndexCommand();
+        try (NodeStoreFixture fixture = memoryFixture();
+                ) {
+            NodeStore store = fixture.getStore();
+            index.session = NodeStoreOpener.openSession(store);
+            assertCommand(index, "",
+                    "{'addNode':'/foo', 'node':{'jcr:primaryType': 
'nt:unstructured', 'x': 1, 'y':{}}}", 
+                    "{'session': 'save'}");
+            assertCommand(index, 
+                    "/foo\n" + 
+                    "/jcr:system\n" +
+                    "/oak:index\n" +
+                    "/rep:security",
+                    "{'xpath':'/jcr:root/* order by @jcr:path'}");
+            assertCommand(index, 
+                    "/oak:index/counter",
+                    "{'xpath':'/jcr:root//element(*, 
oak:QueryIndexDefinition)[@type=`counter`] " + 
+                        "order by @jcr:path'}");
+            assertCommand(index, 
+                    "[nt:unstructured] as [a] /* property test = 1 " + 
+                            "where ([a].[x] = 1) and (isdescendantnode([a], 
[/])) */",
+                    "{'addNode':'/oak:index/test', 'node':{ " + 
+                        "'jcr:primaryType':'oak:QueryIndexDefinition', " + 
+                        "'type':'property', " + 
+                        "'reindex':true, " +
+                        "'entryCount': 1, " +
+                        "'{Name}declaringNodeTypes': ['nt:unstructured'], " +
+                        "'{Name}propertyNames':['x'] " +
+                        "}}",
+                    "{'session':'save'}",
+                    "{'xpath':'explain /jcr:root//element(*, 
nt:unstructured)[@x=1]'}",
+                    "{'xpath':'/jcr:root//element(*, nt:unstructured)[@x=2]'}"
+                    );
+            assertCommand(index, 
+                    "50",
+                    "{'addNode':'/foo/test', 'node':{'jcr:primaryType': 
'oak:Unstructured', 'child':{}}}",
+                    "{'$x':1}",
+                    "{'loop':[" + 
+                            "{'$p': '/foo/test/child/n', '+': '$x'}, " + 
+                            "{'addNode': '$p', 'node': {'x': '$x', 
'jcr:primaryType': 'nt:unstructured'}}, " + 
+                            "{'session':'save'}, " +
+                            "{'$x': '$x', '+':1}, " + 
+                            "{'$break': true, 'if': '$x', '=': 100}]}",
+                    "{'session':'save'}",
+                    "{'xpath':'/jcr:root//element(*, nt:unstructured)[@x<50]', 
'quiet':true}",
+                    "{'$y':0}",
+                    "{'for':'$result', 'do': [{'$y': '$y', '+': 1}]}",
+                    "{'print': '$y'}"
+                    );
+            assertCommand(index, 
+                    "[nt:unstructured] as [a] /* nodeType Filter(query=" + 
+                            "explain select [jcr:path], [jcr:score], * from 
[nt:unstructured] as a " + 
+                            "where [x] = 1 and isdescendantnode(a, '/') /* 
xpath: " + 
+                            "/jcr:root//element(*, nt:unstructured)[@x=1] */, 
path=//*, " + 
+                            "property=[x=[1]]) where ([a].[x] = 1) and 
(isdescendantnode([a], [/])) */",
+                    "{'setProperty': '/oak:index/test/type', 'value': 
'disabled'}",
+                    "{'session':'save'}",
+                    "{'xpath':'explain /jcr:root//element(*, 
nt:unstructured)[@x=1]'}"
+                    );
+            assertCommand(index, 
+                    "[nt:unstructured] as [a] /* traverse '*' " + 
+                            "where [a].[x] = 1 */",
+                    "{'removeNode': '/oak:index/nodetype'}",
+                    "{'session':'save'}",
+                    "{'sql':'explain select * from [nt:unstructured] as [a] 
where [x]=1'}"
+                    );
+            assertCommand(index, 
+                    "['/foo': {\n" +
+                    "  'jcr:primaryType': 'nt:unstructured', '{Long}x': '1', 
'y': {}, 'test': {}\n" +
+                    "}]",
+                    "{'xpath':'/jcr:root/foo', 'depth':2}"
+                    );
+            index.session.logout();
+        }
+    }
+    
+    void assertCommand(String expected, String... commands) throws Exception {
+        assertCommand(new IndexCommand(), expected, commands);
+    }
+
+    void assertCommand(IndexCommand index, String expected, String... 
commands) throws Exception {
+        ByteArrayOutputStream w = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(w, false, "UTF-8");
+        index.output = out;
+        for(String c : commands) {
+            index.execute(c.replace('\'', '"').replace('`', '\''));
+        }
+        String got = new String(w.toByteArray());
+        assertEquals(expected, got.trim().replace('"', '\''));
+    }
+    
+}


Reply via email to