org.smartfrog.services.net.TelnetImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.smartfrog.services.net.TelnetImpl.java

Source

/** (C) Copyright 1998-2011 Hewlett-Packard Development Company, LP
    
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
    
 This library 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
 Lesser General Public License for more details.
    
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
 For more information: www.smartfrog.org
    
 */

package org.smartfrog.services.net;

import org.apache.commons.net.telnet.TelnetClient;
import org.apache.commons.net.telnet.TelnetNotificationHandler;
import org.smartfrog.services.filesystem.FileSystem;
import org.smartfrog.services.passwords.PasswordProvider;
import org.smartfrog.sfcore.common.SmartFrogDeploymentException;
import org.smartfrog.sfcore.common.SmartFrogException;
import org.smartfrog.sfcore.common.SmartFrogLifecycleException;
import org.smartfrog.sfcore.common.SmartFrogResolutionException;
import org.smartfrog.sfcore.prim.Prim;
import org.smartfrog.sfcore.prim.PrimImpl;
import org.smartfrog.sfcore.prim.TerminationRecord;
import org.smartfrog.sfcore.reference.Reference;
import org.smartfrog.sfcore.utils.ComponentHelper;
import org.smartfrog.sfcore.utils.ListUtils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.rmi.RemoteException;
import java.util.Vector;

/**
 * SmartFrog implementation of telnet component.
 * <p/>
 * It uses apache commons net libraries
 *
 * @author Ashish Awasthi
 */
public class TelnetImpl extends PrimImpl implements Telnet, TelnetNotificationHandler {

    public static final String PROMPT_LOGIN = "login:";
    public static final String PROMPT_LINUX_PASSWORD = "Password:";
    public static final String PROMPT_WINDOWS_PASSWORD = "password:";
    public static final int SLEEP_TIME_MS = 500;
    private final int DEFAULT_TIMEOUT = 30000;
    private final int DEFAULT_PORT = 23;

    private String host;
    private String user;
    private String ostype;
    private String password = "";
    private String shellPrompt = "$";
    private int port = DEFAULT_PORT;
    private Vector<String> commandsList;
    private Vector<String> cmdsFailureMsgs;
    private TelnetClient client;
    private int timeout = DEFAULT_TIMEOUT;
    private String logFile;
    private Reference pwdProviderRef = new Reference(ATTR_PASSWORD_PROVIDER);
    private static final Reference attr_commands = new Reference(COMMANDS);
    private static final Reference attr_cmds_failure_msgs = new Reference(CMDS_FAILURE_MSGS);

    /**
     * Constructs TelnetImpl object.
     *
     * @throws RemoteException in case of network/rmi error
     */
    public TelnetImpl() throws RemoteException {
    }

    /**
     * Reads SmartFrog attributes and deploys TelnetImpl component.
     *
     * @throws SmartFrogException in case of error in deploying or reading the
     *                            attributes
     * @throws RemoteException    in case of network/emi error
     */
    @Override
    public synchronized void sfDeploy() throws SmartFrogException, RemoteException {
        super.sfDeploy();
        //read SmartFrog Attributes
        readSFAttributes();
    }

