JUnits for SshJcraftWrapper.java connect
[appc.git] / appc-config / appc-config-adaptor / provider / src / main / java / org / onap / appc / ccadaptor / SshJcraftWrapper.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
8  * =============================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.appc.ccadaptor;
26
27 import com.att.eelf.configuration.EELFLogger;
28 import com.att.eelf.configuration.EELFManager;
29 import com.jcraft.jsch.Channel;
30 import com.jcraft.jsch.ChannelSftp;
31 import com.jcraft.jsch.ChannelShell;
32 import com.jcraft.jsch.ChannelSubsystem;
33 import com.jcraft.jsch.JSch;
34 import com.jcraft.jsch.JSchException;
35 import com.jcraft.jsch.Session;
36 import com.jcraft.jsch.SftpException;
37 import com.jcraft.jsch.UIKeyboardInteractive;
38 import com.jcraft.jsch.UserInfo;
39 import java.io.BufferedInputStream;
40 import java.io.BufferedReader;
41 import java.io.BufferedWriter;
42 import java.io.ByteArrayInputStream;
43 import java.io.ByteArrayOutputStream;
44 import java.io.DataInputStream;
45 import java.io.DataOutputStream;
46 import java.io.File;
47 import java.io.FileWriter;
48 import java.io.IOException;
49 import java.io.InputStream;
50 import java.io.InputStreamReader;
51 import java.io.OutputStream;
52 import java.io.RandomAccessFile;
53 import java.text.DateFormat;
54 import java.text.SimpleDateFormat;
55 import java.util.Calendar;
56 import java.util.Date;
57 import java.util.StringTokenizer;
58 import javax.annotation.Nonnull;
59 import org.apache.commons.lang.StringUtils;
60
61 public class SshJcraftWrapper {
62
63     private static final EELFLogger log = EELFManager.getInstance().getLogger(SshJcraftWrapper.class);
64
65     private static final int BUFFER_SIZE = 512000;
66     static final int DEFAULT_PORT = 22;
67     static final String CHANNEL_SHELL_TYPE = "shell";
68     static final String CHANNEL_SUBSYSTEM_TYPE = "subsystem";
69     private static final String TERMINAL_BASIC_MODE = "vt102";
70     static final String STRICT_HOST_CHECK_KEY = "StrictHostKeyChecking";
71     static final String STRICT_HOST_CHECK_VALUE = "no";
72     private TelnetListener listener = null;
73     private String routerLogFileName = null;
74     private String routerName = null;
75     private char[] charBuffer = new char[BUFFER_SIZE];
76     private BufferedReader reader = null;
77     private BufferedWriter out = null;
78     private File tmpFile = null;
79     private JSch jsch = null;
80     private Session session = null;
81     private Channel channel = null;
82     private String aggregatedReceivedString = "";
83     private String routerCmdType = "XML";
84     private String routerFileName = null;
85     private File jcraftReadSwConfigFileFromDisk = new File("/tmp/jcraftReadSwConfigFileFromDisk");
86     private String equipNameCode = null;
87     private String hostName = null;
88     private String userName = null;
89     private String passWord = null;
90     private Runtime runtime = Runtime.getRuntime();
91
92
93     public SshJcraftWrapper() {
94         this.jsch = new JSch();
95     }
96
97     SshJcraftWrapper(JSch jsch) {
98         this.jsch = jsch;
99     }
100
101     public void connect(String hostname, String username, String password, String prompt, int timeOut)
102         throws IOException {
103         log.debug("Attempting to connect to {0} username={1} prompt='{2}' timeOut={3}",
104             hostname, username, prompt, timeOut);
105         routerName = hostname;
106         hostName = hostname;
107         userName = username;
108         passWord = password;
109         try {
110             channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, timeOut);
111             ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
112             reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
113             channel.connect();
114             log.info("Successfully connected. Flushing input buffer.");
115             try {
116                 receiveUntil(prompt, 3000, "No cmd was sent, just waiting");
117             } catch (IOException e) {
118                 log.warn("Caught an Exception: Nothing to flush out.", e);
119             }
120         } catch (JSchException e) {
121             log.error("Could not connect to host=" + hostname, e);
122             throw new IOException(e.toString());
123         }
124     }
125
126     // User specifies the port number.
127     public void connect(String hostname, String username, String password, String prompt, int timeOut, int portNum)
128         throws IOException {
129         log.debug("Attempting to connect to {0} username={1} prompt='{2}' timeOut={3} portNum={4}",
130             hostname, username, prompt, timeOut, portNum);
131         routerName = hostname;
132         hostName = hostname;
133         userName = username;
134         passWord = password;
135         try {
136             channel = provideSessionChannel(CHANNEL_SHELL_TYPE, portNum, timeOut);
137             ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
138             reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
139             channel.connect();
140             log.info("Successfully connected. Flushing input buffer.");
141             try {
142                 if ("]]>]]>".equals(prompt)) {
143                     receiveUntil("]]>]]>", 10000, "No cmd was sent, just waiting");
144                 } else {
145                     receiveUntil(":~#", 5000, "No cmd was sent, just waiting");
146                 }
147             } catch (IOException e) {
148                 log.warn("Caught an Exception: Nothing to flush out.", e);
149             }
150         } catch (JSchException e) {
151             log.error("Could not connect to host=" + hostname, e);
152             throw new IOException(e.toString());
153         }
154     }
155
156
157     public String receiveUntil(String delimeters, int timeout, String cmdThatWasSent) throws IOException {
158         boolean match = false;
159         boolean cliPromptCmd = false;
160         StringBuilder sb = new StringBuilder();
161         StringBuilder sbReceive = new StringBuilder();
162         log.debug("delimeters='{0}' timeout={1} cmdThatWasSent='{2}'", delimeters, timeout, cmdThatWasSent);
163         int readCounts = 0;
164         aggregatedReceivedString = "";
165         FileWriter fileWriter = null;
166
167         long deadline = new Date().getTime() + timeout;
168         try {
169             session.setTimeout(timeout);  // This is the socket timeout value.
170             while (!match) {
171                 if (new Date().getTime() > deadline) {
172                     String formattedCmd = removeWhiteSpaceAndNewLineCharactersAroundString(cmdThatWasSent);
173                     log.error("Routine has timed out: routerName={0} CmdThatWasSent={1}", routerName, formattedCmd);
174                     throw new TimedOutException("Routine has timed out");
175                 }
176                 try {
177                     Thread.sleep(500);
178                 } catch (java.lang.InterruptedException ee) {
179                     Thread.currentThread().interrupt();
180                 }
181                 int len = reader.read(charBuffer, 0, BUFFER_SIZE);
182                 log.trace("After reader. Read command len={0}", len);
183                 if (len <= 0) {
184                     log.error("Reader failed to read any bytes. Suspected socket timeout, router={0}", routerName);
185                     throw new TimedOutException("Received a SocketTimeoutException router=" + routerName);
186                 }
187                 if (!cliPromptCmd) {
188                     if (cmdThatWasSent.indexOf("IOS_XR_uploadedSwConfigCmd") != -1) {
189                         if (out == null) {
190                             // This is a IOS XR sw config file. We will write it to the disk.
191                             timeout = timeout * 2;
192                             deadline = new Date().getTime() + timeout;
193                             log.debug("IOS XR upload for software config: timeout={0}", timeout);
194                             StringTokenizer st = new StringTokenizer(cmdThatWasSent);
195                             st.nextToken();
196                             routerFileName = st.nextToken();
197                             fileWriter = new FileWriter(routerFileName);
198                             out = new BufferedWriter(fileWriter);
199                             routerLogFileName = "/tmp/" + routerName;
200                             tmpFile = new File(routerLogFileName);
201                             log.debug("Prepared for writing swConfigFile to disk, routerFileName=" + routerFileName);
202                         }
203                         out.write(charBuffer, 0, len);
204                         out.flush();
205                         log.debug("{0} bytes has been written to the disk", len);
206                         if (tmpFile.exists()) {
207                             appendToRouterFile(routerLogFileName, len);
208                         }
209                         match = checkIfReceivedStringMatchesDelimeter(len, "\nXML>");
210                         if (match) {
211                             out.flush();
212                             out.close();
213                             out = null;
214                             return null;
215                         }
216                     } else {
217                         readCounts++;
218                         log.debug("Reader read {0} of data within {1} read iteration", len, readCounts);
219                         int c;
220                         sb.setLength(0);
221                         for (int i = 0; i < len; i++) {
222                             c = charBuffer[i];
223                             if ((c != 7) && (c != 13) && (c != 0) && (c != 27)) {
224                                 sbReceive.append(charBuffer[i]);
225                                 sb.append(charBuffer[i]);
226                             }
227                         }
228                         appendToRouterFile("/tmp/" + routerName, len);
229                         if (listener != null) {
230                             listener.receivedString(sb.toString());
231                         }
232                         match = checkIfReceivedStringMatchesDelimeter(delimeters, sb.toString(), cmdThatWasSent);
233                         if (match) {
234                             log.trace("Match was true, breaking the loop.");
235                             break;
236                         }
237                     }
238                 } else {
239                     log.trace("cliPromptCmd");
240                     sb.setLength(0);
241                     for (int i = 0; i < len; i++) {
242                         sbReceive.append(charBuffer[i]);
243                         sb.append(charBuffer[i]);
244                     }
245                     appendToRouterFile("/tmp/" + routerName, sb);
246                     if (listener != null) {
247                         listener.receivedString(sb.toString());
248                     }
249                     log.debug("sb2={0}  delimiters={1}", sb.toString(), delimeters);
250                     if (sb.toString().contains("\nariPrompt>")) {
251                         log.debug("Found ari prompt");
252                         break;
253                     }
254                 }
255             }
256         } catch (JSchException e) {
257             log.error("JSchException occurred", e);
258             throw new TimedOutException(e.getMessage());
259         } catch (IOException e) {
260             log.error("IOException occurred", e);
261             throw new TimedOutException(e.getMessage());
262         } finally {
263             try {
264                 if (fileWriter != null) {
265                     fileWriter.close();
266                 }
267             } catch (IOException ex) {
268                 log.warn("Failed to close fileWriter output stream", ex);
269             }
270         }
271         return stripOffCmdFromRouterResponse(sbReceive.toString());
272     }
273
274     public boolean checkIfReceivedStringMatchesDelimeter(String delimeters, String receivedString,
275         String cmdThatWasSent) {
276         // The delimeters are in a '|' seperated string. Return true on the first match.
277         log.debug("Entered checkIfReceivedStringMatchesDelimeter: delimeters={0} cmdThatWasSent={1} receivedString={2}",
278             delimeters, cmdThatWasSent, receivedString);
279         StringTokenizer st = new StringTokenizer(delimeters, "|");
280
281         if ((delimeters.contains("#$")) || ("CLI".equals(routerCmdType)))  // This would be an IOS XR, CLI command.
282         {
283             int x = receivedString.lastIndexOf('#');
284             int y = receivedString.length() - 1;
285             log.debug("IOS XR, CLI command");
286             if (log.isTraceEnabled()) {
287                 log.trace("cmdThatWasSent={0}, lastIndexOf hash delimiter={1}, maxIndexNum={2}", cmdThatWasSent, x, y);
288             }
289             return (x != -1) && (y == x);
290         }
291         if (cmdThatWasSent.contains("show config")) {
292             log.trace("In the block for 'show config'");
293             while (st.hasMoreTokens()) {
294                 String delimeter = st.nextToken();
295                 // Make sure we don't get faked out by a response of " #".
296                 // Proc #0
297                 //   # signaling-local-address ipv6 FD00:F4D5:EA06:1::110:136:254
298                 // LAAR2#
299                 int x = receivedString.lastIndexOf(delimeter);
300                 if ((receivedString.lastIndexOf(delimeter) != -1) && (receivedString.lastIndexOf(" #") != x - 1)) {
301                     log.debug("receivedString={0}", receivedString);
302                     log.trace("Found ending for 'show config' command, exiting.");
303                     return true;
304                 }
305             }
306         } else {
307             aggregatedReceivedString = aggregatedReceivedString + receivedString;
308             appendToFile("/tmp/aggregatedReceivedString.debug", aggregatedReceivedString);
309
310             log.debug("receivedString={0}", receivedString);
311             while (st.hasMoreTokens()) {
312                 String delimeter = st.nextToken();
313                 log.debug("Looking for an delimiter of:{0}", delimeter);
314                 if (aggregatedReceivedString.indexOf(delimeter) != -1) {
315                     log.debug("Found delimiter={0}, exiting", delimeter);
316                     aggregatedReceivedString = "";
317                     return true;
318                 }
319             }
320         }
321         return false;
322     }
323
324     public boolean checkIfReceivedStringMatchesDelimeter(int len, String delimeter) {
325         int x;
326         int c;
327         String str = StringUtils.EMPTY;
328
329         if (jcraftReadSwConfigFileFromDisk()) {
330             log.trace("jcraftReadSwConfigFileFromDisk block");
331             File fileName = new File(routerFileName);
332             log.debug("jcraftReadSwConfigFileFromDisk::: Will read the tail end of the file from the disk");
333             try {
334                 str = getLastFewLinesOfFile(fileName, 3);
335             } catch (IOException e) {
336                 log.warn("IOException occurred, while reading file=" + fileName, e);
337             }
338         } else {
339             // When looking at the end of the charBuffer, don't include any linefeeds or spaces. We only want to make the smallest string possible.
340             for (x = len - 1; x >= 0; x--) {
341                 c = charBuffer[x];
342                 if ((c != 10) && (c != 32)) // Not a line feed nor a space.
343                 {
344                     break;
345                 }
346             }
347             if ((x + 1 - 13) >= 0) {
348                 str = new String(charBuffer, x + 1 - 13, 13);
349                 log.debug("str:{0}", str);
350             } else {
351                 File fileName = new File(routerFileName);
352                 log.debug("Will read the tail end of the file from the disk, x={0} len={1} str={2} routerFileName={3}",
353                     x, len, str, routerFileName);
354                 try {
355                     str = getLastFewLinesOfFile(fileName, 3);
356                 } catch (IOException e) {
357                     log.warn("IOException occurred, while reading file=" + fileName, e);
358                 }
359             }
360         }
361
362         log.debug("Parsed string was str='{0}', searched delimiter was {1}");
363         return str.contains(delimeter);
364     }
365
366     public void closeConnection() {
367         log.info("Closing connection");
368         try {
369             if (reader != null) {
370                 reader.close();
371             }
372         } catch(IOException ex) {
373             log.warn("Could not close reader instance", ex);
374         } finally {
375             if(isConnected()) {
376                 channel.disconnect();
377                 session.disconnect();
378                 channel = null;
379                 session = null;
380             }
381             reader = null;
382         }
383     }
384
385     boolean isConnected() {
386         return (channel != null && session != null);
387     }
388
389     public void send(String cmd) throws IOException {
390         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
391             sendSshCommand(cmd, dos);
392         } catch (IOException e) {
393             log.error("IOException occurred while sending command=" + cmd, e);
394             throw e;
395         }
396     }
397
398     public void sendChar(int v) throws IOException {
399         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
400             if (log.isTraceEnabled()) {
401                 log.trace("Sending charCode: {0}", v);
402             }
403             dos.writeChar(v);
404             dos.flush();
405         } catch (IOException e) {
406             log.error("IOException occurred while writing char to channel output stream", e);
407             throw e;
408         }
409     }
410
411     public void send(byte[] b, int off, int len) throws IOException {
412         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
413             dos.write(b, off, len);
414             dos.flush();
415         } catch (IOException e) {
416             log.error("IOException occurred while writing bytes to channel output stream", e);
417             throw e;
418         }
419     }
420
421     public static class MyUserInfo implements UserInfo, UIKeyboardInteractive {
422
423         @Override
424         public String getPassword() {
425             return null;
426         }
427
428         @Override
429         public boolean promptYesNo(String str) {
430             return false;
431         }
432
433         @Override
434         public String getPassphrase() {
435             return null;
436         }
437
438         @Override
439         public boolean promptPassphrase(String message) {
440             return false;
441         }
442
443         @Override
444         public boolean promptPassword(String message) {
445             return false;
446         }
447
448         @Override
449         public void showMessage(String message) {
450             //stub
451         }
452
453         @Override
454         public String[] promptKeyboardInteractive(String destination,
455             String name,
456             String instruction,
457             String[] prompt,
458             boolean[] echo) {
459             return new String[0];
460         }
461     }
462
463     public void addListener(TelnetListener listener) {
464         this.listener = listener;
465     }
466
467     private void appendToFile(String fileName, String dataToWrite) {
468         File outputFile = new File(fileName);
469         if (outputFile.exists()) {
470             try (FileWriter fw = new FileWriter(fileName, true); BufferedWriter ow = new BufferedWriter(fw)) {
471                 ow.write(dataToWrite);
472                 ow.close();
473             } catch (IOException e) {
474                 log.error("IOException occurred while writing to file=" + fileName, e);
475             }
476         }
477     }
478
479     public String getTheDate() {
480         DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy H:mm:ss  ");
481         return dateFormat.format(Calendar.getInstance().getTime());
482     }
483
484
485     public void appendToRouterFile(String fileName, StringBuilder dataToWrite) {
486         appendToFile(fileName, dataToWrite.toString());
487     }
488
489     public void appendToRouterFile(String fileName, int len) {
490         File outputFile = new File(fileName);
491         if (outputFile.exists()) {
492             try (FileWriter fw = new FileWriter(fileName, true); BufferedWriter ow = new BufferedWriter(fw)) {
493                 ow.write(charBuffer, 0, len);
494                 ow.close();
495             } catch (IOException e) {
496                 log.error("IOException occurred while writing to router file=" + fileName, e);
497             }
498         }
499     }
500
501     public String removeWhiteSpaceAndNewLineCharactersAroundString(String str) {
502         if (str != null && !StringUtils.EMPTY.equals(str)) {
503             StringTokenizer strTok = new StringTokenizer(str, "\n");
504             StringBuilder sb = new StringBuilder();
505
506             while (strTok.hasMoreTokens()) {
507                 String line = strTok.nextToken();
508                 sb.append(line);
509             }
510             return sb.toString().trim();
511         }
512         return StringUtils.EMPTY;
513     }
514
515     public String stripOffCmdFromRouterResponse(String routerResponse) {
516         // The session of SSH will echo the command sent to the router, in the router's response.
517         // Since all our commands are terminated by a '\n', strip off the first line
518         // of the response from the router. This first line contains the orginal command.
519         StringTokenizer rr = new StringTokenizer(routerResponse, "\n");
520         StringBuilder sb = new StringBuilder();
521
522         int numTokens = rr.countTokens();
523         if (numTokens > 1) {
524             rr.nextToken(); //Skip the first line.
525             while (rr.hasMoreTokens()) {
526                 sb.append(rr.nextToken()).append("\n");
527             }
528         }
529         return sb.toString();
530     }
531
532     public void setRouterCommandType(String type) {
533         this.routerCmdType = type;
534         log.debug("Router command type is set to: {0}", type);
535     }
536
537     public String getLastFewLinesOfFile(File file, int linesToRead) throws IOException {
538         RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
539         int lines = 0;
540         StringBuilder builder = new StringBuilder();
541         String tail = "";
542         long length = file.length();
543         length--;
544         randomAccessFile.seek(length);
545         for (long seek = length; seek >= 0; --seek) {
546             randomAccessFile.seek(seek);
547             char c = (char) randomAccessFile.read();
548             builder.append(c);
549             if (c == '\n') {
550                 builder = builder.reverse();
551                 tail = builder.toString() + tail;
552                 lines++;
553                 builder.setLength(0);
554                 if (lines == linesToRead) {
555                     break;
556                 }
557             }
558         }
559         randomAccessFile.close();
560         if (log.isDebugEnabled()) {
561             log.debug("Content read from file={0} was tail={1}", file.getName(), tail);
562         }
563         return tail;
564     }
565
566     public boolean jcraftReadSwConfigFileFromDisk() {
567         return jcraftReadSwConfigFileFromDisk.exists();
568     }
569
570     public String getEquipNameCode() {
571         return equipNameCode;
572     }
573
574     public void setEquipNameCode(String equipNameCode) {
575         this.equipNameCode = equipNameCode;
576     }
577
578     public String getRouterName() {
579         return routerName;
580     }
581
582     // Routine does reads until it has read 'nchars' or times out.
583     public void receiveUntilBufferFlush(int ncharsSent, int timeout, String message) throws IOException {
584         log.debug("ncharsSent={0}, timeout={1}, message={2}", ncharsSent, timeout, message);
585         int ncharsTotalReceived = 0;
586         int ncharsRead = 0;
587
588         long deadline = new Date().getTime() + timeout;
589         logMemoryUsage();
590         try {
591             session.setTimeout(timeout);  // This is the socket timeout value.
592             while (true) {
593                 if (new Date().getTime() > deadline) {
594                     log.error("Routine has timed out: ncharsSent={0}, ncharsTotalReceived={1}", ncharsSent,
595                         ncharsTotalReceived);
596                     throw new TimedOutException("Routine has timed out");
597                 }
598                 ncharsRead = reader.read(charBuffer, 0, BUFFER_SIZE);
599                 if (listener != null) {
600                     listener.receivedString(String.copyValueOf(charBuffer, 0, ncharsRead));
601                 }
602                 appendToRouterFile("/tmp/" + routerName, ncharsRead);
603                 ncharsTotalReceived = ncharsTotalReceived + ncharsRead;
604                 if (ncharsTotalReceived >= ncharsSent) {
605                     log.debug("Received the correct number of characters, ncharsSent={0}, ncharsTotalReceived={1}",
606                         ncharsSent, ncharsTotalReceived);
607                     logMemoryUsage();
608                     return;
609                 }
610             }
611         } catch (JSchException e) {
612             log.error("JSchException occurred while command sending", e);
613             log.debug("ncharsSent={0}, ncharsTotalReceived={1}, ncharsRead={2} until error occurred",
614                 ncharsSent, ncharsTotalReceived, ncharsRead);
615             throw new TimedOutException(e.getMessage());
616         }
617     }
618
619     public String getHostName() {
620         return hostName;
621     }
622
623     public String getUserName() {
624         return userName;
625     }
626
627     public String getPassWord() {
628         return passWord;
629     }
630
631     public void sftpPutFile(String sourcePath, String destDirectory) throws IOException {
632         try {
633             Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
634             UserInfo ui = new MyUserInfo();
635             sftpSession.setPassword(passWord);
636             sftpSession.setUserInfo(ui);
637             sftpSession.connect(30 * 1000);
638             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
639             sftp.connect();
640             log.debug("Sending via sftp from source: {0} to destination: {1}", sourcePath, destDirectory);
641             sftp.put(sourcePath, destDirectory, ChannelSftp.OVERWRITE);
642             sftpSession.disconnect();
643         } catch (JSchException ex) {
644             log.error("JSchException occurred while handling sftp session", ex);
645             throw new IOException(ex.getMessage());
646         } catch (SftpException ex) {
647             log.error("SftpException occurred during file transfer", ex);
648             throw new IOException(ex.getMessage());
649         }
650     }
651
652     public void sftpPutStringData(String stringOfData, String fullPathDest) throws IOException {
653         try {
654             Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
655             UserInfo ui = new MyUserInfo();
656             sftpSession.setPassword(passWord);
657             sftpSession.setUserInfo(ui);
658             sftpSession.connect(30 * 1000);
659             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
660             sftp.connect();
661             InputStream is = new ByteArrayInputStream(stringOfData.getBytes());
662             log.debug("Sending via sftp stringOfData to destination: {0}", fullPathDest);
663             sftp.put(is, fullPathDest, ChannelSftp.OVERWRITE);
664             sftpSession.disconnect();
665         } catch (JSchException ex) {
666             log.error("JSchException occurred while handling sftp session", ex);
667             throw new IOException(ex.getMessage());
668         } catch (SftpException ex) {
669             log.error("SftpException occurred during data transfer", ex);
670             throw new IOException(ex.getMessage());
671         }
672     }
673
674     public String sftpGet(String fullFilePathName) throws IOException {
675         try {
676             Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
677             UserInfo ui = new MyUserInfo();
678             sftpSession.setPassword(passWord);
679             sftpSession.setUserInfo(ui);
680             sftpSession.connect(30 * 1000);
681             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
682             sftp.connect();
683             InputStream in = sftp.get(fullFilePathName);
684             String sftpFileString = readInputStreamAsString(in);
685             log.debug("Received data via sftp connection sftpFileString={0} from fullFilePathName={1}",
686                 sftpFileString, fullFilePathName);
687             sftpSession.disconnect();
688             return sftpFileString;
689         } catch (JSchException ex) {
690             log.error("JSchException occurred while handling sftp session", ex);
691             throw new IOException(ex.getMessage());
692         } catch (SftpException ex) {
693             log.error("SftpException occurred during data transfer", ex);
694             throw new IOException(ex.getMessage());
695         }
696     }
697
698     public static String readInputStreamAsString(InputStream in) throws IOException {
699         BufferedInputStream bis = new BufferedInputStream(in);
700         ByteArrayOutputStream buf = new ByteArrayOutputStream();
701         int result = bis.read();
702         while (result != -1) {
703             byte b = (byte) result;
704             buf.write(b);
705             result = bis.read();
706         }
707         return buf.toString();
708     }
709
710
711     public void logMemoryUsage() {
712         int mb = 1024 * 1024;
713         long usedMemory;
714         long maxMemoryAvailable;
715         long memoryLeftOnHeap;
716         maxMemoryAvailable =runtime.maxMemory() / mb;
717         usedMemory = (runtime.totalMemory() / mb) - (runtime.freeMemory() / mb);
718         memoryLeftOnHeap = maxMemoryAvailable - usedMemory;
719         log.info("Memory usage: maxMemoryAvailable={0}, usedMemory={1}, memoryLeftOnHeap={2}",
720             maxMemoryAvailable, usedMemory, memoryLeftOnHeap);
721     }
722
723     public void connect(String hostname, String username, String password, int timeOut, int portNum,
724         String subsystem) throws IOException {
725
726         if (log.isDebugEnabled()) {
727             log.debug(
728                 "Attempting to connect to {0} username={1} timeOut={2} portNum={3} subsystem={4}",
729                 hostname, username, timeOut, portNum, subsystem);
730         }
731         this.routerName = hostname;
732         this.hostName = hostname;
733         this.userName = username;
734         this.passWord = password;
735         try {
736             channel = provideSessionChannel(CHANNEL_SUBSYSTEM_TYPE, portNum, timeOut);
737             ((ChannelSubsystem) channel).setSubsystem(subsystem);
738             ((ChannelSubsystem) channel).setPty(true); //expected ptyType vt102
739             reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
740             channel.connect(5000);
741         } catch (JSchException e) {
742             log.error("JschException occurred ", e);
743             throw new IOException(e.getMessage());
744         }
745     }
746
747     public void connect(String hostName, String username, String password) throws IOException {
748         log.debug("Attempting to connect to {0} username={1} portNumber={2}", hostName, username, DEFAULT_PORT);
749         this.routerName = hostName;
750         this.hostName = hostName;
751         this.userName = username;
752         this.passWord = password;
753         try {
754             channel = provideSessionChannel(CHANNEL_SHELL_TYPE, DEFAULT_PORT, 30000);
755             ((ChannelShell) channel).setPtyType(TERMINAL_BASIC_MODE);
756             reader = new BufferedReader(new InputStreamReader(new DataInputStream(channel.getInputStream())), BUFFER_SIZE);
757             channel.connect();
758             try {
759                 receiveUntil(":~#", 9000, "No cmd was sent, just waiting, but we can stop on a '~#'");
760             } catch (Exception e) {
761                 log.warn("Caught an Exception: Nothing to flush out.", e);
762             }
763
764         } catch (JSchException e) {
765             log.error("JschException occurred ", e);
766             throw new IOException(e.getMessage());
767         }
768     }
769
770
771     public void put(String sourcePath, String destDirectory) throws IOException {
772         try {
773             Session sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
774             UserInfo ui = new MyUserInfo();
775             sftpSession.setPassword(passWord);
776             sftpSession.setUserInfo(ui);
777             sftpSession.connect(30 * 1000);
778             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
779             sftp.connect();
780             log.debug("Sending via sftp from source: {0} to destination: {1}", sourcePath, destDirectory);
781             sftp.put(sourcePath, destDirectory, ChannelSftp.OVERWRITE);
782             sftpSession.disconnect();
783         } catch (JSchException ex) {
784             log.error("JSchException occurred while handling sftp session", ex);
785             throw new IOException(ex.getMessage());
786         } catch (SftpException ex) {
787             log.error("SftpException occurred during file transfer", ex);
788             throw new IOException(ex.getMessage());
789         }
790     }
791
792     public void put(InputStream is, String fullPathDest, String hostName, String userName, String passWord)
793         throws IOException {
794         Session sftpSession = null;
795         try {
796             log.debug("Sftp put invoked, connection details: username={1} hostname={2}",
797                 userName, hostName);
798             jsch = new JSch();
799             java.util.Properties config = new java.util.Properties();
800             config.put("StrictHostKeyChecking", "no");
801             sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
802             UserInfo ui = new MyUserInfo();
803             sftpSession.setPassword(passWord);
804             sftpSession.setUserInfo(ui);
805             sftpSession.setConfig(config);
806             sftpSession.connect(30 * 1000);
807             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
808             sftp.connect();
809             String oldFiles = fullPathDest + "*";
810             log.debug("Deleting old files: {0}", oldFiles);
811             try {
812                 sftp.rm(oldFiles);
813             } catch (SftpException ex) {
814                 String exp = "No such file";
815                 if (ex.getMessage() != null && ex.getMessage().contains(exp)) {
816                     log.warn("No files found, continue");
817                 } else {
818                     log.error("SftpException while invoking rm command over sftp", ex);
819                     throw ex;
820                 }
821             }
822             log.debug("Sending stringOfData to destination {0}", fullPathDest);
823             sftp.put(is, fullPathDest, ChannelSftp.OVERWRITE);
824         } catch (JSchException ex) {
825             log.error("JSchException occurred while handling sftp session", ex);
826             throw new IOException(ex.getMessage());
827         } catch (SftpException ex) {
828             log.error("SftpException occurred during file transfer", ex);
829             throw new IOException(ex.getMessage());
830         } finally {
831             if (sftpSession != null) {
832                 sftpSession.disconnect();
833             }
834         }
835     }
836
837     public String get(String fullFilePathName, String hostName, String userName, String passWord) throws IOException {
838         Session sftpSession = null;
839         try {
840             log.debug("Sftp get invoked, connection details: username={1} hostname={2}",
841                 userName, hostName);
842             jsch = new JSch();
843             sftpSession = jsch.getSession(userName, hostName, DEFAULT_PORT);
844             java.util.Properties config = new java.util.Properties();
845             config.put("StrictHostKeyChecking", "no");
846             UserInfo ui = new MyUserInfo();
847             sftpSession.setPassword(passWord);
848             sftpSession.setUserInfo(ui);
849             sftpSession.setConfig(config);
850             sftpSession.connect(30 * 1000);
851             ChannelSftp sftp = (ChannelSftp) sftpSession.openChannel("sftp");
852             sftp.connect();
853             InputStream in = sftp.get(fullFilePathName);
854             return readInputStreamAsString(in);
855         } catch (JSchException ex) {
856             log.error("JSchException occurred while handling sftp session", ex);
857             throw new IOException(ex.getMessage());
858         } catch (SftpException ex) {
859             log.error("SftpException occurred during file transfer", ex);
860             throw new IOException(ex.getMessage());
861         } finally {
862             if (sftpSession != null) {
863                 sftpSession.disconnect();
864             }
865         }
866     }
867
868     public String send(String cmd, String delimiter) throws IOException {
869         try (OutputStream os = channel.getOutputStream(); DataOutputStream dos = new DataOutputStream(os)) {
870             sendSshCommand(cmd, dos);
871             return receiveUntil(delimiter, 300000, cmd);
872         } catch (IOException ex) {
873             log.error("IOException occurred", ex);
874             throw new IOException(ex.getMessage());
875         }
876     }
877
878     private void sendSshCommand(@Nonnull String originalCommand, @Nonnull DataOutputStream channelOutputStream)
879         throws IOException {
880         String command = enhanceCommandWithEOL(originalCommand);
881         int length = command.length(); // 2,937,706
882         int charsChunkSize = 300000;
883         int charsTotalSent = 0;
884
885         log.debug("Sending ssh command: length={0}, payload: {1}", length, command);
886         if (isCmdLengthEnoughToSendInChunks(length, charsChunkSize)) {
887             int timeout = 9000;
888             for (int i = 0; i < length; i += charsChunkSize) {
889                 String commandChunk = command.substring(i, Math.min(length, i + charsChunkSize));
890                 int numCharsSentInChunk = commandChunk.length();
891                 charsTotalSent = charsTotalSent + commandChunk.length();
892                 log.debug("Iteration nr:{0}, sending command chunk: {1}", i, numCharsSentInChunk);
893                 channelOutputStream.writeBytes(commandChunk);
894                 channelOutputStream.flush();
895                 try {
896                     if (numCharsSentInChunk < length) {
897                         receiveUntilBufferFlush(numCharsSentInChunk, timeout, "buffer flush  i=" + i);
898                     } else {
899                         log.trace("i={0}, flush immediately", i);
900                         channelOutputStream.flush();
901                     }
902                 } catch (IOException ex) {
903                     log.warn("IOException occurred: nothing to flush out", ex);
904                 }
905             }
906         } else {
907             channelOutputStream.writeBytes(command);
908         }
909         channelOutputStream.flush();
910     }
911
912     private boolean isCmdLengthEnoughToSendInChunks(int length, int chunkSize) {
913         return length > 2 * chunkSize;
914     }
915
916     private String enhanceCommandWithEOL(@Nonnull String originalCommand) {
917         char commandEnding = originalCommand.charAt(originalCommand.length() - 1);
918         if (commandEnding != '\n' && commandEnding != '\r') {
919             return originalCommand + "\n";
920         }
921         return originalCommand;
922     }
923
924     private Channel provideSessionChannel(String channelType, int port, int timeout) throws JSchException {
925         session = jsch.getSession(this.userName, this.hostName, port);
926         session.setPassword(this.passWord);
927         session.setUserInfo(new MyUserInfo()); //needed?
928         session.setConfig(STRICT_HOST_CHECK_KEY, STRICT_HOST_CHECK_VALUE);
929         session.connect(timeout);
930         session.setServerAliveCountMax(
931             0); // If this is not set to '0', then socket timeout on all reads will not work!!!!
932         return session.openChannel(channelType);
933     }
934 }