changeset 262:8bd725ac3bdf

Implement interactive Thermostat shell. Reviewed-by: neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-April/001053.html PR 930
author Roman Kennke <rkennke@redhat.com>
date Tue, 24 Apr 2012 14:14:13 +0200
parents 9c2e7e00665c
children 9a1a69250ee2
files common/src/main/java/com/redhat/thermostat/test/ExceptionThrowingInputStream.java common/src/main/java/com/redhat/thermostat/test/TestCommandContextFactory.java tools/pom.xml tools/src/main/java/com/redhat/thermostat/tools/cli/ShellCommand.java tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command tools/src/test/java/com/redhat/thermostat/tools/cli/ShellCommandTest.java tools/src/test/java/com/redhat/thermostat/tools/cli/TestTerminal.java
diffstat 7 files changed, 408 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/com/redhat/thermostat/test/ExceptionThrowingInputStream.java	Tue Apr 24 14:14:13 2012 +0200
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+class ExceptionThrowingInputStream extends InputStream {
+
+    private InputStream in = new ByteArrayInputStream(new byte[0]);
+    private IOException exception;
+
+    @Override
+    public int read() throws IOException {
+        throwExceptionIfNecessary();
+        return in.read();
+    }
+
+    private void throwExceptionIfNecessary() throws IOException {
+        if (exception != null) {
+            throw exception;
+        }
+    }
+
+    public void setInput(String input) {
+        in = new ByteArrayInputStream(input.getBytes());
+    }
+
+    public void setException(IOException ex) {
+        exception = ex;
+    }
+
+}
--- a/common/src/main/java/com/redhat/thermostat/test/TestCommandContextFactory.java	Mon Apr 23 12:22:09 2012 +0200
+++ b/common/src/main/java/com/redhat/thermostat/test/TestCommandContextFactory.java	Tue Apr 24 14:14:13 2012 +0200
@@ -36,8 +36,8 @@
 
 package com.redhat.thermostat.test;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
 
@@ -52,7 +52,7 @@
 
     private ByteArrayOutputStream out;
     private ByteArrayOutputStream err;
-    private ByteArrayInputStream in;
+    private ExceptionThrowingInputStream in;
 
     public TestCommandContextFactory() {
         reset();
@@ -130,8 +130,8 @@
         return new String(out.toByteArray());
     }
 
