001/* 002 * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 */ 019package org.nuxeo.shell.cmds; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.PrintWriter; 024import java.io.Writer; 025 026import jline.ANSIBuffer; 027import jline.ConsoleReader; 028import jline.Terminal; 029 030import org.nuxeo.shell.Command; 031import org.nuxeo.shell.Context; 032import org.nuxeo.shell.Shell; 033import org.nuxeo.shell.ShellConsole; 034import org.nuxeo.shell.ShellException; 035import org.nuxeo.shell.cmds.completors.ShellCompletor; 036 037/** 038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 039 */ 040@Command(name = "interactive", help = "Interactive shell") 041public class Interactive implements Runnable, ShellConsole { 042 043 protected static ConsoleReaderFactory factory; 044 045 public static void setConsoleReaderFactory(ConsoleReaderFactory factory) { 046 Interactive.factory = factory; 047 } 048 049 protected static String currentCmdLine; 050 051 @Context 052 protected Shell shell; 053 054 protected ConsoleReader console; 055 056 private static boolean isRunning = false; 057 058 private static InteractiveShellHandler handler; 059 060 public static void setHandler(InteractiveShellHandler exitHandler) { 061 Interactive.handler = exitHandler; 062 } 063 064 public static void reset() { 065 isRunning = false; 066 } 067 068 public Interactive() throws IOException { 069 console = factory != null ? factory.getConsoleReader() : new ConsoleReader(); 070 } 071 072 /** 073 * Used in GUI mode 074 * 075 * @param shell 076 * @param in 077 * @param out 078 * @throws IOException 079 */ 080 public Interactive(Shell shell, InputStream in, Writer out, Terminal term) throws IOException { 081 this.shell = shell; 082 console = new ConsoleReader(in, out, null, term); 083 } 084 085 public static String getCurrentCmdLine() { 086 return currentCmdLine; 087 } 088 089 public ConsoleReader getConsole() { 090 return console; 091 } 092 093 public Shell getShell() { 094 return shell; 095 } 096 097 public void run() { 098 if (handler != null) { 099 handler.enterInteractiveMode(); 100 } 101 if (isRunning) { // avoid entering twice this command 102 return; 103 } 104 isRunning = true; 105 console.addCompletor(new ShellCompletor(this)); 106 shell.setConsole(this); 107 console.setDefaultPrompt(getPrompt()); 108 try { 109 loadHistory(); 110 shell.hello(); 111 try { 112 shell.getActiveRegistry().autorun(shell); 113 } catch (Throwable t) { 114 handleError(t); 115 } 116 while (true) { 117 try { 118 String cmdline = console.readLine(getPrompt()); 119 currentCmdLine = cmdline; 120 shell.run(cmdline); 121 } catch (Throwable t) { 122 int r = handleError(t); 123 if (r != 0) { 124 isRunning = false; 125 if (handler != null) { 126 if (handler.exitInteractiveMode(r)) { 127 return; 128 } 129 } else { // default exit mechanism 130 shell.bye(); 131 System.exit(r < 0 ? 0 : r); 132 } 133 } 134 } 135 } 136 } catch (IOException e) { 137 e.printStackTrace(); 138 System.exit(1); 139 } finally { 140 closeHistory(); 141 } 142 } 143 144 /** 145 * Return non zero to stop the application. If a negative code is returned the application will stop normally 146 * otherwise it will stop using System.exit with the exit code as argument. 147 * 148 * @param t 149 * @return 150 * @throws IOException 151 */ 152 protected int handleError(Throwable t) throws IOException { 153 if (t instanceof ShellException) { 154 ShellException e = (ShellException) t; 155 int r = e.getErrorCode(); 156 if (r != 0) { 157 return r; 158 } else { 159 shell.setProperty("last.error", e); 160 console.printString(e.getMessage()); 161 console.printNewline(); 162 // console.printString(sw.toString()); 163 } 164 } else { 165 ANSIBuffer buf = shell.newANSIBuffer(); 166 buf.red(Trace.getStackTrace(t)); 167 console.printString(buf.toString()); 168 } 169 return 0; 170 } 171 172 public String getPrompt() { 173 return shell.getActiveRegistry().getPrompt(shell); 174 } 175 176 public void print(String msg) { 177 try { 178 console.printString(msg); 179 } catch (IOException e) { 180 throw new ShellException(e).setErrorCode(1); 181 } 182 } 183 184 public void println(String msg) { 185 try { 186 console.printString(msg); 187 console.printNewline(); 188 } catch (IOException e) { 189 throw new ShellException(e).setErrorCode(1); 190 } 191 } 192 193 public void println() { 194 try { 195 console.printNewline(); 196 } catch (IOException e) { 197 throw new ShellException(e).setErrorCode(1); 198 } 199 } 200 201 public String readLine(String prompt, Character mask) { 202 try { 203 return console.readLine(prompt, mask); 204 } catch (IOException e) { 205 throw new ShellException(e).setErrorCode(1); 206 } 207 } 208 209 public void loadHistory() throws IOException { 210 if (!shell.getBooleanSetting("history", true)) { 211 return; 212 } 213 console.getHistory().setHistoryFile(shell.getHistoryFile()); 214 console.getHistory().moveToEnd(); 215 } 216 217 public void closeHistory() { 218 PrintWriter pw = console.getHistory().getOutput(); 219 if (pw != null) { 220 pw.close(); 221 } 222 } 223 224 public void removeHistory() { 225 closeHistory(); 226 shell.getHistoryFile().delete(); 227 } 228 229}