/*
* Copyright 2008 Hippo.
*
* Licensed 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.hippoecm.tools.cli;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import jline.ArgumentCompletor;
import jline.Completor;
import jline.ConsoleReader;
import jline.History;
import jline.MultiCompletor;
import jline.SimpleCompletor;
/**
* Manage the terminal (with jline).
*/
public class Terminal {
/**
* Displayed when the program first starts. Provides for customization
* option.
*/
private String commandLineHeader = null;
/**
* Provides an unchanging prompt for each line of input. Provides for
* customization option.
*/
private String commandLinePrompt = "> ";
/**
* Displayed when the program first starts. Provides for customization
* option.
*/
private String commandLineVersion = null;
/**
* Jline history file name.
*/
private static final String HISTORYFILE = ".javaterm"; // history file in user's
/**
* Jline history file.
*/
private String historyFile;
/**
* The console reader from jline.
*/
private static ConsoleReader consoleReader;
/**
* Main entry point. The first argument can be a filename with an
* application initialization file.
* @throws IOException when the interaction with the shell fails
*/
public final void init() throws IOException {
String line;
historyFile = System.getProperty("user.home") + File.separator + HISTORYFILE;
consoleReader = new ConsoleReader();
consoleReader.setHistory(new History(new File(historyFile)));
// set completer with list of words
MultiCompletor commandComp = new MultiCompletor(new Completor[] {
new SimpleCompletor(CommandHelper.getCommandsAsArray()),
new SimpleCompletor(CommandHelper.getAliasesAsArray()) });
MultiCompletor paramComp = new MultiCompletor(new Completor[] { new NodeNameCompletor(),
new PropertyNameCompletor() });
Completor[] comp = new Completor[] { commandComp, paramComp };
consoleReader.addCompletor(new ArgumentCompletor(comp));
if (getCommandLineHeader() != null) {
Terminal.println(getCommandLineHeader());
}
if (getCommandLineVersion() != null) {
Terminal.println(getCommandLineVersion());
}
Terminal.println("exit or quit leaves program.");
Terminal.println("help lists commands.");
boolean keepRunning = true;
// main input loop
while (keepRunning) {
try {
line = consoleReader.readLine(getCommandLinePrompt());
if (line != null) {
handleCommand(line, consoleReader);
} else {
// Ctrl-D, do proper exit
handleCommand("exit", consoleReader);
}
} catch (JcrShellShutdownException e) {
// thrown by exit command
keepRunning = false;
} catch (java.io.EOFException eof) {
keepRunning = false;
} catch (UnsupportedEncodingException enc) {
enc.printStackTrace(System.err);
} catch (IOException ioe) {
ioe.printStackTrace(System.err);
}
}
// cleanup
JcrWrapper.logout();
// clear line
consoleReader.printString("Bye bye!");
}
/**
* Parse and handle command line.
* @param line the command line
* @param cr the console reader (jline)
* @return true if the command line was succesful handled and executed
* @throws IOException when the interaction with the shell fails
*/
private boolean handleCommand(final String line, final ConsoleReader cr) throws IOException {
long tickStart = System.currentTimeMillis();
boolean retValue = true;
String cmdLine = line.trim();
// # = comment
if (cmdLine.length() == 0) {
return true;
}
if (cmdLine.startsWith("#")) {
return true;
}
String[] args = tokenizeCommand(cmdLine);
// white space
if (args.length == 0) {
return true;
}
String cmd = args[0].trim().toLowerCase();
if (CommandHelper.isAlias(cmd)) {
cmd = CommandHelper.getCommandForAlias(cmd);
}
if (!CommandHelper.isCommand(cmd)) {
Terminal.println("Unknown command: " + cmd);
return false;
}
String classToInvoke = CommandHelper.getClassForCommand(cmd);
try {
retValue = ((Boolean) CommandHelper.runMethod("execute", classToInvoke, args)).booleanValue();
} catch (Exception e) {
// shutdown 'exception'
if (e instanceof JcrShellShutdownException) {
throw new JcrShellShutdownException();
}
e.printStackTrace();
Terminal.println("Error Running: [" + classToInvoke + "] with [" + Arrays.toString(args) + "]");
}
Terminal.println(" completed: " + (System.currentTimeMillis() - tickStart) + " msecs");
return retValue;
}
/**
* Tokenize the command line on whitespace.
* @param line commandline
* @return String array with tokens
*/
private String[] tokenizeCommand(final String line) {
String[] tokens = line.split("\\s+");
return tokens;
}
/**
* Getter for commandLineHeader variable.
* @return the command line header.
*/
public final String getCommandLineHeader() {
return commandLineHeader;
}
/**
* Getter for commandLinePrompt variable.
* @return the command line prompt.
*/
public final String getCommandLinePrompt() {
return commandLinePrompt;
}
/**
* Getter for commandLineVersion variable.
* @return the command line version.
*/
public final String getCommandLineVersion() {
return commandLineVersion;
}
/**
* Setter for the commandLineHeader variable.
* @param string The command line header.
*/
public final void setCommandLineHeader(final String string) {
commandLineHeader = string;
}
/**
* Setter for the commandLinePrompt variable.
* @param string The prompt.
*/
public final void setCommandLinePrompt(final String string) {
commandLinePrompt = string;
}
/**
* Setter for the commandLineVersion variable.
* @param string The version.
*/
public final void setCommandLineVersion(final String string) {
commandLineVersion = string;
}
/**
* Get the custom shutdown hook.
* @return the shutdown hook
*/
public final ShutdownHook getShutdownHook() {
return new ShutdownHook();
}
/**
* Print string to console, fall back to System.out.
* @param str string to print
*/
public static final void print(final String str) {
try {
consoleReader.printString(str);
} catch (IOException e) {
// ugh..
System.out.print(str);
}
}
/**
* Read password from terminal with masking.
* @return the entered password
*/
public static final String getPassword() {
try {
return consoleReader.readLine("password: ", '*');
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* Print string to console, fall back to System.out.
* @param str string to print
*/
public static final void println(final String str) {
try {
consoleReader.printString(str);
consoleReader.printNewline();
} catch (IOException e) {
// ugh..
System.out.println(str);
}
}
/**
* Print new line to console, fall back to System.out.
*/
public static final void newLine() {
println("");
}
/**
* Trivial shutdown hook class.
*/
static class ShutdownHook extends Thread {
/**
* Exit properly on shutdown.
*/
public void run() {
try {
Terminal.newLine();
Terminal.print("Shuting down..");
JcrWrapper.logout();
Terminal.println("done.");
} catch (JcrShellShutdownException e) {
// ignore, expected, thrown by exit command
}
}
}
}