path) {
- this.commandTable = commandTable;
- this.path = path;
- setSettings(s);
+ //add built-in commands
}
- private CommandTable commandTable;
-
/**
- * @return the CommandTable for this shell.
+ * Add new command handler.
+ * @param handler object with methods annotated with @Commands
*/
- public CommandTable getCommandTable() {
- return commandTable;
+ public void addHandler(Object handler) {
+ addHandler(handler, null);
}
- private OutputConversionEngine outputConverter = new OutputConversionEngine();
-
/**
- * Call this method to get OutputConversionEngine used by the Shell.
+ * Add new command handler and all methods in this handler will be prefixed with provided prefix.
+ *
+ * For example, instance of this class:
+ *
+ * class Foo {
*
- * @return a conversion engine.
- */
- public OutputConversionEngine getOutputConverter() {
- return outputConverter;
- }
-
- private InputConversionEngine inputConverter = new InputConversionEngine();
-
- /**
- * Call this method to get InputConversionEngine used by the Shell.
+ * {@literal @}Command(name="hello")
+ * public String command() {
+ * return "Hello world!"
+ * }
+ * }
*
- * @return a conversion engine.
- */
- public InputConversionEngine getInputConverter() {
- return inputConverter;
- }
-
- private MultiMap auxHandlers = new ArrayHashMultiMap();
- private List
*
- * @param handler Object which should be registered as handler.
- * @param prefix Prefix that should be prepended to all handler's command names.
- * @see asg.cliche.ShellDependent
- * @see asg.cliche.ShellManageable
- */
- public void addMainHandler(Object handler, String prefix) {
- if (handler == null) {
- throw new NullPointerException();
- }
- allHandlers.add(handler);
-
- addDeclaredMethods(handler, prefix);
- inputConverter.addDeclaredConverters(handler);
- outputConverter.addDeclaredConverters(handler);
-
- if (handler instanceof ShellDependent) {
- ((ShellDependent) handler).cliSetShell(this);
- }
- }
-
- /**
- * This method is very similar to addMainHandler, except ShellFactory
- * will pass all handlers registered with this method to all this shell's subshells.
+ * Will be translated to command '!hello'
+ *
+ * Mostly usable to specific commands set like help and so on.
*
- * @param handler Object which should be registered as handler.
- * @param prefix Prefix that should be prepended to all handler's command names.
- * @see asg.cliche.Shell#addMainHandler(java.lang.Object, java.lang.String)
+ * @param handler object with methods annotated with @Commands
+ * @param prefix prefix would be used for any command name from this handler
*/
- public void addAuxHandler(Object handler, String prefix) {
- if (handler == null) {
- throw new NullPointerException();
- }
- auxHandlers.put(prefix, handler);
- allHandlers.add(handler);
+ public void addHandler(Object handler, @Nullable String prefix) {
+ Preconditions.checkNotNull(handler, "Handler instance can't be null");
+ //TODO
- addDeclaredMethods(handler, prefix);
- inputConverter.addDeclaredConverters(handler);
- outputConverter.addDeclaredConverters(handler);
-
- if (handler instanceof ShellDependent) {
- ((ShellDependent) handler).cliSetShell(this);
- }
}
- private void addDeclaredMethods(Object handler, String prefix) throws SecurityException {
- for (Method m : handler.getClass().getMethods()) {
- Command annotation = m.getAnnotation(Command.class);
- if (annotation != null) {
- commandTable.addMethod(m, handler, prefix);
- }
- }
+ public void addInputConverter(Class> classType, InputConverter inputConverter) {
+ inputConverters.put(classType, inputConverter);
}
- private Throwable lastException = null;
-
- /**
- * Returns last thrown exception
- */
- @Command(description = "Returns last thrown exception") // Shell is self-manageable, isn't it?
- public Throwable getLastException() {
- return lastException;
+ public void addOutputConverter(Class> classType, OutputConverter outputConverter) {
+ outputConverters.put(classType, outputConverter);
}
- private List path;
-
/**
- * @return list of path elements, as it was passed in constructor
+ * Process command line through ConsoleIO.
+ * @param commandLine command line line to be processed.
*/
- public List getPath() {
- return path;
- }
-
- /**
- * Runs the command session.
- * Create the Shell, then run this method to listen to the user,
- * and the Shell will invoke Handler's methods.
- *
- * @throws java.io.IOException when can't readLine() from input.
- */
- public void commandLoop() throws IOException {
- for (Object handler : allHandlers) {
- if (handler instanceof ShellManageable) {
- ((ShellManageable) handler).cliEnterLoop();
- }
- }
- output.output(appName, outputConverter);
- String command = "";
- while (!command.trim().equals("exit")) {
- try {
- command = input.readCommand(path);
- if (!Strings.isNullOrEmpty(command)) {
- processLine(command);
- }
- } catch (TokenException te) {
- lastException = te;
- output.outputException(command, te);
- } catch (CLIException clie) {
- lastException = clie;
- if (!command.trim().equals("exit")) {
- output.outputException(clie);
- }
- }
- }
- for (Object handler : allHandlers) {
- if (handler instanceof ShellManageable) {
- ((ShellManageable) handler).cliLeaveLoop();
- }
+ public void processCommand(String commandLine) {
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(commandLine), "Command should not be null");
+ //parse commandLine
+ String[] commandsInLine = commandLine.split(COMMAND_UNION_SIGN);
+ for (String basicCommand : commandsInLine) {
+ processCommand0(basicCommand);
}
}
- private void outputHeader(String header, Object[] parameters) {
- if (header == null || header.isEmpty()) {
- output.outputHeader(null);
- } else {
- output.outputHeader(String.format(header, parameters));
+ @VisibleForTesting
+ void processCommand0(String command) {
+ List tokens = Token.tokenize(command);
+ ShellCommand shellCommand = lookupCommand(command);
+ Class>[] parameterTypes = shellCommand.getMethod().getParameterTypes();
+ Object[] parameters = new Object[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ Class> parameterType = parameterTypes[i];
+ String parameterString = tokens.get(i).getString();
}
- }
-
- private static final String HINT_FORMAT = "This is %1$s, running on Cliche Shell\n" +
- "For more information on the Shell, enter ?help";
+ for (Class> parameterType : parameterTypes) {
+ synchronized (inputConverters) {
- /**
- * You can operate Shell linewise, without entering the command loop.
- * All output is directed to shell's Output.
- *
- * @param line Full command line
- * @throws asg.cliche.CLIException This may be TokenException
- * @see asg.cliche.Output
- */
- public void processLine(String line) throws CLIException {
- if (line.trim().equals("?")) {
- output.output(String.format(HINT_FORMAT, appName), outputConverter);
- } else {
- List tokens = Token.tokenize(line);
- if (tokens.size() > 0) {
- String discriminator = tokens.get(0).getString();
- processCommand(discriminator, tokens);
}
}
- }
-
- private void processCommand(String discriminator, List tokens) throws CLIException {
- assert discriminator != null;
- assert !discriminator.equals("");
-
- ShellCommand commandToInvoke = commandTable.lookupCommand(discriminator, tokens);
+ Class[] paramClasses = parameterTypes;
+ Object[] parameters = new Object[paramClasses.length];
- Class[] paramClasses = commandToInvoke.getMethod().getParameterTypes();
Object[] parameters = inputConverter.convertToParameters(tokens, paramClasses,
commandToInvoke.getMethod().isVarArgs());
-
- outputHeader(commandToInvoke.getHeader(), parameters);
-
- long timeBefore = Calendar.getInstance().getTimeInMillis();
- Object invocationResult = commandToInvoke.invoke(parameters);
- long timeAfter = Calendar.getInstance().getTimeInMillis();
+ Object result = commandToInvoke.invoke(parameters);
if (invocationResult != null) {
- output.output(invocationResult, outputConverter);
- }
- if (displayTime) {
- final long time = timeAfter - timeBefore;
- if (time != 0L) {
- output.output(String.format(TIME_MS_FORMAT_STRING, time), outputConverter);
- }
+ consoleIO.output();
}
- }
-
- private static final String TIME_MS_FORMAT_STRING = "time: %d ms";
- private boolean displayTime = false;
-
- /**
- * Turns command execution time display on and off
- *
- * @param displayTime true if do display, false otherwise
- */
- @Command(description = "Turns command execution time display on and off")
- public void setDisplayTime(
- @Param(name = "do-display-time", description = "true if do display, false otherwise")
- boolean displayTime) {
- this.displayTime = displayTime;
}
+ private ShellCommand lookupCommand(String command) {
+ List tokens = Token.tokenize(command);
+ String discriminator = tokens.get(0).getString();
+ }
/**
- * Hint is some text displayed before the command loop and every time user enters "?".
+ * Runs the command session.
+ * Create the Shell, then run this method to listen to the user,
+ * and the Shell will invoke Handler's methods.
+ *
+ * @throws java.io.IOException when can't readLine() from input.
*/
- public void setAppName(String appName) {
- this.appName = appName;
- }
-
- public String getAppName() {
- return appName;
+ public void commandLoop() throws IOException {
+ String command;
+ do {
+ command = consoleIO.readCommand(prompt);
+ if (!Strings.isNullOrEmpty(command)) {
+ List tokens = ;
+ if (tokens.size() > 0) {
+ String discriminator = tokens.get(0).getString();
+ try {
+ processCommand(discriminator, tokens);
+ } catch (CLIException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ } while();
}
-
}
diff --git a/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellCommandParamSpec.java b/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellCommandParamSpec.java
index 6f299fd..b4d9d0e 100644
--- a/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellCommandParamSpec.java
+++ b/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellCommandParamSpec.java
@@ -24,7 +24,6 @@ static ShellCommandParamSpec[] forMethod(Method theMethod) {
}
}
if (paramAnnotation != null) {
- assert !paramAnnotation.name().isEmpty() : "@Param.name mustn\'t be empty";
result[i] = new ShellCommandParamSpec(paramAnnotation.name(), paramTypes[i],
paramAnnotation.description(), i);
} else {
diff --git a/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellFactory.java b/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellFactory.java
index 72ce398..611d9bd 100644
--- a/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellFactory.java
+++ b/cliche-shell-core/src/main/java/com/maxifier/cliche/ShellFactory.java
@@ -45,7 +45,7 @@ private ShellFactory() {
* @return Shell that can be either further customized or run directly by calling commandLoop().
*/
public static Shell createConsoleShell(String prompt, String appName, Object... handlers) throws IOException {
- ConsoleIO io = new ConsoleIO();
+ ConsoleIO io = new JLineConsoleIO();
List path = new ArrayList(1);
path.add(prompt);
@@ -83,7 +83,7 @@ public void run() {
try {
PrintStream out = new PrintStream(socket.getOutputStream());
ConsoleReader consoleReader = new ConsoleReader(socket.getInputStream(), out);
- ConsoleIO io = new ConsoleIO(consoleReader, out, out);
+ ConsoleIO io = new JLineConsoleIO(consoleReader, out, out);
List path = new ArrayList(1);
path.add(promt);
@@ -160,7 +160,7 @@ public Command create() {
*/
public static Shell createConsoleShell(String prompt, String appName, Object mainHandler,
MultiMap auxHandlers) throws IOException {
- ConsoleIO io = new ConsoleIO();
+ ConsoleIO io = new JLineConsoleIO();
List path = new ArrayList(1);
path.add(prompt);
diff --git a/cliche-shell-core/src/main/java/com/maxifier/cliche/SshShellCommand.java b/cliche-shell-core/src/main/java/com/maxifier/cliche/SshShellCommand.java
index e036de7..d0a2b83 100644
--- a/cliche-shell-core/src/main/java/com/maxifier/cliche/SshShellCommand.java
+++ b/cliche-shell-core/src/main/java/com/maxifier/cliche/SshShellCommand.java
@@ -27,7 +27,7 @@
/**
* @author aleksey.didik@maxifier.com Aleksey Didik
*/
-class SshShellCommand implements Command {
+public class SshShellCommand implements Command {
private final ExecutorService executor;
private final String promt;
@@ -35,14 +35,15 @@ class SshShellCommand implements Command {
private final Collection> handlersCollection;
private InputStream in;
private PrintStream out;
- private PrintStream err;
private ExitCallback callback;
private Future future;
private ConsoleReader consoleReader;
private Shell theShell;
- public SshShellCommand(ExecutorService executor, String promt, String appName, Collection> handlersCollection) {
- //To change body of created methods use File | Settings | File Templates.
+ public SshShellCommand(ExecutorService executor,
+ String promt,
+ String appName,
+ Collection> handlersCollection) {
this.executor = executor;
this.promt = promt;
this.appName = appName;
@@ -81,12 +82,11 @@ public void run() {
consoleReader.setHistoryEnabled(true);
consoleReader.setExpandEvents(false);
- ConsoleIO io = new ConsoleIO(consoleReader, out, out);
+ ConsoleIO io = new JLineConsoleIO(consoleReader, out, out);
List path = new ArrayList(1);
path.add(promt);
- theShell = new Shell(new Shell.Settings(io, io, new EmptyMultiMap(), false),
+ theShell = new Shell(new Shell.Settings(io, io, new EmptyMultiMap()),
new CommandTable(new DashJoinedNamer(true)), path);
- theShell.setAppName(appName);
theShell.addMainHandler(theShell, "!");
theShell.addMainHandler(new HelpCommandHandler(), "?");
for (Object h : handlersCollection) {
diff --git a/cliche-shell-core/src/main/java/com/maxifier/cliche/Token.java b/cliche-shell-core/src/main/java/com/maxifier/cliche/Token.java
index 3fa0ab9..73319b6 100644
--- a/cliche-shell-core/src/main/java/com/maxifier/cliche/Token.java
+++ b/cliche-shell-core/src/main/java/com/maxifier/cliche/Token.java
@@ -73,9 +73,6 @@ public int hashCode() {
*
* @param input String to be tokenized
* @return List of tokens
- *
- * @see asg.cliche.Shell.Token
- * @see asg.cliche.Shell.escapeString
*/
/*package-private for tests*/
static List tokenize(final String input) {
diff --git a/cliche-shell-guice/src/main/java/com/maxifier/cliche/guice/SshShellModule.java b/cliche-shell-guice/src/main/java/com/maxifier/cliche/guice/SshShellModule.java
index 53b870a..d236873 100644
--- a/cliche-shell-guice/src/main/java/com/maxifier/cliche/guice/SshShellModule.java
+++ b/cliche-shell-guice/src/main/java/com/maxifier/cliche/guice/SshShellModule.java
@@ -1,13 +1,17 @@
package com.maxifier.cliche.guice;
-import com.maxifier.cliche.ShellFactory;
-
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
+import com.maxifier.cliche.ShellFactory;
+import com.maxifier.cliche.SshShellCommand;
import org.apache.sshd.SshServer;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
@@ -15,6 +19,8 @@
import java.io.IOException;
import java.security.PublicKey;
import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/**
* @author aleksey.didik@maxifier.com (Aleksey Didik)
@@ -33,27 +39,41 @@ public SshShellModule(String promt, String appName, int port) {
@Override
protected void configure() {
- SshServer sshServer = SshServer.setUpDefaultServer();
+ final SshServer sshServer = SshServer.setUpDefaultServer();
+
sshServer.setPublickeyAuthenticator(new PublickeyAuthenticator() {
@Override
public boolean authenticate(String username, PublicKey key, ServerSession session) {
return true;
}
});
- // sshServer.setPasswordAuthenticator(new PasswordAuthenticator() {
- // @Override
- // public boolean authenticate(String username, String password, ServerSession session) {
- // return true; //To change body of implemented methods use File | Settings | File Templates.
- // }
- // });
+ sshServer.setPasswordAuthenticator(new PasswordAuthenticator() {
+ @Override
+ public boolean authenticate(String username, String password, ServerSession session) {
+ return true;
+ }
+ });
sshServer.setPort(port);
sshServer.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser"));
+
+
final Collection shellHandlers = ShellFactory.createSshShell(sshServer, promt, appName);
+
+ sshServer.setCommandFactory(new CommandFactory() {
+
+ @Override
+ public Command createCommand(String command) {
+ final ExecutorService executor = Executors.newCachedThreadPool();
+ return new SshShellCommand(executor, promt, appName, shellHandlers);
+ }
+ });
+
try {
sshServer.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
+
bindListener(AnnotatedClassMatcher.with(CommandHandler.class),
new TypeListener() {
@Override
@@ -67,6 +87,7 @@ public void afterInjection(I injectee) {
}
}
);
+
}
diff --git a/cliche-shell-guice/src/test/java/com/maxifier/cliche/guice/SshShellModuleTest.java b/cliche-shell-guice/src/test/java/com/maxifier/cliche/guice/SshShellModuleTest.java
index 79fdeb6..f52a8b4 100644
--- a/cliche-shell-guice/src/test/java/com/maxifier/cliche/guice/SshShellModuleTest.java
+++ b/cliche-shell-guice/src/test/java/com/maxifier/cliche/guice/SshShellModuleTest.java
@@ -1,5 +1,8 @@
package com.maxifier.cliche.guice;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
import com.maxifier.cliche.Command;
import com.brsanthu.dataexporter.model.AlignType;
@@ -17,15 +20,15 @@ public class SshShellModuleTest {
public static void main(String[] args) throws IOException, InterruptedException {
System.out.println(new Foo().table());
-// Injector injector = Guice.createInjector(
-// new SshShellModule("test", "Test App", 12891),
-// new AbstractModule() {
-// @Override
-// protected void configure() {
-// bind(Foo.class).asEagerSingleton();
-// }
-// });
-// Thread.sleep(500000);
+ Injector injector = Guice.createInjector(
+ new SshShellModule("test", "Test App", 12891),
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Foo.class).asEagerSingleton();
+ }
+ });
+ Thread.sleep(500000);
}
diff --git a/pom.xml b/pom.xml
index 85eb594..f1a4732 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,13 +93,17 @@
data-exporter
1.0.2
-
-
+
+ com.google.code.findbugs
+ jsr305
+ 1.3.9
+
-
+
+
junit
junit
4.10