    /**
     * Connects to remote host and executes commands.
     *
     * @throws SmartFrogException in case of error in connecting to remote host
     *                            ,executing commands or command output is same
     *                            as failure message provided in the component
     *                            desciption.
     * @throws RemoteException    in case of network/emi error
     */
    @Override
    public synchronized void sfStart() throws SmartFrogException, RemoteException {
        super.sfStart();
        FileOutputStream fout = null;
        try {
            // create optional log file for the telnet session
            try {
                if (logFile != null) {
                    fout = new FileOutputStream(logFile, false);
                }
            } catch (IOException ioex) {
                sfLog().error("Error in opening log file:" + ioex, ioex);
            }

            client = new TelnetClient();
            String destination = "" + host + ":" + port;
            sfLog().debug("Connecting to " + destination);
            try {
                client.connect(host, port);
            } catch (IOException e) {
                throw new SmartFrogDeploymentException("Failed to connect to " + destination, e, this);
            }

            OutputStream opStream = client.getOutputStream();
            InputStream inpStream = client.getInputStream();
            sfLog().debug("Waiting for prompt '" + PROMPT_LOGIN + "'");
            String failureCause = "";
            boolean operationStatus = waitForString(inpStream, PROMPT_LOGIN, timeout);
            sfLog().debug(" result: " + operationStatus);
            if (operationStatus) {
                String loginName = user + '\n';
                opStream.write(loginName.getBytes());
                opStream.flush();
                String prompt;
                if (OSTYPE_LINUX.equals(ostype)) {
                    prompt = PROMPT_LINUX_PASSWORD;
                } else if (OS_TYPE_WINDOWS.equals(ostype)) {
                    prompt = PROMPT_WINDOWS_PASSWORD;
                } else {
                    throw new SmartFrogDeploymentException("Unknown " + Telnet.OSTYPE + " value '" + ostype + "'",
                            this);
                }
                operationStatus = waitForString(inpStream, prompt, timeout);
                if (!operationStatus) {
                    failureCause = "Failed to get password prompt '" + prompt + "' expected for ostype " + ostype;
                }
            } else {
                failureCause = "Failed to get login prompt '" + PROMPT_LOGIN + "'";
            }
            String passwordDetails = (password == null) ? "NO PASSWORD" : "password length " + password.length();

            sfLog().debug("Entering password");
            if (operationStatus) {
                String passWd = password + '\n';
                opStream.write(passWd.getBytes());
                opStream.flush();
                sfLog().debug("Waiting for shell prompt '" + shellPrompt + "'");
                LoginResults results = attemptLogin(inpStream, shellPrompt, timeout);
                if (!results.promptFound) {
                    operationStatus = false;
                    failureCause = "User \"" + user + "\" was not known, " + " their password was invalid "
                            + " or the shell prompt \"" + shellPrompt + "\" was not found." + " Password details: "
                            + passwordDetails + " \n" + " remote server log: " + results.received;
                }

            }
            if (!operationStatus) {
                throw new SmartFrogLifecycleException(
                        "Unable to login in remote machine " + destination + " cause: " + failureCause, this);
            }

            //at this point, we are successfully logged in

            if (sfLog().isInfoEnabled()) {
                sfLog().info("Successful login to:" + destination);
            }

            // register the output stream and set to null
            FileOutputStream fout1 = fout;
            fout = null;
            client.registerSpyStream(fout1);
            boolean checkCmdExecStatus = false;
            if ((cmdsFailureMsgs != null) && (!cmdsFailureMsgs.isEmpty())) {
                checkCmdExecStatus = true;
            }
            // Execute commands
            for (int i = 0; i < commandsList.size(); i++) {
                String cmd = commandsList.get(i);
                sfLog().debug("Executing " + cmd);
                cmd = cmd + '\n';
                opStream.write(cmd.getBytes());
                opStream.flush();

                // wait for prompt to return.
                waitForString(inpStream, shellPrompt, timeout);

                // check if command was successfully executed
                if (checkCmdExecStatus) {
                    String errMsg = cmdsFailureMsgs.get(i);
                    sfLog().debug("Waiting for error message " + errMsg);
                    boolean execError = waitForString(inpStream, errMsg, timeout);
                    if (execError) {
                        // throw exception
                        throw new SmartFrogTelnetException(cmd, errMsg);
                    }
                }

            }

            ComponentHelper helper = new ComponentHelper(this);
            TerminationRecord termR = TerminationRecord.normal("Telnet session to " + destination + " finished: ",
                    sfCompleteName());
            helper.sfSelfDetachAndOrTerminate(termR);

        } catch (Exception e) {
            throw SmartFrogLifecycleException.forward(e);
        } finally {
            //stop the client if defined
            if (client != null) {
                client.stopSpyStream();
            }
            //and the output stream, if not null.
            FileSystem.close(fout);
        }
    }

    /**
     * Life cycle method for terminating the SmartFrog component.
     *
     * @param tr Termination record
     */
    @Override
    public synchronized void sfTerminateWith(TerminationRecord tr) {
        super.sfTerminateWith(tr);
        try {
            if (client != null) {
                // It also closes input and output streams
                client.disconnect();
            }
        } catch (IOException ioex) {
            sfLog().ignore("When terminating the client", ioex);
            // ignore
        }
    }