-    void setInput(String input) {
-        in = new ByteArrayInputStream(input.getBytes());
+    public void setInput(String input) {
+        in.setInput(input);
     }
 
     public Object getError() {
@@ -141,11 +141,15 @@
     public void reset() {
         out = new ByteArrayOutputStream();
         err = new ByteArrayOutputStream();
-        in = new ByteArrayInputStream(new byte[0]);
+        in = new ExceptionThrowingInputStream();
         console = new TestConsole();
     }
 
     public Console getConsole() {
         return console;
     }
+
+    public void setInputThrowsException(IOException ex) {
+        in.setException(ex);
+    }
 }
--- a/tools/pom.xml	Mon Apr 23 12:22:09 2012 +0200
+++ b/tools/pom.xml	Tue Apr 24 14:14:13 2012 +0200
@@ -74,6 +74,11 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>jline</groupId>
+      <artifactId>jline</artifactId>
+      <version>2.6</version>
+    </dependency>
+    <dependency>
       <groupId>com.redhat.thermostat</groupId>
       <artifactId>thermostat-common</artifactId>
       <version>${project.version}</version>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/main/java/com/redhat/thermostat/tools/cli/ShellCommand.java	Tue Apr 24 14:14:13 2012 +0200
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.tools.cli;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+
+import jline.Terminal;
+import jline.TerminalFactory;
+import jline.console.ConsoleReader;
+
+import com.redhat.thermostat.cli.ArgumentSpec;
+import com.redhat.thermostat.cli.Command;
+import com.redhat.thermostat.cli.CommandContext;
+import com.redhat.thermostat.cli.CommandException;
+import com.redhat.thermostat.cli.Launcher;
+
+public class ShellCommand implements Command {
+
+    private static final String NAME = "shell";
+
+    private static final String DESCRIPTION = "launches the Thermostat interactive shell";
+
+    private static final String USAGE = DESCRIPTION;
+
+    private static final String PROMPT = "Thermostat > ";
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        Terminal term = TerminalFactory.create();
+        try {
+            shellMainLoop(ctx, term);
+        } catch (IOException ex) {
+            throw new CommandException(ex);
+        } finally {
+            closeTerminal(term);
+        }
+    }
+
+    private void closeTerminal(Terminal term) throws CommandException {
+        try {
+            term.restore();
+        } catch (Exception e) {
+            System.err.println("restore kaputted");
+            throw new CommandException(e);
+        }
+    }
+
+    private void shellMainLoop(CommandContext ctx, Terminal term) throws IOException {
+        ConsoleReader reader = new ConsoleReader("fluff", ctx.getConsole().getInput(), ctx.getConsole().getOutput(), term);
+        while (handleConsoleInput(reader));
+    }
+
+    private boolean handleConsoleInput(ConsoleReader reader) throws IOException {
+        String line = reader.readLine(PROMPT).trim();
+        if (line.equals("")) {
+            return true;
+        } else if (line.equals("exit")) {
+            return false;
+        } else {
+            launchCommand(line);
+            return true;
+        }
+    }
+
+    private void launchCommand(String line) {
+        String[] parsed = line.split(" ");
+        Launcher launcher = new Launcher();
+        launcher.run(parsed);
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public String getDescription() {
+        return DESCRIPTION;
+    }
+
+    @Override
+    public String getUsage() {
+        return USAGE;
+    }
+
+    @Override
+    public Collection<ArgumentSpec> getAcceptedArguments() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean isStorageRequired() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+}
--- a/tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command	Mon Apr 23 12:22:09 2012 +0200
+++ b/tools/src/main/resources/META-INF/services/com.redhat.thermostat.cli.Command	Tue Apr 24 14:14:13 2012 +0200
@@ -3,3 +3,4 @@
 com.redhat.thermostat.tools.db.DBService
 com.redhat.thermostat.agent.AgentApplication
 com.redhat.thermostat.tools.cli.ListVMsCommand
+com.redhat.thermostat.tools.cli.ShellCommand
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/test/java/com/redhat/thermostat/tools/cli/ShellCommandTest.java	Tue Apr 24 14:14:13 2012 +0200
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.tools.cli;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import jline.TerminalFactory;
+import jline.TerminalFactory.Flavor;
+import jline.TerminalFactory.Type;
+import jline.UnixTerminal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.cli.Arguments;
+import com.redhat.thermostat.cli.CommandContext;
+import com.redhat.thermostat.cli.CommandException;
+import com.redhat.thermostat.cli.SimpleArguments;
+import com.redhat.thermostat.test.TestCommandContextFactory;
+
+public class ShellCommandTest {
+
+    private ShellCommand cmd;
+
+    @Before
+    public void setUp() {
+        cmd = new ShellCommand();
+    }
+
+    @After
+    public void tearDown() {
+        cmd = null;
+        TerminalFactory.registerFlavor(Flavor.UNIX, UnixTerminal.class);
+        TerminalFactory.reset();
+    }
+
+    @Test
+    public void testBasic() throws CommandException {
+        TestCommandContextFactory ctxFactory = new TestCommandContextFactory();
+        ctxFactory.setInput("help\nexit\n");
+        Arguments args = new SimpleArguments();
+        CommandContext ctx = ctxFactory.createContext(args);
+        cmd.run(ctx);
+        assertEquals("Thermostat > help\nThermostat > exit\n", ctxFactory.getOutput());
+    }
+
+    @Test
+    public void testDoNothingWithoutInput() throws CommandException {
+        TestCommandContextFactory ctxFactory = new TestCommandContextFactory();
+        ctxFactory.setInput("\nexit\n");
+        Arguments args = new SimpleArguments();
+        CommandContext ctx = ctxFactory.createContext(args);
+        cmd.run(ctx);
+        assertEquals("Thermostat > \nThermostat > exit\n", ctxFactory.getOutput());
+    }
+
+    @Test(expected=CommandException.class)
+    public void testIOException() throws CommandException {
+        TestCommandContextFactory ctxFactory = new TestCommandContextFactory();
+        ctxFactory.setInputThrowsException(new IOException());
+        Arguments args = new SimpleArguments();
+        CommandContext ctx = ctxFactory.createContext(args);
+        cmd.run(ctx);
+    }
+
+    @Test(expected=CommandException.class)
+    public void testTerminalRestoreException() throws CommandException {
+        TerminalFactory.configure(Type.UNIX);
+        TerminalFactory.registerFlavor(Flavor.UNIX, TestTerminal.class);
+        TestCommandContextFactory ctxFactory = new TestCommandContextFactory();
+        ctxFactory.setInputThrowsException(new IOException());
+        Arguments args = new SimpleArguments();
+        CommandContext ctx = ctxFactory.createContext(args);
+        cmd.run(ctx);
+    }
+
+    @Test
+    public void testName() {
+        assertEquals("shell", cmd.getName());
+    }
+
+    @Test
+    public void testDescription() {
+        assertEquals("launches the Thermostat interactive shell", cmd.getDescription());
+    }
+
+    @Test
+    public void testUsage() {
+        assertEquals("launches the Thermostat interactive shell", cmd.getUsage());
+    }
+
+    @Test
+    public void testAcceptedArguments() {
+        assertEquals(Collections.EMPTY_LIST, cmd.getAcceptedArguments());
+    }
+
+    @Test
+    public void testStorageRequired() {
+        assertFalse(cmd.isStorageRequired());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/src/test/java/com/redhat/thermostat/tools/cli/TestTerminal.java	Tue Apr 24 14:14:13 2012 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.tools.cli;
+
+import jline.UnixTerminal;
+
+public class TestTerminal extends UnixTerminal {
+
+    public TestTerminal() throws Exception {
+        super();
+    }
+
+    @Override
+    public void restore() throws Exception {
+        throw new Exception();
+    }
+
+}