Now-a-days, we can see that whole world is moving around Clouds and virtualization. More and more applications are building for managing datacentre servers and other stuff. I have been part of one of such a module. I developed one module for monitoring and managing Linux servers remotely. We used JCraft’s Jsch with Google’s Expect4j for the same. Let’s get some idea about those API in brief.
As the website suggests, it is pure implementation of SSH2. It is very easy to use and integrate into your program. It is a fact that it is not well documented. You can read more about JSchfrom its website.
Expect is the kitchen sink of IO control. It supports control of processes and sockets, and a complex method of match multiple patterns at the same time. This is what Google code has to say about Expect4j. When you executes commands on remote machines one after other, your program needs to know when to execute next command. It’s like send your command and wait for execution of the same. Or we can say that wait for the command prompt. Expect4j does similar stuff(as far as I know). It also provides closures for getting complete output log of executed commands. I don’t know if there is some other use of closures.
Now, let’s start with an example. First open up SSH connection on remote machine.
| 1 2 3 4 5 6 7 8 9 10 11 | JSch jsch = newJSch();Session session = jsch.getSession(username, hostname, port);session.setPassword(password);Hashtable<String,String> config = newHashtable<String,String>();config.put("StrictHostKeyChecking", "no");session.setConfig(config);session.connect(60000);ChannelShell channel = (ChannelShell) session.openChannel("shell");Expect4j expect = newExpect4j(channel.getInputStream(), channel.getOutputStream());channel.connect(); | 
You can see that we have opened “shell” channel. That is because we want to execute sequence of commands on linux shell. You can see that Expect4j is initialized from JSCH session.
Now, we will prepare RegX patterns of command prompts of targeted machine. Be careful here because if these are not right then you might end up not executing commands. You can also provide closure provided by Expect4j to get output of your commands. (There might be some other use of it.)
| 1 2 3 4 5 6 | StringBuilder buffer = newStringBuilder();Closure closure = newClosure() {            publicvoidrun(ExpectState expectState) throwsException {                buffer.append(expectState.getBuffer());//string buffer for appending output of executed command            }}; | 
| 1 2 3 4 5 6 7 8 9 10 11 12 | String[] linuxPromptRegEx = newString[]{"\\>","#"};List<Match> lstPattern =  newArrayList<Match>();        for(String regexElement : linuxPromptRegEx) {            try{                Match mat = newRegExpMatch(regexElement, closure);                lstPattern.add(mat);            } catch(MalformedPatternException e) {                e.printStackTrace();            } catch(Exception e) {                e.printStackTrace();            }} | 
You can see that we have defined a closure that appends output of every command executed. We are providing this closure to every command pattern. I have my linux shell prompt working at “/>” and “#”. You can define your prompts as per your linux box.
Now, start executing commands.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | List<String> lstCmds = newArrayList<String>();lstCmds.add("ls");lstCmds.add("pwd");lstCmds.add("mkdir testdir");for(String strCmd : lstCmds) {    intreturnVal = expect.expect(objPattern);    if(returnVal == -2) {        expect.send(strCommandPattern);        expect.send("\r");//enter character    }} | 
We have three commands to execute in a single continuous SSH session. First it expects one of its prompt patterns to match. If right prompt has encountered then send one command over the shell to execute. Immediately after the command, send an enter character to execute the command. After that again wait for one of your command prompt to occur and then send other command. So, we can see that it send/wait kind of mechanism.
Now, put all together and here is a SSH client that can execute sequence of command on remote linux box. You will need following libraries in your project to execute this test class.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | importorg.apache.oro.text.regex.MalformedPatternException;importcom.jcraft.jsch.ChannelShell;importcom.jcraft.jsch.JSch;importcom.jcraft.jsch.Session;importexpect4j.Closure;importexpect4j.Expect4j;importexpect4j.ExpectState;importexpect4j.matches.Match;importexpect4j.matches.RegExpMatch;publicclassSSHClient {    privatestaticfinalintCOMMAND_EXECUTION_SUCCESS_OPCODE = -2;    privatestaticString ENTER_CHARACTER = "\r";    privatestaticfinalintSSH_PORT = 22;    privateList<String> lstCmds = newArrayList<String>();    privatestaticString[] linuxPromptRegEx = newString[]{"\\>","#", "~#"};    privateExpect4j expect = null;    privateStringBuilder buffer = newStringBuilder();    privateString userName;    privateString password;    privateString host;    /**     *     * @param host     * @param userName     * @param password     */    publicSSHClient(String host, String userName, String password) {        this.host = host;        this.userName = userName;        this.password = password;    }    /**     *     * @param cmdsToExecute     */    publicString execute(List<String> cmdsToExecute) {        this.lstCmds = cmdsToExecute;        Closure closure = newClosure() {            publicvoidrun(ExpectState expectState) throwsException {                buffer.append(expectState.getBuffer());            }        };        List<Match> lstPattern =  newArrayList<Match>();        for(String regexElement : linuxPromptRegEx) {            try{                Match mat = newRegExpMatch(regexElement, closure);                lstPattern.add(mat);            } catch(MalformedPatternException e) {                e.printStackTrace();            } catch(Exception e) {                e.printStackTrace();            }        }        try{            expect = SSH();            booleanisSuccess = true;            for(String strCmd : lstCmds) {                isSuccess = isSuccess(lstPattern,strCmd);                if(!isSuccess) {                    isSuccess = isSuccess(lstPattern,strCmd);                }            }            checkResult(expect.expect(lstPattern));        } catch(Exception ex) {            ex.printStackTrace();        } finally{            closeConnection();        }        returnbuffer.toString();    }    /**     *     * @param objPattern     * @param strCommandPattern     * @return     */    privatebooleanisSuccess(List<Match> objPattern,String strCommandPattern) {        try{            booleanisFailed = checkResult(expect.expect(objPattern));            if(!isFailed) {                expect.send(strCommandPattern);                expect.send(ENTER_CHARACTER);                returntrue;            }            returnfalse;        } catch(MalformedPatternException ex) {            ex.printStackTrace();            returnfalse;        } catch(Exception ex) {            ex.printStackTrace();            returnfalse;        }    }    /**     *     * @param hostname     * @param username     * @param password     * @param port     * @return     * @throws Exception     */    privateExpect4j SSH() throwsException {        JSch jsch = newJSch();        Session session = jsch.getSession(userName, host, SSH_PORT);        if(password != null) {            session.setPassword(password);        }        Hashtable<String,String> config = newHashtable<String,String>();        config.put("StrictHostKeyChecking", "no");        session.setConfig(config);        session.connect(60000);        ChannelShell channel = (ChannelShell) session.openChannel("shell");        Expect4j expect = newExpect4j(channel.getInputStream(), channel.getOutputStream());        channel.connect();        returnexpect;    }    /**     *     * @param intRetVal     * @return     */    privatebooleancheckResult(intintRetVal) {        if(intRetVal == COMMAND_EXECUTION_SUCCESS_OPCODE) {            returntrue;        }        returnfalse;    }    /**     *     */    privatevoidcloseConnection() {        if(expect!=null) {            expect.close();        }    }    /**     *     * @param args     */    publicstaticvoidmain(String[] args) {        SSHClient ssh = newSSHClient("linux_host", "root", "password");        List<String> cmdsToExecute = newArrayList<String>();        cmdsToExecute.add("ls");        cmdsToExecute.add("pwd");        cmdsToExecute.add("mkdir testdir");        String outputLog = ssh.execute(cmdsToExecute);        System.out.println(outputLog);    }} | 
If you find any difficulties to execute it, just play around execute command loop and command prompts RegX patterns.
Remote SSH: Using JSCH with Expect4j
原文:http://www.cnblogs.com/xuxiuxiu/p/6266583.html