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