/** * MailArchiver is an application that provides services for storing and managing e-mail messages through a Web Services SOAP interface. * Copyright (C) 2012 Marcio Andre Scholl Levien and Fernando Alberto Reuter Wendt and Jose Ronaldo Nogueira Fonseca Junior * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ /******************************************************************************\ * * This product was developed by * * SERVIÇO FEDERAL DE PROCESSAMENTO DE DADOS (SERPRO), * * a government company established under Brazilian law (5.615/70), * at Department of Development of Porto Alegre. * \******************************************************************************/ package serpro.mailarchiver.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.util.Set; import java.util.TreeSet; import javax.annotation.PostConstruct; import bsh.ConsoleInterface; import com.eventrouter.annotation.AnnotationProcessor; import com.eventrouter.annotation.Subscribe; import com.eventrouter.SubscriptionRegistry; import org.springframework.beans.factory.annotation.Configurable; import org.vaadin.console.Console; import org.vaadin.console.Console.Command; import serpro.mailarchiver.view.BaseApplication; @Configurable public class ConsoleMediator implements ConsoleInterface, Console.Handler { private static final Logger log = Logger.getLocalLogger(); @PostConstruct private void postConstruct() { SubscriptionRegistry subscriptionRegistry = BaseApplication.getInstance().getDispatchContext().getSubscriptionRegistry(); AnnotationProcessor processor = new AnnotationProcessor(subscriptionRegistry); processor.process(this); } private static final long serialVersionUID = 1L; private static final String ENTER = System.getProperty("line.separator"); private static final String EOT = new String(new char[] {'\004'}); private static final String LF = new String(new char[] {'\012'}); private static final String CR = new String(new char[] {'\015'}); private static final String ESC = new String(new char[] {'\033'}); enum AnsiEsc { Reset (0), FontWeightBolder(1), FontWeightNormal(22), FontWeightLighter(2), FontStyleItalic(3), TextDecorationUnderline(4), TextDecorationUnderlineOff(24), TextDecorationBlinkSlow(5), TextDecorationBlinkRapid(6), TextDecorationBlinkOff(25), TextDecorationLineThrough(9), TextDecorationLineThroughOff(29), TextDecorationOverline(53), TextDecorationOverlineOff(55), ColorBlack(30), ColorRed(31), ColorGreen(32), ColorYellow(33), ColorBlue(34), ColorPurple(35), ColorTeal(36), ColorSilver(37), BackgroundColorBlack(40), BackgroundColorRed(41), BackgroundColorGreen(42), BackgroundColorYellow(43), BackgroundColorBlue(44), BackgroundColorPurple(45), BackgroundColorTeal(46), BackgroundColorSilver(47); @Override public String toString() { return ESC + '[' + code + 'm'; } private final int code; AnsiEsc(int code) { this.code = code; } } private final Console console; private MyReader interpreterIn; private PrintStream interpreterOut; private PrintStream interpreterErr; private ByteArrayOutputStream interpreterOutByteArray; private StringBuffer interpreterOutBuffer; public ConsoleMediator(Console console) { this.console = console; this.console.setConvertANSIToCSS(true); this.console.setHandler(this); interpreterIn = new MyReader(); interpreterOutByteArray = new ByteArrayOutputStream(); interpreterOutBuffer = new StringBuffer(); try { interpreterOut = new PrintStream(interpreterOutByteArray, false, "UTF-8") { @Override public void flush() { super.flush(); appendOutput(); } }; interpreterErr = new PrintStream(interpreterOutByteArray, false, "UTF-8") { @Override public void flush() { super.flush(); appendOutput(); } }; } catch (UnsupportedEncodingException ex) { log.error(ex); } } // @Override public Reader getIn() { return interpreterIn; } @Override public PrintStream getOut() { return interpreterOut; } @Override public PrintStream getErr() { return interpreterErr; } @Override public void println(Object o) { String s = String.valueOf(o); interpreterOut.print(AnsiEsc.ColorYellow.toString() + s + ENTER); interpreterOut.flush(); } @Override public void print(Object o) { String s = String.valueOf(o); if("bsh % ".equals(s)) { interpreterOut.print(EOT); } else { interpreterOut.print(AnsiEsc.ColorYellow.toString() + s); } interpreterOut.flush(); } @Override public void error(Object o) { String s = String.valueOf(o); if(s.endsWith(LF)) { s = s.substring(0, s.length() - 1); if(s.endsWith(CR)) { s = s.substring(0, s.length() - 1); } } interpreterErr.print(AnsiEsc.ColorRed.toString() + s); interpreterErr.flush(); } // // /** * Called when user has entered input to the Console and presses enter * to execute it. */ @Override public void inputReceived(Console console, String lastInput) { boolean run = false; if(lastInput.isEmpty()) { lastInput = ";"; run = true; } else { for(int i = lastInput.length() - 1; i >= 0; i--) { char c = lastInput.charAt(i); if(Character.isSpaceChar(c)) { continue; } if(c == ';') { run = true; } break; } } interpreterIn.put(lastInput + ENTER); if(!run) { console.setPs("> "); console.prompt(); console.focusInput(); } } /** * Called when user uses TAB to complete the command entered into the * Console input. */ @Override public Set getSuggestions(Console console, String lastInput) { //TODO ? Set suggestions = new TreeSet(); return suggestions; } /** * Handle an exception during a Command execution. */ @Override public void handleException(Console console, Exception e, Command cmd, String[] argv) { //TODO ? log.error(e); } /** * Handle situation where a command could not be found. */ @Override public void commandNotFound(Console console, String[] argv) { //TODO ? } // private void appendOutput() { if(interpreterOutByteArray.size() > 0) { try { String content = interpreterOutByteArray.toString("UTF-8"); interpreterOutByteArray.reset(); if(!content.isEmpty()) { interpreterOutBuffer.append(content); } } catch (UnsupportedEncodingException ex) { log.error(ex); } } } @Subscribe({"/mailarchiver/refresh"}) private void refresh() { if(interpreterOutBuffer.length() > 0) { String content = interpreterOutBuffer.toString(); interpreterOutBuffer.setLength(0); boolean eot = false; if(content.endsWith(EOT)) { eot = true; content = content.substring(0, content.length() - 1); if(content.endsWith(LF)) { content = content.substring(0, content.length() - 1); if(content.endsWith(CR)) { content = content.substring(0, content.length() - 1); } } } if(!content.isEmpty()) { console.print(content); } if(eot) { console.setPs("{bsh}> "); console.prompt(); console.focusInput(); } } } private static class MyReader extends Reader { private final StringBuilder buffer = new StringBuilder(); public void put(String value) { synchronized(buffer) { buffer.append(value); buffer.notifyAll(); } } @Override public int read(char[] cbuf, int off, int len) throws IOException { int n; synchronized(buffer) { while(buffer.length() == 0) { try { buffer.wait(); } catch (InterruptedException ex) { log.error(ex); } } n = Math.min(len, buffer.length()); for(int i = 0; i < n; i++) { cbuf[off++] = buffer.charAt(i); } buffer.delete(0, n); } return n; } @Override public void close() throws IOException { } } }