package com.tinkerpop.rexster.console; import com.tinkerpop.pipes.util.iterators.SingleIterator; import com.tinkerpop.rexster.Tokens; import com.tinkerpop.rexster.client.RemoteRexsterSession; import com.tinkerpop.rexster.protocol.msg.*; import jline.ConsoleReader; import jline.History; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; public class RexsterConsole { static { // disable all logging in console. final List<Logger> loggers = Collections.<Logger>list(LogManager.getCurrentLoggers()); loggers.add(LogManager.getRootLogger()); for ( Logger logger : loggers ) { logger.setLevel(Level.OFF); } } private RemoteRexsterSession session = null; private List<String> currentBindings = new ArrayList<String>(); private final PrintStream output = System.out; private final ConsoleSettings settings; private static final String REXSTER_HISTORY = ".rexster_history"; public RexsterConsole(final ConsoleSettings settings) { this.settings = settings; } public void start() throws Exception { if (this.settings.isExecuteMode()) { oneTimeExecuteScript(readFile(this.settings.getFileToExecute())); } else { this.writeAsciiArt(); this.acceptReplCommands(); } } private void writeAsciiArt() { this.output.println(" (l_(l"); this.output.println("(_______( 0 0"); this.output.println("( (-Y-) <woof>"); this.output.println("l l-----l l"); this.output.println("l l,, l l,,"); } private void acceptReplCommands() throws Exception { this.output.println("opening session " + this.settings.getHostPort()); this.initAndOpenSessionFromSettings(); if (this.session.isOpen()) { this.output.println("?h for help"); this.primaryLoop(); } else { this.output.println("could not connect to the Rexster server"); } } private void oneTimeExecuteScript(final String script) { this.initAndOpenSessionFromSettings(); if (!this.session.isOpen()) { this.output.println("could not connect to the Rexster server"); } else { this.executeScript(script, false); } this.closeConsole(); } private void initAndOpenSessionFromSettings() { this.session = new RemoteRexsterSession(this.settings.getHost(), this.settings.getPort(), this.settings.getTimeout(), this.settings.getUsername(), this.settings.getPassword()); this.session.open(); } private void primaryLoop() throws Exception { final ConsoleReader reader = getInputReader(); String line = ""; this.output.println(); while (line != null) { try { line = ""; boolean submit = false; boolean newline = false; while (!submit) { if (newline) line = line + "\n" + reader.readLine(RexsterConsole.makeSpace(this.getPrompt().length())); else line = line + "\n" + reader.readLine(this.getPrompt()); if (line.endsWith(" .")) { newline = true; line = line.substring(0, line.length() - 2); } else { line = line.trim(); submit = true; } } if (line.isEmpty()) continue; if (line.equals(Tokens.REXSTER_CONSOLE_QUIT)) { this.closeConsole(); return; } else if (line.equals(Tokens.REXSTER_CONSOLE_HELP)) { this.printHelp(); } else if (line.equals(Tokens.REXSTER_CONSOLE_BINDINGS)) { this.printBindings(); } else if (line.equals(Tokens.REXSTER_CONSOLE_RESET)) { this.resetSessionWithRexster(); } else if (line.startsWith(Tokens.REXSTER_CONSOLE_EXECUTE)) { final String fileToExecute = line.substring(Tokens.REXSTER_CONSOLE_EXECUTE.length()).trim(); if (fileToExecute.isEmpty()) { this.output.println("specify the file to execute"); } else { try { this.executeScript(readFile(fileToExecute)); } catch (IOException ioe) { this.output.println("could not read the file specified"); } } } else if (line.equals(Tokens.REXSTER_CONSOLE_LANGUAGES)) { this.printAvailableLanguages(); } else if (line.startsWith(Tokens.REXSTER_CONSOLE_LANGUAGE)) { changeLanugage(line); } else { executeScript(line); } } catch (Exception e) { this.output.println("Evaluation error: " + e.getMessage()); } } } private void changeLanugage(final String line) { final String langToChangeTo = line.substring(1); if (langToChangeTo == null || langToChangeTo.isEmpty()) { this.output.println("specify a language on Rexster ?<language-name>"); this.printAvailableLanguages(); } else if (this.session.isAvailableLanguage(langToChangeTo)) { this.settings.setLanguage(langToChangeTo); } else { this.output.println("not a valid language on Rexster: [" + langToChangeTo + "]."); this.printAvailableLanguages(); } } private void resetSessionWithRexster() { this.output.print("resetting session with Rexster " + this.settings.getHostPort()); if (this.session != null) { this.session.reset(); } else { this.initAndOpenSessionFromSettings(); } // reset binding cache in the console...it will come back fresh from the server on the // next script eval this.currentBindings.clear(); this.output.println("--> done"); } private void closeConsole() { this.output.print("closing session with Rexster " + this.settings.getHostPort()); if (this.session != null) { this.session.close(); this.session = null; } this.output.println("--> done"); } private ConsoleReader getInputReader() throws IOException { final ConsoleReader reader = new ConsoleReader(); reader.setBellEnabled(false); reader.setUseHistory(true); try { final History history = new History(); history.setHistoryFile(new File(System.getProperty("user.home") + "/" + REXSTER_HISTORY)); reader.setHistory(history); } catch (IOException e) { System.err.println("Could not find history file"); } return reader; } private void executeScript(final String line) { executeScript(line, true); } private void executeScript(final String line, final boolean showPrefix) { final ResultAndBindings result = eval(line, this.settings.getLanguage(), this.session); final Iterator itty; if (result.getResult() instanceof Iterator) { itty = (Iterator) result.getResult(); } else if (result.getResult() instanceof Iterable) { itty = ((Iterable) result.getResult()).iterator(); } else if (result.getResult() instanceof Map) { itty = ((Map) result.getResult()).entrySet().iterator(); } else { itty = new SingleIterator<Object>(result.getResult()); } while (itty.hasNext()) { final Object o = itty.next(); if (o != null) { if (showPrefix) { this.output.println("==>" + o); } else { this.output.println(o); } } } this.currentBindings = result.getBindings(); } private void printAvailableLanguages() { this.output.println("-= Available Languages =-"); final Iterator<String> languages = this.session.getAvailableLanguages(); while (languages.hasNext()) { this.output.println("?" + languages.next()); } } public void printHelp() { this.output.println("-= Console Specific =-"); this.output.println("?<language-name>: jump to engine"); this.output.println(Tokens.REXSTER_CONSOLE_LANGUAGES + ": list of available languages on Rexster"); this.output.println(Tokens.REXSTER_CONSOLE_BINDINGS + ": print available bindings in the session"); this.output.println(Tokens.REXSTER_CONSOLE_RESET + ": reset the rexster session"); this.output.println(Tokens.REXSTER_CONSOLE_EXECUTE + " <file-name>: execute a script file"); this.output.println(Tokens.REXSTER_CONSOLE_QUIT + ": quit"); this.output.println(Tokens.REXSTER_CONSOLE_HELP + ": displays this message"); this.output.println(""); this.output.println("-= Rexster Context =-"); this.output.println("rexster.getGraph(graphName) - gets a Graph instance"); this.output.println(" :graphName - [String] - the name of a graph configured within Rexster"); this.output.println("rexster.getGraphNames() - gets the set of graph names configured within Rexster"); this.output.println("rexster.getVersion() - gets the version of Rexster server"); this.output.println(""); } public void printBindings() { for (String binding : this.currentBindings) { this.output.println("==>" + binding); } } public String getPrompt() { return "rexster[" + this.settings.getLanguage() + "]> "; } public static String makeSpace(final int number) { String space = ""; for (int i = 0; i < number; i++) { space = space + " "; } return space; } public List<String> bindingsAsList(ScriptResponseMessage msg) { final List<String> bindings = new ArrayList<String>(); for(Map.Entry pair: msg.Bindings.entrySet()) { if (pair.getValue() == null) { bindings.add(pair.getKey() + "=null"); } else { bindings.add(pair.getKey() + "=" + pair.getValue().toString()); } } return bindings; } public List<String> consoleLinesAsList(ScriptResponseMessage msg) { final List<String> list = new ArrayList<String>(); for (String line : (ArrayList<String>) msg.Results.get()) { list.add(line); } return list; } private ResultAndBindings eval(final String script, final String scriptEngineName, final RemoteRexsterSession session) { ResultAndBindings returnValue = null; try { assert session.isOpen(); // the session field gets set by the RemoteRexsterSession class automatically final ScriptRequestMessage scriptMessage = new ScriptRequestMessage(); scriptMessage.Script = script; scriptMessage.LanguageName = scriptEngineName; scriptMessage.metaSetInSession(true); scriptMessage.metaSetTransaction(false); scriptMessage.metaSetIsolate(false); scriptMessage.metaSetConsole(true); scriptMessage.setRequestAsUUID(UUID.randomUUID()); final RexProMessage resultMessage = session.sendRequest(scriptMessage, 3, 500); List<String> lines = new ArrayList<String>(); List<String> bindings = new ArrayList<String>(); try { if (resultMessage instanceof ScriptResponseMessage) { final ScriptResponseMessage responseMessage = (ScriptResponseMessage) resultMessage; bindings = bindingsAsList(responseMessage); lines = consoleLinesAsList(responseMessage); } else if (resultMessage instanceof ErrorResponseMessage) { final ErrorResponseMessage errorMessage = (ErrorResponseMessage) resultMessage; lines = new ArrayList<String>() {{ add(errorMessage.ErrorMessage); }}; } else { throw new RuntimeException("Unexpected message type received from RexPro Server."); } } catch (Exception iae) { lines.add(iae.getMessage()); } Object result = lines.iterator(); if (lines.size() == 1) { result = lines.get(0); } returnValue = new ResultAndBindings(result, bindings); } catch (Exception e) { System.out.println("The session with Rexster Server may have been lost. Please try again or refresh your session with ?r"); } return returnValue; } private static String readFile(final String file) throws IOException { final BufferedReader reader = new BufferedReader(new FileReader(file)); String line; final StringBuilder stringBuilder = new StringBuilder(); final String ls = System.getProperty("line.separator"); while ((line = reader.readLine()) != null) { stringBuilder.append(line); stringBuilder.append(ls); } return stringBuilder.toString(); } public static void main(final String[] args) throws Exception { try { final ConsoleSettings settings = new ConsoleSettings(args); new RexsterConsole(settings).start(); } catch (Exception ex) { die(ex); } } private static void die(final Throwable ex) { System.out.println(ex.getMessage() + " (stack trace follows)"); ex.printStackTrace(); System.exit(1); } }