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 mr...@apache.org on 2014/05/21 14:29:56 UTC
svn commit: r1596557 - in
/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak:
console/ console/Command.java console/Console.java
console/ConsoleSession.java run/Main.java
Author: mreutegg
Date: Wed May 21 12:29:56 2014
New Revision: 1596557
URL: http://svn.apache.org/r1596557
Log:
OAK-1805: Debugging console
Initial version of a command line tool
Added:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Command.java
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Console.java
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/ConsoleSession.java
Modified:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java
Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Command.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Command.java?rev=1596557&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Command.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Command.java Wed May 21 12:29:56 2014
@@ -0,0 +1,527 @@
+/*
+ * 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 java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import com.google.common.base.StandardSystemProperty;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
+
+/**
+ * All available console commands.
+ */
+public abstract class Command {
+
+ private static final char[] SPACES = new char[4];
+
+ static {
+ Arrays.fill(SPACES, ' ');
+ }
+
+ protected String args;
+ protected String description;
+ protected String usage;
+
+ @Nonnull
+ public static Command create(String line) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+ for (; i < line.length(); i++) {
+ char c = line.charAt(i);
+ if (Character.isWhitespace(c)) {
+ if (sb.length() == 0) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (sb.length() == 0) {
+ c = Character.toUpperCase(c);
+ } else {
+ c = Character.toLowerCase(c);
+ }
+ sb.append(c);
+ }
+
+ if (sb.length() == 0) {
+ return new NoOp();
+ }
+
+ Command cmd;
+ try {
+ cmd = Class.forName(Command.class.getName() + "$" + sb.toString())
+ .asSubclass(Command.class).newInstance();
+ } catch (Exception e) {
+ cmd = new Unknown(sb.toString().toLowerCase(Locale.US));
+ }
+ cmd.init(line.substring(i).trim());
+ return cmd;
+ }
+
+ protected final void init(String args) {
+ this.args = args;
+ }
+
+ public abstract void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws Exception;
+
+ public String getName() {
+ return getClass().getSimpleName().toLowerCase(Locale.US);
+ }
+
+ protected void println(Object value, OutputStream out) throws IOException {
+ Writer writer = new OutputStreamWriter(out);
+ if (value == null) {
+ writer.write("[null]");
+ } else {
+ writer.write(value.toString());
+ }
+ println(writer);
+ writer.flush();
+ }
+
+ protected static void println(Writer writer) throws IOException {
+ writer.write(StandardSystemProperty.LINE_SEPARATOR.value());
+ }
+
+ private static final class Unknown extends Command {
+
+ private final String cmd;
+
+ private Unknown(String cmd) {
+ this.cmd = cmd;
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ println("Unknown command: " + cmd + " " + args, out);
+ }
+ }
+
+ private static final class NoOp extends Command {
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ }
+ }
+
+ public static final class Help extends Command {
+
+ public Help() {
+ this.description = "List all available command.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws Exception {
+ SortedMap<String, Command> commands = Maps.newTreeMap();
+ SortedMap<String, Command> docCommands = Maps.newTreeMap();
+ Class[] classes = Command.class.getDeclaredClasses();
+ int maxCmdLength = 0;
+ for (Class clazz : classes) {
+ if (!Modifier.isPublic(clazz.getModifiers())
+ || !Command.class.isAssignableFrom(clazz)) {
+ continue;
+ }
+ Command cmd = (Command) clazz.newInstance();
+ if (DocumentNodeStoreCommand.class.isAssignableFrom(clazz)) {
+ docCommands.put(cmd.getName(), cmd);
+ } else {
+ commands.put(cmd.getName(), cmd);
+ }
+ maxCmdLength = Math.max(maxCmdLength, cmd.getName().length());
+ }
+ println("Generic commands:", out);
+ Writer writer = new OutputStreamWriter(out);
+ printCommands(commands, maxCmdLength, writer);
+ println(writer);
+ writer.flush();
+ println("DocumentNodeStore specific commands:", out);
+ printCommands(docCommands, maxCmdLength, writer);
+ writer.flush();
+ }
+
+ private void printCommands(SortedMap<String, Command> commands,
+ int maxCmdLength,
+ Writer writer) throws IOException {
+ for (String c : commands.keySet()) {
+ Command cmd = commands.get(c);
+ writer.write(SPACES);
+ writer.write(c);
+ int numSpaces = maxCmdLength - c.length() + 4;
+ for (int i = 0; i < numSpaces; i++) {
+ writer.write(" ");
+ }
+ if (cmd.description != null) {
+ writer.write(cmd.description);
+ }
+ println(writer);
+ }
+ }
+ }
+
+ public static final class Pwd extends Command {
+
+ public Pwd() {
+ this.description = "Print the full path of the current working node.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ println(session.getWorkingPath(), out);
+ }
+ }
+
+ public static final class Exit extends Command {
+
+ public Exit() {
+ this.description = "Quit this console.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ println("Good bye.", out);
+ }
+ }
+
+ public static final class Cd extends Command {
+
+ public Cd() {
+ this.description = "Change the current working directory to a specific node.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ if (!PathUtils.isValid(args)) {
+ println("Not a valid path: " + args, out);
+ return;
+ }
+ String path;
+ if (PathUtils.isAbsolute(args)) {
+ path = args;
+ } else {
+ path = PathUtils.concat(session.getWorkingPath(), args);
+ }
+ List<String> elements = Lists.newArrayList();
+ for (String element : PathUtils.elements(path)) {
+ if (PathUtils.denotesParent(element)) {
+ if (!elements.isEmpty()) {
+ elements.remove(elements.size() - 1);
+ }
+ } else if (!PathUtils.denotesCurrent(element)) {
+ elements.add(element);
+ }
+ }
+ path = PathUtils.concat("/", elements.toArray(new String[elements.size()]));
+ String old = session.setWorkingPath(path);
+ if (!session.getWorkingNode().exists()) {
+ session.setWorkingPath(old);
+ println("No such node", out);
+ }
+ }
+ }
+
+ public static final class Ls extends Command {
+
+ public Ls() {
+ this.description = "List the names of the children of the current node.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ for (String name : session.getWorkingNode().getChildNodeNames()) {
+ println(name, out);
+ }
+ }
+ }
+
+ public static final class Pn extends Command {
+
+ public Pn() {
+ this.description = "Print the current node.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ println(AbstractNodeState.toString(session.getWorkingNode()), out);
+ }
+ }
+
+ public static final class Refresh extends Command {
+
+ public Refresh() {
+ this.description = "Control how the current session is refreshed.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ if (args.contains("auto")) {
+ session.setAutoRefresh(true);
+ println("Enabled auto refresh", out);
+ } else if (args.contains("manual")) {
+ session.setAutoRefresh(false);
+ println("Disabled auto refresh", out);
+ } else if (args.trim().length() == 0) {
+ session.refresh();
+ println("Session refreshed", out);
+ } else {
+ println("Unrecognized arguments: " + args, out);
+ }
+
+ }
+ }
+
+ public static final class Checkpoint extends Command {
+
+ public Checkpoint() {
+ this.description = "Create a checkpoint.";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws Exception {
+ long time = TimeUnit.HOURS.toSeconds(1);
+ if (args.trim().length() > 0) {
+ try {
+ time = Long.parseLong(args.trim());
+ } catch (NumberFormatException e) {
+ println("Not a number: " + args, out);
+ return;
+ }
+ }
+ StringBuilder msg = new StringBuilder();
+ msg.append("Checkpoint created: ");
+ msg.append(session.checkpoint(time));
+ msg.append(" (expires: ");
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.SECOND, (int) time);
+ msg.append(cal.getTime().toString());
+ msg.append(").");
+ println(msg, out);
+ }
+ }
+
+ public static final class Retrieve extends Command {
+
+ public Retrieve() {
+ this.description = "Retrieve a snapshot of the given checkpoint";
+ }
+
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws Exception {
+ if (session.isAutoRefresh()) {
+ session.setAutoRefresh(false);
+ println("Auto refresh disabled.", out);
+ }
+ session.retrieve(args.trim());
+ println("Root node is now at " + args.trim(), out);
+ }
+ }
+
+ //-----------------------< DocumentNodeStore specific >---------------------
+
+ abstract static class DocumentNodeStoreCommand extends Command {
+ @Override
+ public void execute(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ if (session.getStore() instanceof DocumentNodeStore) {
+ execute(session, (DocumentNodeStore) session.getStore(), in, out);
+ } else {
+ println("Can only execute command on a DocumentNodeStore", out);
+ }
+ }
+
+ protected abstract void execute(@Nonnull ConsoleSession session,
+ @Nonnull DocumentNodeStore store,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out)
+ throws IOException;
+ }
+
+ public static final class Pd extends DocumentNodeStoreCommand {
+
+ public Pd() {
+ this.description = "Print the current document.";
+ }
+
+ @Override
+ protected void execute(@Nonnull ConsoleSession session,
+ @Nonnull DocumentNodeStore store,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ String id = Utils.getIdFromPath(session.getWorkingPath());
+ NodeDocument doc = store.getDocumentStore().find(NODES, id);
+ if (doc == null) {
+ println("[null]", out);
+ } else {
+ println(doc, out);
+ }
+ }
+
+ private void println(NodeDocument doc, OutputStream out)
+ throws IOException {
+ int level = 1;
+ Writer writer = new OutputStreamWriter(out);
+ Set<String> mapKeys = Sets.newHashSet();
+ writer.write('{');
+ String comma = "";
+ for (String key : doc.keySet()) {
+ Object value = doc.get(key);
+ if (value instanceof Map) {
+ mapKeys.add(key);
+ continue;
+ }
+ writer.write(comma);
+ comma = ",";
+ println(writer);
+ printIndent(level, writer);
+ printJson(key, value, writer);
+ }
+ for (String key : mapKeys) {
+ writer.write(comma);
+ comma = ",";
+ println(writer);
+ printIndent(level, writer);
+ writer.write(JSONObject.escape(key));
+ writer.write(": {");
+ println((Map) doc.get(key), level + 1, writer);
+ println(writer);
+ printIndent(level, writer);
+ writer.write("}");
+ }
+ println(writer);
+ writer.write('}');
+ println(writer);
+ writer.flush();
+ }
+
+ private void println(Map map, int level, Writer writer)
+ throws IOException {
+ String comma = "";
+ for (Object obj : map.keySet()) {
+ String key = obj.toString();
+ Object value = map.get(obj);
+ writer.write(comma);
+ comma = ",";
+ println(writer);
+ printIndent(level, writer);
+ printJson(key, value, writer);
+ }
+ }
+
+ private void printJson(String key, Object value, Writer writer)
+ throws IOException {
+ writer.write(JSONObject.escape(key));
+ writer.write(": ");
+ writer.write(jsonEscape(value));
+ }
+
+ private String jsonEscape(Object value) {
+ String escaped = JSONValue.toJSONString(value);
+ return escaped.replaceAll("\\\\/", "/");
+ }
+
+ private void printIndent(int level, Writer writer) throws IOException {
+ for (int i = 0; i < level; i++) {
+ writer.write(SPACES);
+ }
+ }
+ }
+
+ public static final class Lsd extends DocumentNodeStoreCommand {
+
+ public Lsd() {
+ this.description = "List the identifiers of the child documents at the current path.";
+ }
+
+ @Override
+ protected void execute(@Nonnull ConsoleSession session,
+ @Nonnull DocumentNodeStore store,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) throws IOException {
+ Writer writer = new OutputStreamWriter(out);
+ String path = session.getWorkingPath();
+ String fromKey = Utils.getKeyLowerLimit(path);
+ String toKey = Utils.getKeyUpperLimit(path);
+ int num = 0;
+ for (NodeDocument doc : store.getDocumentStore().query(
+ NODES, fromKey, toKey, Integer.MAX_VALUE)) {
+ writer.write(doc.getId());
+ println(writer);
+ num++;
+ }
+ println(writer);
+ writer.write("Found " + num + " document");
+ if (num != 1) {
+ writer.write("s");
+ }
+ writer.write(".");
+ println(writer);
+ writer.flush();
+ }
+ }
+
+}
Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Console.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Console.java?rev=1596557&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Console.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Console.java Wed May 21 12:29:56 2014
@@ -0,0 +1,129 @@
+/*
+ * 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 java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.fixture.OakFixture;
+import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
+import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+/**
+ * A command line console.
+ */
+public class Console {
+
+ public static void main(String[] args) throws Exception {
+ OptionParser parser = new OptionParser();
+ OptionSpec<File> base = parser.accepts("base", "Base directory")
+ .withRequiredArg().ofType(File.class)
+ .defaultsTo(new File("."));
+ OptionSpec<String> host = parser.accepts("host", "MongoDB host")
+ .withRequiredArg().defaultsTo("localhost");
+ OptionSpec<Integer> port = parser.accepts("port", "MongoDB port")
+ .withRequiredArg().ofType(Integer.class).defaultsTo(27017);
+ OptionSpec<String> dbName = parser.accepts("db", "MongoDB database")
+ .withRequiredArg().defaultsTo("oak");
+ OptionSpec<Integer> clusterId = parser.accepts("clusterId", "MongoMK clusterId")
+ .withRequiredArg().ofType(Integer.class).defaultsTo(1);
+ OptionSpec<Integer> cache = parser.accepts("cache", "cache size (MB)")
+ .withRequiredArg().ofType(Integer.class).defaultsTo(100);
+
+ OptionSet options = parser.parse(args);
+ Set<String> argset = Sets.newHashSet(options.nonOptionArguments());
+
+ String uri = "mongodb://" + host.value(options) + ":" +
+ port.value(options) + "/" + dbName.value(options);
+ NodeStore store;
+ if (argset.contains(OakFixture.OAK_MONGO)) {
+ MongoConnection mongo = new MongoConnection(uri);
+ store = new DocumentMK.Builder().
+ setMongoDB(mongo.getDB()).
+ memoryCacheSize(cache.value(options)).
+ setClusterId(clusterId.value(options)).getNodeStore();
+ } else if (argset.contains(OakFixture.OAK_TAR)) {
+ store = new SegmentNodeStore(new FileStore(base.value(options), 256));
+ } else {
+ System.out.println("No repository specified. " +
+ "Was expecting one of: " + Lists.newArrayList(
+ OakFixture.OAK_TAR, OakFixture.OAK_MONGO));
+ System.exit(1);
+ return;
+ }
+
+ System.exit(new Console(store, System.in, System.out).run());
+ }
+
+ private final NodeStore store;
+ private final InputStream in;
+ private final OutputStream out;
+
+ public Console(NodeStore store, InputStream in, OutputStream out) {
+ this.store = store;
+ this.in = in;
+ this.out = out;
+ }
+
+ public int run() throws Exception {
+ int code = 1;
+ ConsoleSession session = ConsoleSession.create(store);
+ for (;;) {
+ prompt();
+ String line = readLine(in);
+ if (line == null) {
+ break;
+ }
+ Command c = Command.create(line);
+ try {
+ c.execute(session, in, out);
+ } catch (Exception e) {
+ e.printStackTrace(new PrintWriter(out));
+ }
+ if (c.getName().equals("exit")) {
+ code = 0;
+ break;
+ }
+
+ }
+ return code;
+ }
+
+ private void prompt() throws IOException {
+ out.write("> ".getBytes());
+ }
+
+ private static String readLine(InputStream in) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ return reader.readLine();
+ }
+}
Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/ConsoleSession.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/ConsoleSession.java?rev=1596557&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/ConsoleSession.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/ConsoleSession.java Wed May 21 12:29:56 2014
@@ -0,0 +1,165 @@
+/*
+ * 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 java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+import com.google.common.collect.Maps;
+
+/**
+ * Light weight session to a NodeStore, holding context information.
+ */
+public abstract class ConsoleSession {
+
+ private final Map<String, Object> context = Maps.newHashMap();
+
+ private final NodeStore store;
+
+ private ConsoleSession(NodeStore store) {
+ this.store = store;
+ }
+
+ public static ConsoleSession create(NodeStore store) {
+ return new DefaultSession(store);
+ }
+
+ /**
+ * Returns the current working path. This method will return the path
+ * of the root node if none is set explicity.
+ *
+ * @return the current working path.
+ */
+ public String getWorkingPath() {
+ String path = (String) context.get("workingPath");
+ if (path == null) {
+ path = "/";
+ context.put("workingPath", path);
+ }
+ return path;
+ }
+
+ /**
+ * Sets a new working path and returns the previously set.
+ *
+ * @param path the new working path.
+ * @return the previously set working path.
+ */
+ public String setWorkingPath(String path) {
+ String old = getWorkingPath();
+ context.put("workingPath", path);
+ return old;
+ }
+
+ /**
+ * Creates and returns a checkpoint with the given lifetime in seconds.
+ *
+ * @param lifetimeSeconds the lifetime of the checkpoint in seconds.
+ * @return the checkpoint reference.
+ */
+ public String checkpoint(long lifetimeSeconds) {
+ return store.checkpoint(TimeUnit.SECONDS.toMillis(lifetimeSeconds));
+ }
+
+ /**
+ * Retrieves the root node from a previously created checkpoint. The root
+ * node is available with {@link #getRoot()}.
+ *
+ * @param checkpoint the checkpoint reference.
+ */
+ public void retrieve(String checkpoint) {
+ context.put("root", store.retrieve(checkpoint));
+ }
+
+ /**
+ * Returns the currently set root node. If {@link #isAutoRefresh()} is set,
+ * a fresh root node is retrieved from the store.
+ *
+ * @return the current root node.
+ */
+ public NodeState getRoot() {
+ NodeState root = (NodeState) context.get("root");
+ if (root == null || isAutoRefresh()) {
+ root = store.getRoot();
+ context.put("root", root);
+ }
+ return root;
+ }
+
+ /**
+ * @return the underlying node store.
+ */
+ public NodeStore getStore() {
+ return store;
+ }
+
+ /**
+ * The node state for the current working path. Possibly non-existent.
+ *
+ * @return the working node state.
+ */
+ @Nonnull
+ public NodeState getWorkingNode() {
+ NodeState current = getRoot();
+ for (String element : PathUtils.elements(getWorkingPath())) {
+ current = current.getChildNode(element);
+ }
+ return current;
+ }
+
+ /**
+ * Enables or disables auto-refresh of the root node state on
+ * {@link #getRoot()}.
+ *
+ * @param enable enables or disables auto-refresh.
+ */
+ public void setAutoRefresh(boolean enable) {
+ if (enable) {
+ context.put("auto-refresh", "true");
+ } else {
+ context.remove("auto-refresh");
+ }
+ }
+
+ /**
+ * @return <code>true</code> if auto-refresh is enabled; <code>false</code>
+ * otherwise.
+ */
+ public boolean isAutoRefresh() {
+ return context.containsKey("auto-refresh");
+ }
+
+ /**
+ * Performs a manual refresh of the root node state.
+ */
+ public void refresh() {
+ context.put("root", store.getRoot());
+ }
+
+ private static final class DefaultSession extends ConsoleSession {
+
+ DefaultSession(NodeStore store) {
+ super(store);
+ }
+ }
+}
Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java?rev=1596557&r1=1596556&r2=1596557&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Main.java Wed May 21 12:29:56 2014
@@ -48,6 +48,7 @@ import org.apache.jackrabbit.oak.Oak;
import org.apache.jackrabbit.oak.api.ContentRepository;
import org.apache.jackrabbit.oak.benchmark.BenchmarkRunner;
import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.console.Console;
import org.apache.jackrabbit.oak.fixture.OakFixture;
import org.apache.jackrabbit.oak.http.OakServlet;
import org.apache.jackrabbit.oak.jcr.Jcr;
@@ -99,6 +100,9 @@ public class Main {
case BENCHMARK:
BenchmarkRunner.main(args);
break;
+ case CONSOLE:
+ Console.main(args);
+ break;
case DEBUG:
debug(args);
break;
@@ -487,6 +491,7 @@ public class Main {
BACKUP("backup"),
BENCHMARK("benchmark"),
+ CONSOLE("debug"),
DEBUG("debug"),
SERVER("server"),
UPGRADE("upgrade"),