in brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjTool.java [960:1058]
public Integer create() throws Exception {
try {
session = acquire(newSessionAction());
shell = session.startShell();
if (out != null) {
InputStream outstream = shell.getInputStream();
outgobbler = new StreamGobbler(outstream, out, (Logger)null);
outgobbler.start();
}
if (err != null) {
InputStream errstream = shell.getErrorStream();
errgobbler = new StreamGobbler(errstream, err, (Logger)null);
errgobbler.start();
}
OutputStream output = shell.getOutputStream();
for (CharSequence cmd : commands) {
try {
output.write(toUTF8ByteArray(cmd+"\n"));
output.flush();
} catch (ConnectionException e) {
if (!shell.isOpen()) {
// shell is closed; presumably the user command did `exit`
if (LOG.isDebugEnabled()) LOG.debug("Shell closed to {} when executing {}", SshjTool.this.toString(), commands);
break;
} else {
throw e;
}
}
}
// workaround attempt for SSHJ deadlock - https://github.com/shikhar/sshj/issues/105
synchronized (shell.getOutputStream()) {
shell.sendEOF();
}
closeWhispering(output, this);
boolean timedOut = false;
try {
long timeoutMillis = Math.min(timeout.toMilliseconds(), Integer.MAX_VALUE);
long timeoutEnd = System.currentTimeMillis() + timeoutMillis;
Exception last = null;
do {
if (!shell.isOpen() && ((SessionChannel)session).getExitStatus()!=null)
// shell closed, and exit status returned
break;
boolean endBecauseReturned =
// if either condition is satisfied, then wait 1s in hopes the other does, then return
(!shell.isOpen() || ((SessionChannel)session).getExitStatus()!=null);
try {
shell.join(1000, TimeUnit.MILLISECONDS);
} catch (ConnectionException e) {
last = e;
}
if (endBecauseReturned) {
// shell is still open, ie some process is running
// but we have a result code, so main shell is finished
// we waited one second extra to allow any background process
// which is nohupped to really be in the background (#162)
// now let's bail out
break;
}
} while (System.currentTimeMillis() < timeoutEnd);
if (shell.isOpen() && ((SessionChannel)session).getExitStatus()==null) {
LOG.debug("Timeout ({}) in SSH shell to {}", timeout, this);
// we timed out, or other problem -- reproduce the error.
// The shell.join should always have thrown ConnectionExceptoin (looking at code of
// AbstractChannel), but javadoc of Channel doesn't explicity say that so play it safe.
timedOut = true;
throw (last != null) ? last : new TimeoutException("Timeout after "+timeout+" executing "+this);
}
return ((SessionChannel)session).getExitStatus();
} finally {
// wait for all stdout/stderr to have been re-directed
closeWhispering(shell, this);
shell = null;
try {
// Don't use forever (i.e. 0) because BROOKLYN-106: ssh hangs
long joinTimeout = (timedOut) ? 1000 : 10*1000;
if (outgobbler != null) {
outgobbler.join(joinTimeout);
outgobbler.close();
}
if (errgobbler != null) {
errgobbler.join(joinTimeout);
errgobbler.close();
}
} catch (InterruptedException e) {
LOG.warn("Interrupted gobbling streams from ssh: "+commands, e);
Thread.currentThread().interrupt();
}
}
} finally {
clear();
}
}