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('"', '\''));
+ }
+
+}