@@ -32,6 +32,12 @@ public class SshHelper {
3232 private static final int DEFAULT_CONNECT_TIMEOUT = 180000 ;
3333 private static final int DEFAULT_KEX_TIMEOUT = 60000 ;
3434
35+ /**
36+ * Waiting time to check if the SSH session was successfully opened. This value (of 1000
37+ * milliseconds) represents one (1) second.
38+ */
39+ private static final int WAITING_OPEN_SSH_SESSION = 1000 ;
40+
3541 private static final Logger s_logger = Logger .getLogger (SshHelper .class );
3642
3743 public static Pair <Boolean , String > sshExecute (String host , int port , String user , File pemKeyFile , String password , String command ) throws Exception {
@@ -40,19 +46,19 @@ public static Pair<Boolean, String> sshExecute(String host, int port, String use
4046 }
4147
4248 public static void scpTo (String host , int port , String user , File pemKeyFile , String password , String remoteTargetDirectory , String localFile , String fileMode )
43- throws Exception {
49+ throws Exception {
4450
4551 scpTo (host , port , user , pemKeyFile , password , remoteTargetDirectory , localFile , fileMode , DEFAULT_CONNECT_TIMEOUT , DEFAULT_KEX_TIMEOUT );
4652 }
4753
4854 public static void scpTo (String host , int port , String user , File pemKeyFile , String password , String remoteTargetDirectory , byte [] data , String remoteFileName ,
49- String fileMode ) throws Exception {
55+ String fileMode ) throws Exception {
5056
5157 scpTo (host , port , user , pemKeyFile , password , remoteTargetDirectory , data , remoteFileName , fileMode , DEFAULT_CONNECT_TIMEOUT , DEFAULT_KEX_TIMEOUT );
5258 }
5359
5460 public static void scpTo (String host , int port , String user , File pemKeyFile , String password , String remoteTargetDirectory , String localFile , String fileMode ,
55- int connectTimeoutInMs , int kexTimeoutInMs ) throws Exception {
61+ int connectTimeoutInMs , int kexTimeoutInMs ) throws Exception {
5662
5763 com .trilead .ssh2 .Connection conn = null ;
5864 com .trilead .ssh2 .SCPClient scpClient = null ;
@@ -88,7 +94,7 @@ public static void scpTo(String host, int port, String user, File pemKeyFile, St
8894 }
8995
9096 public static void scpTo (String host , int port , String user , File pemKeyFile , String password , String remoteTargetDirectory , byte [] data , String remoteFileName ,
91- String fileMode , int connectTimeoutInMs , int kexTimeoutInMs ) throws Exception {
97+ String fileMode , int connectTimeoutInMs , int kexTimeoutInMs ) throws Exception {
9298
9399 com .trilead .ssh2 .Connection conn = null ;
94100 com .trilead .ssh2 .SCPClient scpClient = null ;
@@ -123,7 +129,8 @@ public static void scpTo(String host, int port, String user, File pemKeyFile, St
123129 }
124130
125131 public static Pair <Boolean , String > sshExecute (String host , int port , String user , File pemKeyFile , String password , String command , int connectTimeoutInMs ,
126- int kexTimeoutInMs , int waitResultTimeoutInMs ) throws Exception {
132+ int kexTimeoutInMs ,
133+ int waitResultTimeoutInMs ) throws Exception {
127134
128135 com .trilead .ssh2 .Connection conn = null ;
129136 com .trilead .ssh2 .Session sess = null ;
@@ -146,6 +153,8 @@ public static Pair<Boolean, String> sshExecute(String host, int port, String use
146153 }
147154 sess = conn .openSession ();
148155
156+ throwSshExceptionIfSshConnectionIsNull (sess );
157+
149158 sess .execCommand (command );
150159
151160 InputStream stdout = sess .getStdout ();
@@ -156,22 +165,22 @@ public static Pair<Boolean, String> sshExecute(String host, int port, String use
156165
157166 int currentReadBytes = 0 ;
158167 while (true ) {
168+ throwSshExceptionIfStdoutOrStdeerIsNull (stdout , stderr );
169+
159170 if ((stdout .available () == 0 ) && (stderr .available () == 0 )) {
160- int conditions =
161- sess .waitForCondition (ChannelCondition .STDOUT_DATA | ChannelCondition .STDERR_DATA | ChannelCondition .EOF | ChannelCondition .EXIT_STATUS ,
171+ int conditions = sess .waitForCondition (ChannelCondition .STDOUT_DATA | ChannelCondition .STDERR_DATA | ChannelCondition .EOF | ChannelCondition .EXIT_STATUS ,
162172 waitResultTimeoutInMs );
163173
164- if ((conditions & ChannelCondition .TIMEOUT ) != 0 ) {
165- String msg = "Timed out in waiting SSH execution result" ;
166- s_logger .error (msg );
167- throw new Exception (msg );
168- }
174+ throwSshExceptionIfConditionsTimeout (conditions );
169175
170176 if ((conditions & ChannelCondition .EXIT_STATUS ) != 0 ) {
171- if ((conditions & (ChannelCondition .STDOUT_DATA | ChannelCondition .STDERR_DATA )) == 0 ) {
172- break ;
173- }
177+ break ;
178+ }
179+
180+ if (canEndTheSshConnection (waitResultTimeoutInMs , sess , conditions )) {
181+ break ;
174182 }
183+
175184 }
176185
177186 while (stdout .available () > 0 ) {
@@ -189,11 +198,12 @@ public static Pair<Boolean, String> sshExecute(String host, int port, String use
189198
190199 if (sess .getExitStatus () == null ) {
191200 //Exit status is NOT available. Returning failure result.
201+ s_logger .error (String .format ("SSH execution of command %s has no exit status set. Result output: %s" , command , result ));
192202 return new Pair <Boolean , String >(false , result );
193203 }
194204
195205 if (sess .getExitStatus () != null && sess .getExitStatus ().intValue () != 0 ) {
196- s_logger .error ("SSH execution of command " + command + " has an error status code in return. result output: " + result );
206+ s_logger .error (String . format ( "SSH execution of command %s has an error status code in return. Result output: %s" , command , result ) );
197207 return new Pair <Boolean , String >(false , result );
198208 }
199209
@@ -206,4 +216,91 @@ public static Pair<Boolean, String> sshExecute(String host, int port, String use
206216 conn .close ();
207217 }
208218 }
219+
220+ /**
221+ * Handles the SSH connection in case of timeout or exit. If the session ends with a timeout
222+ * condition, it throws an exception; if the channel reaches an end of file condition, but it
223+ * does not have an exit status, it returns true to break the loop; otherwise, it returns
224+ * false.
225+ *
226+ * @param waitResultTimeoutInMs
227+ * @param sess
228+ * @param conditions
229+ * @return boolean
230+ * @throws SshException
231+ */
232+ protected static boolean canEndTheSshConnection (int waitResultTimeoutInMs , com .trilead .ssh2 .Session sess , int conditions ) throws SshException {
233+ if (isChannelConditionEof (conditions )) {
234+ int newConditions = sess .waitForCondition (ChannelCondition .EXIT_STATUS , waitResultTimeoutInMs );
235+ throwSshExceptionIfConditionsTimeout (newConditions );
236+ if ((newConditions & ChannelCondition .EXIT_STATUS ) != 0 ) {
237+ return true ;
238+ }
239+ }
240+ return false ;
241+ }
242+
243+ /**
244+ * It throws a {@link SshException} if the channel condition is {@link ChannelCondition#TIMEOUT}
245+ *
246+ * @param conditions
247+ * @throws SshException
248+ */
249+ protected static void throwSshExceptionIfConditionsTimeout (int conditions ) throws SshException {
250+ if ((conditions & ChannelCondition .TIMEOUT ) != 0 ) {
251+ String msg = "Timed out in waiting for SSH execution exit status" ;
252+ s_logger .error (msg );
253+ throw new SshException (msg );
254+ }
255+ }
256+
257+ /**
258+ * Checks if the channel condition mask is of {@link ChannelCondition#EOF} and not
259+ * {@link ChannelCondition#STDERR_DATA} or {@link ChannelCondition#STDOUT_DATA}.
260+ *
261+ * @param conditions
262+ * @return boolean
263+ */
264+ protected static boolean isChannelConditionEof (int conditions ) {
265+ if ((conditions & ChannelCondition .EOF ) != 0 ) {
266+ return true ;
267+ }
268+ return false ;
269+ }
270+
271+ /**
272+ * Checks if the SSH session {@link com.trilead.ssh2.Session#getStdout()} or
273+ * {@link com.trilead.ssh2.Session#getStderr()} is null.
274+ *
275+ * @param stdout
276+ * @param stderr
277+ * @throws SshException
278+ */
279+ protected static void throwSshExceptionIfStdoutOrStdeerIsNull (InputStream stdout , InputStream stderr ) throws SshException {
280+ if (stdout == null || stderr == null ) {
281+ String msg = "Stdout or Stderr of SSH session is null" ;
282+ s_logger .error (msg );
283+ throw new SshException (msg );
284+ }
285+ }
286+
287+ /**
288+ * Checks if the session is open. It waits {@link SshHelper#WAITING_OPEN_SSH_SESSION}
289+ * milliseconds; if the session is not open after the waiting time, then it assumes that the
290+ * session could not open a SSH connection.
291+ *
292+ * @param sess
293+ * @throws InterruptedException
294+ * @throws SshException
295+ */
296+ protected static void throwSshExceptionIfSshConnectionIsNull (com .trilead .ssh2 .Session sess ) throws InterruptedException , SshException {
297+ Thread .sleep (WAITING_OPEN_SSH_SESSION );
298+
299+ if (sess == null ) {
300+ String msg = "Cannot open SSH session" ;
301+ s_logger .error (msg );
302+ throw new SshException (msg );
303+ }
304+ }
305+
209306}
0 commit comments