/** * */ package org.codehaus.mojo.enchanter.impl; import java.io.BufferedInputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.naming.OperationNotSupportedException; import org.codehaus.mojo.enchanter.ConnectionLibrary; import org.codehaus.mojo.enchanter.StreamConnection; import org.codehaus.mojo.enchanter.StreamListener; /** * Default implementation of StreamConnection connection and parsing methods */ public class DefaultStreamConnection implements StreamConnection { private BufferedInputStream in; private PrintWriter out; private Map<String, Response> respondWith = new HashMap<String, Response>(); private List<Prompt> waitFor = new ArrayList<Prompt>(); List<StreamListener> streamListeners = new ArrayList<StreamListener>(); private String endOfLine = "\r\n"; private char lastChar; private boolean alive = true; private StringBuilder lastLine = new StringBuilder(); private Thread timeoutThread; private int timeout = 200; private ConnectionLibrary connectionLibrary; private StreamListener debugStreamListener = null; public DefaultStreamConnection() { } public DefaultStreamConnection( ConnectionLibrary connLib ) { this.connectionLibrary = connLib; } public void connect( String host ) throws IOException { try { connectionLibrary.connect( host ); } catch ( OperationNotSupportedException e ) { throw new RuntimeException( e ); } setupStreams(); } public void connect( String host, int port ) throws IOException { try { connectionLibrary.connect( host, port ); } catch ( OperationNotSupportedException e ) { throw new RuntimeException( e ); } setupStreams(); } public void connect( String host, String username ) throws IOException { try { connectionLibrary.connect( host, username ); } catch ( OperationNotSupportedException e ) { throw new RuntimeException( e ); } setupStreams(); } private void setupStreams() { this.in = new BufferedInputStream( connectionLibrary.getInputStream() ); this.out = new PrintWriter( connectionLibrary.getOutputStream() ); for ( StreamListener listener : streamListeners ) { listener.init( this.out ); } } public void connect( String host, int port, String username, final String password ) throws IOException { try { connectionLibrary.connect( host, port, username, password ); } catch ( OperationNotSupportedException e ) { throw new RuntimeException( e ); } setupStreams(); } public void connect( String host, int port, String username, final String password, String privateKeyPath ) throws IOException { try { connectionLibrary.connect( host, port, username, password, privateKeyPath ); } catch ( OperationNotSupportedException e ) { throw new RuntimeException( e ); } setupStreams(); } public void setEndOfLine( String eol ) { this.endOfLine = eol; } public void setConnectionLibrary( ConnectionLibrary lib ) { this.connectionLibrary = lib; } public void disconnect() throws IOException { if ( timeoutThread != null ) { timeoutThread.interrupt(); } alive = false; connectionLibrary.disconnect(); } public void addStreamListener( StreamListener listener ) { streamListeners.add( listener ); } public void removeStreamListener( StreamListener listener ) { streamListeners.remove( listener ); } public void setDebug( boolean debug ) { if ( this.debugStreamListener == null ) { this.debugStreamListener = new DebugStreamListener(); } if ( debug ) { removeStreamListener( this.debugStreamListener ); //dont want duplicate addStreamListener( debugStreamListener ); } else { removeStreamListener( this.debugStreamListener ); } } public void send( String text ) throws IOException { print( text, false ); } public void sendLine( String text ) throws IOException { print( text, true ); } public void sleep( int millis ) throws InterruptedException { Thread.sleep( millis ); } private void print( String text, boolean eol ) throws IOException { text = text.replace( "^C", String.valueOf( (char) 3 ) ); text = text.replace( "^M", endOfLine ); if ( eol ) { out.print( text + endOfLine ); out.flush(); getLine(); } else { out.print( text ); out.flush(); } byte[] bytes = text.getBytes(); for ( StreamListener listener : streamListeners ) { listener.hasWritten( bytes ); } } public void respond( String prompt, String response ) { if ( response == null ) { respondWith.remove( prompt ); } else { respondWith.put( prompt, new Response( prompt, response ) ); } } public boolean waitFor( String waitFor ) throws IOException { return waitFor( waitFor, false ); } public boolean waitFor( String waitFor, boolean readLineOnMatch ) throws IOException { prepare( new String[] { waitFor } ); return ( readFromStream( readLineOnMatch ) == 0 ); } public int waitForMux( String... waitFor ) throws IOException { return waitForMux( waitFor, false ); } public int waitForMux( String[] waitFor, boolean readLineOnMatch ) throws IOException { prepare( waitFor ); return readFromStream( readLineOnMatch ); } public void setTimeout( int timeout ) { this.timeout = timeout; } public int getTimeout() { return this.timeout; } protected void prepare( String[] text ) { this.alive = true; for ( String val : text ) { waitFor.add( new Prompt( val ) ); } this.lastLine.setLength( 0 ); } public String lastLine() { return this.lastLine.toString(); } public String getLine() throws IOException { if ( waitFor( endOfLine, false ) ) { return lastLine(); } return null; } private int read( byte[] data ) throws IOException { int length = 0; while ( alive ) { if ( in.available() == 0 ) { try { Thread.sleep( 100 ); } catch ( InterruptedException e ) { } } else { length = in.read( data ); if ( length > 0 ) { break; } } } return length; } public void clear() throws IOException { while ( in.available() > 0 ) { in.read(); } } public int readFromStream( boolean readLineOnMatch ) throws IOException { int result = -1; byte[] data = new byte[1]; int length = 0; boolean readTillEndOfLine = false; if ( timeout > 0 ) { timeoutThread = new Thread() { public void run() { try { this.setName( "TimeOut" ); sleep( timeout ); } catch ( InterruptedException e ) { return; } alive = false; } }; timeoutThread.start(); } outer: while ( alive && ( length = read( data ) ) >= 0 ) { for ( int x = 0; x < length; x++ ) { char c = (char) data[x]; for ( StreamListener listener : streamListeners ) { listener.hasRead( (byte) data[x] ); } if ( readTillEndOfLine && ( c == '\r' || c == '\n' ) ) break outer; int match = lookForMatch( c ); if ( match != -1 ) { result = match; if ( readLineOnMatch && ( c != '\r' && c != '\n' ) ) { readTillEndOfLine = true; } else { break outer; } } else { lookForResponse( (char) data[x] ); lastChar = (char) data[x]; } } } reset(); return result; } int lookForMatch( char s ) { if ( s != '\r' && s != '\n' ) lastLine.append( s ); for ( int m = 0; alive && m < waitFor.size(); m++ ) { Prompt prompt = (Prompt) waitFor.get( m ); if ( prompt.matchChar( s ) ) { // the whole thing matched so, return the match answer if ( prompt.match() ) { return m; } else { prompt.nextPos(); } } else { // if the current character did not match reset prompt.resetPos(); if ( s == '\n' || s == '\r' ) { lastLine.setLength( 0 ); } } } return -1; } void lookForResponse( char s ) throws IOException { for ( Response response : respondWith.values() ) { if ( response.matchChar( s ) ) { if ( response.match() ) { print( response.getResponse(), false ); response.resetPos(); } else { response.nextPos(); } } else { response.resetPos(); } } } void reset() { waitFor.clear(); if ( timeout > 0 ) { timeoutThread.interrupt(); } alive = true; } static class Prompt { private String prompt; private int pos; public Prompt( String prompt ) { this.prompt = prompt; this.pos = 0; } public boolean matchChar( char c ) { return ( prompt.charAt( pos ) == c ); } public boolean match() { return pos + 1 == prompt.length(); } public String getPrompt() { return prompt; } public void nextPos() { this.pos++; } public void resetPos() { this.pos = 0; } } static class Response extends Prompt { private String response; public Response( String prompt, String response ) { super( prompt ); this.response = response; } public String getResponse() { return response; } } private class DebugStreamListener implements StreamListener { public void hasRead( byte b ) { if ( b != '\r' ) System.out.print( (char) b ); } public void hasWritten( byte[] b ) { // Not usually necessary // System.out.print(new String(b)); } public void init( PrintWriter writer ) { } } }