    /**
     * Reads SmartFrog attributes.
     *
     * @throws SmartFrogResolutionException if failed to read any attribute or a
     *                                      mandatory attribute is not defined.
     * @throws RemoteException              in case of network/rmi error
     */
    private void readSFAttributes() throws SmartFrogException, RemoteException {
        // Mandatory attributes
        host = sfResolve(HOST, host, true);
        user = sfResolve(USER, user, true);
        ostype = sfResolve(OSTYPE, ostype, true);
        PasswordProvider pwdProvider = (PasswordProvider) sfResolve(pwdProviderRef, (Prim) null, true);
        password = pwdProvider.getPassword();
        commandsList = ListUtils.resolveStringList(this, attr_commands, true);

        //optional attributes
        cmdsFailureMsgs = ListUtils.resolveStringList(this, attr_cmds_failure_msgs, false);
        port = sfResolve(PORT, port, true);
        timeout = sfResolve(TIMEOUT, timeout, true);
        shellPrompt = sfResolve(SHELL_PROMPT, shellPrompt, true);
        logFile = sfResolve(LOG_FILE, logFile, false);
    }

    /**
     * Callback method called when TelnetClient receives an option negotiation
     * command.
     * <p/>
     *
     * @param negotiation_code - type of negotiation command received
     *                         (RECEIVED_DO, RECEIVED_DONT, RECEIVED_WILL,
     *                         RECEIVED_WONT)
     *                         <p/>
     * @param option_code      - code of the option negotiated
     *                         <p/>
     *                         *
     */
    @Override
    public void receivedNegotiation(int negotiation_code, int option_code) {
        String command = null;
        if (negotiation_code == TelnetNotificationHandler.RECEIVED_DO) {
            command = "DO";
        } else if (negotiation_code == TelnetNotificationHandler.RECEIVED_DONT) {
            command = "DONT";
        } else if (negotiation_code == TelnetNotificationHandler.RECEIVED_WILL) {
            command = "WILL";
        } else if (negotiation_code == TelnetNotificationHandler.RECEIVED_WONT) {
            command = "WONT";
        }
        sfLog().debug("Received negotiation code " + command);
    }

    /**
     * Waits for a string with timeout.
     *
     * @param is      Input Stream which is searched
     * @param end     String to search
     * @param waitTime Timeout
     * @return true if string is located in the inp stream, false if search is
     *         timedout or string is not found
     * @throws IOException for network problems
     */
    @SuppressWarnings({ "BusyWait" })
    public boolean waitForString(InputStream is, String end, long waitTime) throws IOException {
        byte[] buffer = new byte[32];
        long starttime = System.currentTimeMillis();

        String readbytes = "";
        while ((!readbytes.contains(end)) && ((System.currentTimeMillis() - starttime) < waitTime)) {
            if (is.available() > 0) {
                int ret_read = is.read(buffer);
                readbytes = readbytes + new String(buffer, 0, ret_read);
            } else {
                try {
                    Thread.sleep(SLEEP_TIME_MS);
                } catch (InterruptedException e) {
                    //interrupted
                    return false;
                }
            }
        }
        return readbytes.contains(end);
    }

    /**
     * Checks if login is successful.
     *
     * @param is input stream
     * @param end end string to wait for
     * @param loginTimeout how log to wait in millis
     * @return true if login sucessful else false
     * @throws IOException for network problems
     */
    @SuppressWarnings({ "BusyWait" })
    private LoginResults attemptLogin(InputStream is, String end, long loginTimeout) throws IOException {
        byte[] buffer = new byte[32];
        long starttime = System.currentTimeMillis();

        String readbytes = "";

        while ((!readbytes.contains(end)) && ((System.currentTimeMillis() - starttime) < loginTimeout)) {
            if (is.available() > 0) {
                int ret_read = is.read(buffer);
                readbytes = readbytes + new String(buffer, 0, ret_read);
            } else {
                try {
                    Thread.sleep(SLEEP_TIME_MS);
                } catch (InterruptedException e) {
                    //interrupted
                    LoginResults results = new LoginResults(false, readbytes);
                    results.interrupted = true;
                    return results;
                }
            }
        }

        boolean found = readbytes.contains(end);
        return new LoginResults(found, readbytes);
    }

    /**
     * Results of the login attempt
     */
    private static class LoginResults {
        public boolean promptFound;
        public boolean interrupted;
        public String received;

        private LoginResults(final boolean promptFound, final String received) {
            this.promptFound = promptFound;
            this.received = received;
        }

    }
}