/** * Copyright 2014 Comcast Cable Communications Management, LLC * * This file is part of CATS. * * CATS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CATS 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CATS. If not, see <http://www.gnu.org/licenses/>. */ package com.comcast.cats.telnet; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.SocketException; import java.util.Date; import org.apache.commons.net.telnet.TelnetClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents a generic telnet connection. * * @author skurup00c * */ public class TelnetConnection { private InputStream is; private PrintStream os; private String host; private Integer port; private String defaultPromptString; private TelnetClient telnetClient; private Integer defaultReadTimeout; protected Date lastActiveTime; protected Boolean isConnected = false; private boolean isBusy = false; public static final int DEFAULT_READ_TIMEOUT = 60000;//1 * 60 * 1000; one minute public static final int DEFAULT_TIMEOUT = 300000;//5 * 60 * 1000; private static Logger logger = LoggerFactory.getLogger( TelnetConnection.class ); /** * Creates a TelnetConnection insatnce. * * @param ip * : of the telnet device * @param port * : telnet port * @param defaultPromptString * : default prompt string to be used. Usually ">". * */ public TelnetConnection( String host, Integer port, String defaultPromptString ) { this.host = host; this.port = port; this.defaultPromptString = defaultPromptString; this.telnetClient = new TelnetClient(); setDefaultReadTimeout( DEFAULT_READ_TIMEOUT ); lastActiveTime = new Date(); } /** * Connect to the telnet client. * * @param isEnterRequired * : sometime an ENTER key maybe required to reach the prompt. * * @return true: if connected. * * @throws SocketException * @throws IOException */ public synchronized Boolean connect( Boolean isEnterRequired ) throws SocketException, IOException { if ( !isConnected ) { try { telnetClient.connect( getHost(), getPort() ); logger.info( "connected to telnet host " + host + " port " + port + " defaultPromptString " + defaultPromptString ); } catch ( SocketException e ) { logger.warn( "Could not connect to telnetSession " + e.getMessage() ); throw new SocketException( e.getMessage() ); } is = telnetClient.getInputStream(); os = new PrintStream( telnetClient.getOutputStream() ); if ( isEnterRequired ) { os.println(); } isConnected = true; lastActiveTime = new Date(); } return isConnected; } /** * Connect to the telnet client with a password. * * @param isEnterRequired * : sometime an ENTER key maybe required to reach the prompt. * * @param password * @param passwordPromptString * : the prompt that asks for a password : usually something like * "Enter Password :" * @return true if connected successfully * @throws SocketException * @throws IOException */ public synchronized Boolean connectWithPassword( String password, String passwordPromptString, Boolean isEnterRequired ) throws SocketException, IOException { if ( !isConnected && password != null && passwordPromptString != null ) { try { telnetClient.connect( getHost(), getPort() ); logger.info( "connected to telnet host " + host + " port " + port ); } catch ( SocketException e ) { logger.warn( "Could not connect to telnetSession " + e.getMessage() ); throw new SocketException( e.getMessage() ); } is = telnetClient.getInputStream(); os = new PrintStream( telnetClient.getOutputStream() ); if ( isEnterRequired ) { os.println(); } String passwordPrompt = readUntil( passwordPromptString ); logger.debug( "passwordPrompt " + passwordPrompt ); if ( passwordPrompt != null ) { write( password ); isConnected = true; lastActiveTime = new Date(); } else { logger.info( "Prompt string could not be reached" ); disconnect(); } } return isConnected; } /** * Disconnects a telnet session. * * @throws IOException */ public synchronized void disconnect() throws IOException { if ( is != null ) { is.close(); } if ( os != null ) { os.close(); } logger.trace( "dicsonnecting telnetClient" ); if ( telnetClient.isConnected() ) { telnetClient.disconnect(); } is = null; os = null; isConnected = false; logger.info( "disconnected telnetConnection " + host + " port " + port ); } /** * Status of telnet connection * * @return true if connected. */ public synchronized Boolean isConnected() { return isConnected; } /** * Send a command to the telnet session. Requires * TelnetConnection.isConnected() to be true. * * @param command * to send * @return returned value in telnet client after execution of command. * @throws IOException */ public synchronized String sendCommand( String command ) throws IOException { return sendCommand( command, defaultPromptString ); } /** * Send a command to the telnet session, and read till the following prompt * instead of the default prompt. * * Requires TelnetConnection.isConnected() to be true. * * @param command * @param prompt * @return * @throws IOException */ public synchronized String sendCommand( String command, String prompt ) throws IOException { isBusy = true; logger.trace( "sendCommand " + command + " prompt " + prompt ); String result = null; if ( isConnected && command != null ) { write( command ); result = readUntil( prompt ); } lastActiveTime = new Date(); isBusy = false; return result; } /** * if prompt string is not recieved within the default timeout value, the * read will be interrupted. This is important to avoid the connection to * hang on a read when the prompt string does not arrive. Default value is 1 * minute. * * @return defaultTimeout value */ public Integer getDefaultReadTimeout() { return defaultReadTimeout; } /** * Timeout to that closes the socket during a period of inactivity. * */ public void setDefaultReadTimeout( Integer defaultReadTimeout ) { this.defaultReadTimeout = defaultReadTimeout; } /** * This just provides the Output stream to write to the telnet connection. * Should be called only after a connect is called. This is to provide users * with an option to write stuff to the telnet connection other than what is * provided through this class. Its the responsibility of the user to * understand the working of TelnetConnection and the proper use of the * PrintStream * */ public PrintStream getPrintStream() { return os; } /** * This just provides the input stream to write to the telnet connection. * Should be called only after a connect is called. This is to provide users * with an option to read stuff from the telnet connection other than what * is provided through this class. Its the responsibility of the user to * understand the working of TelnetConnection and the proper use of the * input * */ public InputStream getInputStream() { return is; } /** * Get the last time a connect, or sendCommand was sent. can be used to * determine timeout to disconnect based on inactivity etc. * * @return */ public Date getLastActiveTime() { return lastActiveTime; } public String getHost() { return host; } public Integer getPort() { return port; } public boolean isBusy() { return isBusy; } /** * * Reads Telnet response. * * @param pattern * @return * @throws IOException */ private synchronized String readUntil( String pattern ) throws IOException { logger.trace( "readUntil " + pattern ); String retVal = null; if ( is != null ) { StringBuffer sb = new StringBuffer(); char lastChar = pattern.charAt( pattern.length() - 1 ); char ch; telnetClient.setSoTimeout( defaultReadTimeout ); logger.trace( "getDefaultReadTimeout() " + defaultReadTimeout ); while ( true ) { try { int readByte = is.read(); if ( readByte != -1 ) { ch = ( char ) readByte; } else { logger.warn( "TelnetConnection: End of stream reached. Maybe remote end crashed " ); disconnect(); throw new IOException( "TelnetConnection: End of stream reached. Maybe remote end crashed " ); } } catch ( IOException e ) { logger.warn( "Error occured in TelnetConnect readUntil " + e.getMessage() ); disconnect(); throw new IOException( e ); } sb.append( ch ); if ( ch == lastChar ) { if ( sb.toString().endsWith( pattern ) ) { String string = sb.toString(); retVal = string.substring( 0, string.length() - pattern.length() ); logger.trace( "readUntil string " + retVal ); break; } } } } return retVal; } /** * Sends actual Telnet command. * * @param value */ private synchronized void write( String value ) { if ( null != os ) { logger.trace( "write value " + value ); os.println( value ); os.flush(); } } }