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/22 14:07:03 UTC
svn commit: r1596825 - in
/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console:
Command.java Console.java IOPump.java
Author: mreutegg
Date: Thu May 22 12:07:03 2014
New Revision: 1596825
URL: http://svn.apache.org/r1596825
Log:
OAK-1805: Debugging console
Add support for piped commands and executing an OS process.
Added:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/IOPump.java (with props)
Modified:
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
Modified: 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=1596825&r1=1596824&r2=1596825&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Command.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Command.java Thu May 22 12:07:03 2014
@@ -34,6 +34,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
+import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
@@ -59,7 +60,7 @@ import static org.apache.jackrabbit.oak.
/**
* All available console commands.
*/
-public abstract class Command {
+public abstract class Command implements Callable<Object> {
private static final char[] SPACES = new char[4];
@@ -70,9 +71,21 @@ public abstract class Command {
protected String args;
protected String description;
protected String usage;
+ protected ConsoleSession session;
+ protected InputStream in;
+ protected OutputStream out;
@Nonnull
- public static Command create(String line) throws IOException {
+ public static List<Command> create(String line) throws IOException {
+ List<Command> commands = Lists.newArrayList();
+ for (String s : line.split("\\|")) {
+ commands.add(parse(s));
+ }
+ return commands;
+ }
+
+ @Nonnull
+ private static Command parse(String line) throws IOException {
StringBuilder sb = new StringBuilder();
int i = 0;
for (; i < line.length(); i++) {
@@ -103,17 +116,31 @@ public abstract class Command {
} catch (Exception e) {
cmd = new Unknown(sb.toString().toLowerCase(Locale.US));
}
- cmd.init(line.substring(i).trim());
+ cmd.setArgs(line.substring(i).trim());
return cmd;
}
- protected final void init(String args) {
+ protected final void setArgs(String args) {
this.args = args;
}
- public abstract void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws Exception;
+ protected final void init(@Nonnull ConsoleSession session,
+ @Nonnull InputStream in,
+ @Nonnull OutputStream out) {
+ this.session = session;
+ this.in = in;
+ this.out = out;
+ }
+
+ @Override
+ public Object call() throws Exception {
+ execute();
+ in.close();
+ out.close();
+ return null;
+ }
+
+ public abstract void execute() throws Exception;
public String getName() {
return getClass().getSimpleName().toLowerCase(Locale.US);
@@ -143,9 +170,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
println("Unknown command: " + cmd + " " + args, out);
}
}
@@ -153,9 +178,7 @@ public abstract class Command {
private static final class NoOp extends Command {
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
}
}
@@ -166,9 +189,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws Exception {
+ public void execute() throws Exception {
SortedMap<String, Command> commands = Maps.newTreeMap();
SortedMap<String, Command> docCommands = Maps.newTreeMap();
Class[] classes = Command.class.getDeclaredClasses();
@@ -222,9 +243,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
println(session.getWorkingPath(), out);
}
}
@@ -236,9 +255,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
println("Good bye.", out);
}
}
@@ -250,9 +267,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
if (!PathUtils.isValid(args)) {
println("Not a valid path: " + args, out);
return;
@@ -289,9 +304,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
for (String name : session.getWorkingNode().getChildNodeNames()) {
println(name, out);
}
@@ -305,9 +318,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
println(AbstractNodeState.toString(session.getWorkingNode()), out);
}
}
@@ -319,9 +330,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
if (args.contains("auto")) {
session.setAutoRefresh(true);
println("Enabled auto refresh", out);
@@ -345,9 +354,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws Exception {
+ public void execute() throws Exception {
long time = TimeUnit.HOURS.toSeconds(1);
if (args.trim().length() > 0) {
try {
@@ -376,9 +383,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws Exception {
+ public void execute() throws Exception {
if (session.isAutoRefresh()) {
session.setAutoRefresh(false);
println("Auto refresh disabled.", out);
@@ -395,9 +400,7 @@ public abstract class Command {
}
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws Exception {
+ public void execute() throws Exception {
String langExtn;
Reader scriptReader;
if (args.startsWith("-")) {
@@ -432,24 +435,38 @@ public abstract class Command {
}
}
+ public static final class Exec extends Command {
+
+ public Exec() {
+ this.description = "Execute an operating system process.";
+ }
+
+ @Override
+ public void execute() throws Exception {
+ Process proc = new ProcessBuilder().command(args.split(" "))
+ .redirectErrorStream(true).start();
+ IOPump inPump = IOPump.start(in, proc.getOutputStream());
+ IOPump outPump = IOPump.start(proc.getInputStream(), out);
+ proc.waitFor();
+ inPump.join();
+ outPump.join();
+ }
+
+ }
+
//-----------------------< DocumentNodeStore specific >---------------------
abstract static class DocumentNodeStoreCommand extends Command {
@Override
- public void execute(@Nonnull ConsoleSession session,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ public void execute() throws IOException {
if (session.getStore() instanceof DocumentNodeStore) {
- execute(session, (DocumentNodeStore) session.getStore(), in, out);
+ execute((DocumentNodeStore) session.getStore());
} 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)
+ protected abstract void execute(@Nonnull DocumentNodeStore store)
throws IOException;
}
@@ -460,10 +477,8 @@ public abstract class Command {
}
@Override
- protected void execute(@Nonnull ConsoleSession session,
- @Nonnull DocumentNodeStore store,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ protected void execute(@Nonnull DocumentNodeStore store)
+ throws IOException {
String id = Utils.getIdFromPath(session.getWorkingPath());
NodeDocument doc = store.getDocumentStore().find(NODES, id);
if (doc == null) {
@@ -550,10 +565,8 @@ public abstract class Command {
}
@Override
- protected void execute(@Nonnull ConsoleSession session,
- @Nonnull DocumentNodeStore store,
- @Nonnull InputStream in,
- @Nonnull OutputStream out) throws IOException {
+ protected void execute(@Nonnull DocumentNodeStore store)
+ throws IOException {
Writer writer = new OutputStreamWriter(out);
String path = session.getWorkingPath();
String fromKey = Utils.getKeyLowerLimit(path);
Modified: 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=1596825&r1=1596824&r2=1596825&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Console.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/Console.java Thu May 22 12:07:03 2014
@@ -17,13 +17,25 @@
package org.apache.jackrabbit.oak.console;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
import java.io.PrintStream;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
@@ -31,6 +43,9 @@ import org.apache.jackrabbit.oak.plugins
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.mongodb.MongoClientURI;
+import com.mongodb.MongoURI;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
@@ -65,8 +80,13 @@ public class Console {
}
NodeStore store;
- if (nonOptions.get(0).startsWith("mongodb://")) {
- MongoConnection mongo = new MongoConnection(nonOptions.get(0));
+ if (nonOptions.get(0).startsWith(MongoURI.MONGODB_PREFIX)) {
+ MongoClientURI uri = new MongoClientURI(nonOptions.get(0));
+ if (uri.getDatabase() == null) {
+ System.err.println("Database missing in MongoDB URI: " + uri.getURI());
+ System.exit(1);
+ }
+ MongoConnection mongo = new MongoConnection(uri.getURI());
store = new DocumentMK.Builder().
setMongoDB(mongo.getDB()).
setClusterId(clusterId.value(options)).getNodeStore();
@@ -79,22 +99,32 @@ public class Console {
String script = eval.value(options);
if (script != null) {
Command evalCommand = new Command.Eval();
- evalCommand.init(script);
+ evalCommand.setArgs(script);
System.exit(console.run(evalCommand));
}
System.exit(console.run());
}
- private final NodeStore store;
private final InputStream in;
private final OutputStream out;
+ private final ExecutorService executor = Executors.newCachedThreadPool();
private final ConsoleSession session;
public Console(NodeStore store, InputStream in, OutputStream out) {
- this.store = store;
- this.in = in;
- this.out = out;
+ this.in = new FilterInputStream(in) {
+ @Override
+ public void close() throws IOException {
+ // do not close
+ }
+ };
+ this.out = new FilterOutputStream(out) {
+ @Override
+ public void close() throws IOException {
+ // flush but do not close
+ flush();
+ }
+ };
this.session = ConsoleSession.create(store);
}
@@ -106,29 +136,61 @@ public class Console {
if (line == null) {
break;
}
- Command c = Command.create(line);
- run(c);
- if (c.getName().equals("exit")) {
+ boolean exit = false;
+ PipedInputStream input;
+ OutputStream output = out;
+ List<Command> commands = Command.create(line);
+ Collections.reverse(commands);
+ for (Iterator<Command> it = commands.iterator(); it.hasNext(); ) {
+ Command c = it.next();
+ if (c.getName().equals("exit")) {
+ exit = true;
+ }
+ if (it.hasNext()) {
+ input = new PipedInputStream();
+ c.init(session, input, output);
+ output = new PipedOutputStream(input);
+ } else {
+ // first command -> pass an empty stream for now
+ // FIXME: find a way to read from stdin without blocking
+ c.init(session, new ByteArrayInputStream(new byte[0]), output);
+ }
+ }
+
+ List<Callable<Object>> tasks = Lists.newArrayList();
+ for (Command c : commands) {
+ tasks.add(c);
+ }
+ for (Future<Object> result : executor.invokeAll(tasks)) {
+ try {
+ result.get();
+ } catch (ExecutionException e) {
+ e.getCause().printStackTrace(new PrintStream(out, true));
+ }
+ }
+
+ if (exit) {
code = 0;
break;
}
-
}
return code;
}
public int run(Command c) {
+ c.init(session, in, out);
try {
- c.execute(session, in, out);
+ c.call();
return 0;
} catch (Exception e) {
- e.printStackTrace(new PrintStream(out));
+ e.printStackTrace(new PrintStream(out, true));
return 1;
}
}
private void prompt() throws IOException {
out.write("> ".getBytes());
+ out.flush();
}
private static String readLine(InputStream in) throws IOException {
Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/IOPump.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/IOPump.java?rev=1596825&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/IOPump.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/IOPump.java Thu May 22 12:07:03 2014
@@ -0,0 +1,58 @@
+/*
+ * 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.InputStream;
+import java.io.OutputStream;
+
+import org.apache.commons.io.IOUtils;
+
+import com.google.common.io.ByteStreams;
+
+/**
+* Copies bytes from the input to the output stream and closes both of them.
+*/
+final class IOPump {
+
+ private final Thread t;
+
+ private IOPump(final InputStream in, final OutputStream out) {
+ this.t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ByteStreams.copy(in, out);
+ } catch (Exception e) {
+ // ignore
+ }
+ IOUtils.closeQuietly(in);
+ IOUtils.closeQuietly(out);
+ }
+ }, "IO Pump Thread");
+ t.setDaemon(true);
+ }
+
+ static IOPump start(InputStream in, OutputStream out) {
+ IOPump p = new IOPump(in, out);
+ p.t.start();
+ return p;
+ }
+
+ void join() throws InterruptedException {
+ t.join();
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/console/IOPump.java
------------------------------------------------------------------------------
svn:eol-style = native