bump sshj version
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/c3de6287 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/c3de6287 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/c3de6287 Branch: refs/heads/master Commit: c3de62874f04f74bc3b542ff8f4db7e1a9a96010 Parents: 9f8a3aa Author: Andrea Turli <andrea.tu...@gmail.com> Authored: Fri Sep 16 17:50:03 2016 +0200 Committer: Andrea Turli <andrea.tu...@gmail.com> Committed: Tue Dec 6 15:29:57 2016 +0100 ---------------------------------------------------------------------- core/pom.xml | 2 +- .../util/core/internal/ssh/sshj/SshjTool.java | 206 +++++++++---------- parent/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 102 insertions(+), 110 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c3de6287/core/pom.xml ---------------------------------------------------------------------- diff --git a/core/pom.xml b/core/pom.xml index f133515..02c6cbf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -58,7 +58,7 @@ <version>${project.version}</version> </dependency> <dependency> - <groupId>net.schmizz</groupId> + <groupId>com.hierynomus</groupId> <artifactId>sshj</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c3de6287/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjTool.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjTool.java b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjTool.java index fa46af0..1ee02da 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjTool.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/sshj/SshjTool.java @@ -38,17 +38,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; -import net.schmizz.sshj.connection.ConnectionException; -import net.schmizz.sshj.connection.channel.direct.PTYMode; -import net.schmizz.sshj.connection.channel.direct.Session; -import net.schmizz.sshj.connection.channel.direct.Session.Command; -import net.schmizz.sshj.connection.channel.direct.Session.Shell; -import net.schmizz.sshj.connection.channel.direct.SessionChannel; -import net.schmizz.sshj.sftp.FileAttributes; -import net.schmizz.sshj.sftp.SFTPClient; -import net.schmizz.sshj.transport.TransportException; -import net.schmizz.sshj.xfer.InMemorySourceFile; - import org.apache.brooklyn.core.BrooklynFeatureEnablement; import org.apache.brooklyn.util.core.internal.ssh.BackoffLimitedRetryHandler; import org.apache.brooklyn.util.core.internal.ssh.ShellTool; @@ -56,11 +45,9 @@ import org.apache.brooklyn.util.core.internal.ssh.SshAbstractTool; import org.apache.brooklyn.util.core.internal.ssh.SshTool; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.exceptions.RuntimeTimeoutException; -import org.apache.brooklyn.util.io.FileUtil; import org.apache.brooklyn.util.repeat.Repeater; import org.apache.brooklyn.util.stream.KnownSizeInputStream; import org.apache.brooklyn.util.stream.StreamGobbler; -import org.apache.brooklyn.util.stream.Streams; import org.apache.brooklyn.util.text.Strings; import org.apache.brooklyn.util.time.Duration; import org.apache.brooklyn.util.time.Time; @@ -80,6 +67,19 @@ import com.google.common.io.CountingOutputStream; import com.google.common.net.HostAndPort; import com.google.common.primitives.Ints; +import net.schmizz.sshj.connection.ConnectionException; +import net.schmizz.sshj.connection.channel.direct.PTYMode; +import net.schmizz.sshj.connection.channel.direct.Session; +import net.schmizz.sshj.connection.channel.direct.Session.Command; +import net.schmizz.sshj.connection.channel.direct.Session.Shell; +import net.schmizz.sshj.connection.channel.direct.SessionChannel; +import net.schmizz.sshj.sftp.FileAttributes; +import net.schmizz.sshj.sftp.SFTPClient; +import net.schmizz.sshj.transport.TransportException; +import net.schmizz.sshj.xfer.FileSystemFile; +import net.schmizz.sshj.xfer.InMemorySourceFile; +import net.schmizz.sshj.xfer.LocalDestFile; + /** * For ssh and scp-style commands, using the sshj library. */ @@ -95,7 +95,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { * Having multiple threads call connect/disconnect is going to be brittle. With * our retries we can get away with it usually, but it's not good! * - * TODO need to upgrade sshj version from 0.8.1 to 0.9, but jclouds 1.7.2 still + * TODO need to upgrade sshj version from 0.8.1 to 0.9, but jclouds 1.7.2 still * relies on 0.8.1. In 0.9, it fixes the https://github.com/shikhar/sshj/issues/89 * so does not throw AssertionError. */ @@ -108,7 +108,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { /** Terminal type name for {@code allocatePTY} option. */ final static String TERM = "vt100"; // "dumb" - + private class CloseFtpChannelOnCloseInputStream extends ProxyInputStream { private final SFTPClient sftp; @@ -129,17 +129,17 @@ public class SshjTool extends SshAbstractTool implements SshTool { public static SshjToolBuilder builder() { return new SshjToolBuilder(); } - + public static class SshjToolBuilder extends Builder<SshjTool, SshjToolBuilder> { } - + public static class Builder<T extends SshjTool, B extends Builder<T,B>> extends AbstractSshToolBuilder<T,B> { protected long connectTimeout; protected long sessionTimeout; protected int sshTries = 4; //allow 4 tries by default, much safer protected long sshTriesTimeout = 2*60*1000; //allow 2 minutes by default (so if too slow trying sshTries times, abort anyway) protected long sshRetryDelay = 50L; - + @Override public B from(Map<String,?> props) { super.from(props); @@ -175,10 +175,10 @@ public class SshjTool extends SshAbstractTool implements SshTool { public SshjTool(Map<String,?> map) { this(builder().from(map)); } - + protected SshjTool(Builder<?,?> builder) { super(builder); - + sshTries = builder.sshTries; sshTriesTimeout = builder.sshTriesTimeout; backoffLimitedRetryHandler = new BackoffLimitedRetryHandler(sshTries, builder.sshRetryDelay); @@ -194,10 +194,10 @@ public class SshjTool extends SshAbstractTool implements SshTool { .connectTimeout(builder.connectTimeout) .sessionTimeout(builder.sessionTimeout) .build(); - + if (LOG.isTraceEnabled()) LOG.trace("Created SshTool {} ({})", this, System.identityHashCode(this)); } - + @Override public void connect() { try { @@ -231,19 +231,19 @@ public class SshjTool extends SshAbstractTool implements SshTool { public boolean isConnected() { return sshClientConnection.isConnected() && sshClientConnection.isAuthenticated(); } - + @Override public int copyToServer(java.util.Map<String,?> props, byte[] contents, String pathAndFileOnRemoteServer) { return copyToServer(props, newInputStreamSupplier(contents), contents.length, pathAndFileOnRemoteServer); } - + @Override public int copyToServer(Map<String,?> props, InputStream contents, String pathAndFileOnRemoteServer) { /* sshj needs to: * 1) to know the length of the InputStream to copy the file to perform copy; and * 2) re-read the input stream on retry if the first attempt fails. * For now, write it to a file, unless caller supplies a KnownSizeInputStream - * + * * (We could have a switch where we hold it in memory if less than some max size, * but most the routines should supply a string or byte array or similar, * so we probably don't come here too often.) @@ -259,12 +259,12 @@ public class SshjTool extends SshAbstractTool implements SshTool { } } } - + @Override public int copyToServer(Map<String,?> props, File localFile, String pathAndFileOnRemoteServer) { return copyToServer(props, newInputStreamSupplier(localFile), (int)localFile.length(), pathAndFileOnRemoteServer); } - + private int copyToServer(Map<String,?> props, Supplier<InputStream> contentsSupplier, long length, String pathAndFileOnRemoteServer) { acquire(new PutFileAction(props, pathAndFileOnRemoteServer, contentsSupplier, length)); return 0; // TODO Can we assume put will have thrown exception if failed? Rather than exit code != 0? @@ -273,13 +273,8 @@ public class SshjTool extends SshAbstractTool implements SshTool { @Override public int copyFromServer(Map<String,?> props, String pathAndFileOnRemoteServer, File localFile) { - InputStream contents = acquire(new GetFileAction(pathAndFileOnRemoteServer)); - try { - FileUtil.copyTo(contents, localFile); - return 0; // TODO Can we assume put will have thrown exception if failed? Rather than exit code != 0? - } finally { - Streams.closeQuietly(contents); - } + LocalDestFile localDestFile = acquire(new GetFileAction(pathAndFileOnRemoteServer)); + return 0; } /** @@ -304,11 +299,11 @@ public class SshjTool extends SshAbstractTool implements SshTool { * e.g. by putting every second command as "echo <uid>", and waiting for the stdout. * This gets fiddly... * </ul> - * + * * So on balance, the script-based approach seems most reliable, even if there is an overhead * of separate message(s) for copying the file! - * - * Another consideration is long-running scripts. On some clouds when executing a script that takes + * + * Another consideration is long-running scripts. On some clouds when executing a script that takes * several minutes, we have seen it fail with -1 (e.g. 1 in 20 times). This suggests the ssh connection * is being dropped. To avoid this problem, we can execute the script asynchronously, writing to files * the stdout/stderr/pid/exitStatus. We then periodically poll to retrieve the contents of these files. @@ -337,17 +332,17 @@ public class SshjTool extends SshAbstractTool implements SshTool { /** * Executes the script in the background (`nohup ... &`), and then executes other ssh commands to poll for the * stdout, stderr and exit code of that original process (which will each have been written to separate files). - * + * * The polling is a "long poll". That is, it executes a long-running ssh command to retrieve the stdout, etc. * If that long-poll command fails, then we just execute another one to pick up from where it left off. * This means we do not need to execute many ssh commands (which are expensive), but can still return promptly * when the command completes. - * + * * Much of this was motivated by https://issues.apache.org/jira/browse/BROOKLYN-106, which is no longer * an issue. The retries (e.g. in the upload-script) are arguably overkill given that {@link #acquire(SshAction)} * will already retry. However, leaving this in place as it could prove useful when working with flakey * networks in the future. - * + * * TODO There are (probably) issues with this method when using {@link ShellTool#PROP_RUN_AS_ROOT}. * I (Aled) saw the .pid file having an owner of root:root, and a failure message in stderr of: * -bash: line 3: /tmp/brooklyn-20150113-161203056-XMEo-move_install_dir_from_user_to_.pid: Permission denied @@ -362,12 +357,12 @@ public class SshjTool extends SshAbstractTool implements SshTool { private int stdoutCount = 0; private int stderrCount = 0; private Stopwatch timer; - + public int run() { timer = Stopwatch.createStarted(); final String scriptContents = toScript(props, commands, env); if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as async script: {}", host, scriptContents); - + // Upload script; try repeatedly because have seen timeout intermittently on vcloud-director (BROOKLYN-106 related). boolean uploadSuccess = Repeater.create("async script upload on "+SshjTool.this.toString()+" (for "+getSummary()+")") .backoffTo(maxDelayBetweenPolls) @@ -389,14 +384,14 @@ public class SshjTool extends SshAbstractTool implements SshTool { return true; }}) .run(); - + if (!uploadSuccess) { // Unexpected! Should have either returned true or have rethrown the exception; should never get false. String msg = "Unexpected state: repeated failure for async script upload on "+SshjTool.this.toString()+" ("+getSummary()+")"; LOG.warn(msg+"; rethrowing"); throw new IllegalStateException(msg); } - + // Execute script asynchronously int execResult = asInt(acquire(new ShellAction(buildRunScriptCommand(), out, err, execTimeout)), -1); if (execResult != 0) return execResult; @@ -417,16 +412,16 @@ public class SshjTool extends SshAbstractTool implements SshTool { return exitstatus != null; }}) .run(); - + if (!success) { // Timed out String msg = "Timeout for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")"; LOG.warn(msg+"; rethrowing"); throw new TimeoutException(msg); } - + return result.get(); - + } catch (Exception e) { LOG.debug("Problem polling for async script on "+SshjTool.this.toString()+" (for "+getSummary()+"); rethrowing after deleting temporary files", e); throw Exceptions.propagate(e); @@ -445,13 +440,13 @@ public class SshjTool extends SshAbstractTool implements SshTool { } } } - + Integer longPoll() throws IOException { // Long-polling to get stdout, stderr + exit status of async task. // If our long-poll disconnects, we will just re-execute. - // We wrap the stdout/stderr so that we can get the size count. + // We wrap the stdout/stderr so that we can get the size count. // If we disconnect, we will pick up from that char of the stream. - // TODO Additional stdout/stderr written by buildLongPollCommand() could interfere, + // TODO Additional stdout/stderr written by buildLongPollCommand() could interfere, // causing us to miss some characters. Duration nextPollTimeout = Duration.min(pollTimeout, Duration.millis(execTimeout.toMilliseconds()-timer.elapsed(TimeUnit.MILLISECONDS))); CountingOutputStream countingOut = (out == null) ? null : new CountingOutputStream(out); @@ -459,7 +454,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { List<String> pollCommand = buildLongPollCommand(stdoutCount, stderrCount, nextPollTimeout); Duration sshJoinTimeout = nextPollTimeout.add(Duration.TEN_SECONDS); ShellAction action = new ShellAction(pollCommand, countingOut, countingErr, sshJoinTimeout); - + int longPollResult; try { longPollResult = asInt(acquire(action, 3, nextPollTimeout), -1); @@ -469,11 +464,11 @@ public class SshjTool extends SshAbstractTool implements SshTool { } stdoutCount += (countingOut == null) ? 0 : countingOut.getCount(); stderrCount += (countingErr == null) ? 0 : countingErr.getCount(); - + if (longPollResult == 0) { if (LOG.isDebugEnabled()) LOG.debug("Long-poll succeeded (exit status 0) on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return longPollResult; // success - + } else if (longPollResult == -1) { // probably a connection failure; try again if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")"); @@ -495,7 +490,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { return result; } } - + consecutiveSshFailures++; if (consecutiveSshFailures > maxConsecutiveSshFailures) { LOG.warn("Aborting on "+consecutiveSshFailures+" consecutive ssh connection errors (return -1) when polling for async script to complete on "+SshjTool.this.toString()+" ("+getSummary()+")"); @@ -505,14 +500,14 @@ public class SshjTool extends SshAbstractTool implements SshTool { return null; } } - + Integer retrieveStatusCommand() throws IOException { // want to double-check whether this is the exit-code from the async process, or // some unexpected failure in our long-poll command. ByteArrayOutputStream statusOut = new ByteArrayOutputStream(); ByteArrayOutputStream statusErr = new ByteArrayOutputStream(); int statusResult = asInt(acquire(new ShellAction(buildRetrieveStatusCommand(), statusOut, statusErr, execTimeout)), -1); - + if (statusResult == 0) { // The status we retrieved really is valid; return it. // TODO How to ensure no additional output in stdout/stderr when parsing below? @@ -531,7 +526,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { // probably a connection failure; try again with long-poll if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status directly received exit status -1; will retry on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return null; - + } else { if (out != null) { out.write(toUTF8ByteArray("retrieving status failed with exit code "+statusResult+" (stdout follow)")); @@ -541,28 +536,28 @@ public class SshjTool extends SshAbstractTool implements SshTool { err.write(toUTF8ByteArray("retrieving status failed with exit code "+statusResult+" (stderr follow)")); err.write(statusErr.toByteArray()); } - + if (LOG.isDebugEnabled()) LOG.debug("Long-poll retrieving status failed; returning "+statusResult+" on "+SshjTool.this.toString()+" (for "+getSummary()+")"); return statusResult; } } }.run(); } - + public int execShellDirect(Map<String,?> props, List<String> commands, Map<String,?> env) { OutputStream out = getOptionalVal(props, PROP_OUT_STREAM); OutputStream err = getOptionalVal(props, PROP_ERR_STREAM); Duration execTimeout = getOptionalVal(props, PROP_EXEC_TIMEOUT); - + List<String> cmdSequence = toCommandSequence(commands, env); List<String> allcmds = ImmutableList.<String>builder() .add(getOptionalVal(props, PROP_DIRECT_HEADER)) .addAll(cmdSequence) .add("exit $?") .build(); - + if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {}: {}", host, allcmds); - + Integer result = acquire(new ShellAction(allcmds, out, err, execTimeout)); if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} completed: return status {}", host, result); return asInt(result, -1); @@ -573,7 +568,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { if (Boolean.FALSE.equals(props.get("blocks"))) { throw new IllegalArgumentException("Cannot exec non-blocking: command="+commands); } - + // If async is set, then do it as execScript Boolean execAsync = getOptionalVal(props, PROP_EXEC_ASYNC); if (Boolean.TRUE.equals(execAsync) && BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC)) { @@ -591,14 +586,14 @@ public class SshjTool extends SshAbstractTool implements SshTool { if (Boolean.TRUE.equals(getOptionalVal(props, PROP_RUN_AS_ROOT))) { LOG.warn("Cannot run as root when executing as command; run as a script instead (will run as normal user): "+singlecmd); } - + if (LOG.isTraceEnabled()) LOG.trace("Running command at {}: {}", host, singlecmd); - + Command result = acquire(new ExecAction(singlecmd, out, err, execTimeout)); if (LOG.isTraceEnabled()) LOG.trace("Running command at {} completed: exit code {}", host, result.getExitStatus()); // can be null if no exit status is received (observed on kill `ps aux | grep thing-to-grep-for | awk {print $2}` if (result.getExitStatus()==null) LOG.warn("Null exit status running at {}: {}", host, singlecmd); - + return asInt(result.getExitStatus(), -1); } @@ -615,16 +610,16 @@ public class SshjTool extends SshAbstractTool implements SshTool { protected <T, C extends SshAction<T>> T acquire(C action) { return acquire(action, sshTries, sshTriesTimeout == 0 ? Duration.PRACTICALLY_FOREVER : Duration.millis(sshTriesTimeout)); } - + protected <T, C extends SshAction<T>> T acquire(C action, int sshTries, Duration sshTriesTimeout) { Stopwatch stopwatch = Stopwatch.createStarted(); - + for (int i = 0; i < sshTries; i++) { try { action.clear(); if (LOG.isTraceEnabled()) LOG.trace(">> ({}) acquiring {}", toString(), action); Stopwatch perfStopwatch = Stopwatch.createStarted(); - + T returnVal; try { returnVal = action.create(); @@ -633,25 +628,25 @@ public class SshjTool extends SshAbstractTool implements SshTool { * TODO In net.schmizz.sshj.SSHClient.auth(SSHClient.java:204) throws AssertionError * if not connected. This can happen if another thread has called disconnect * concurrently. This is changed in sshj v0.9.0 to instead throw an IllegalStateException. - * + * * For now, we'll retry. See "TODO" at top of class about synchronization. */ throw new IllegalStateException("Problem in "+toString()+" for "+action, e); } - + if (LOG.isTraceEnabled()) LOG.trace("<< ({}) acquired {}", toString(), returnVal); if (LOG.isTraceEnabled()) LOG.trace("SSH Performance: {} {} took {}", new Object[] { - sshClientConnection.getHostAndPort(), - action.getClass().getSimpleName() != null ? action.getClass().getSimpleName() : action, + sshClientConnection.getHostAndPort(), + action.getClass().getSimpleName() != null ? action.getClass().getSimpleName() : action, Time.makeTimeStringRounded(perfStopwatch)}); return returnVal; } catch (Exception e) { - // uninformative net.schmizz.sshj.connection.ConnectionException: + // uninformative net.schmizz.sshj.connection.ConnectionException: // Request failed (reason=UNKNOWN) may mean remote Subsytem is disabled (e.g. for FTP) // if key is missing, get a UserAuth error String errorMessage = String.format("(%s) error acquiring %s", toString(), action); - String fullMessage = String.format("%s (attempt %s/%s, in time %s/%s)", - errorMessage, (i+1), sshTries, Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), + String fullMessage = String.format("%s (attempt %s/%s, in time %s/%s)", + errorMessage, (i+1), sshTries, Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), (sshTriesTimeout.equals(Duration.PRACTICALLY_FOREVER) ? "unlimited" : Time.makeTimeStringRounded(sshTriesTimeout))); try { disconnect(); @@ -700,7 +695,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { } }; - private class GetFileAction implements SshAction<InputStream> { + private class GetFileAction implements SshAction<LocalDestFile> { private final String path; private SFTPClient sftp; @@ -715,10 +710,11 @@ public class SshjTool extends SshAbstractTool implements SshTool { } @Override - public InputStream create() throws Exception { + public LocalDestFile create() throws Exception { sftp = acquire(sftpConnection); - return new CloseFtpChannelOnCloseInputStream( - sftp.getSFTPEngine().open(path).getInputStream(), sftp); + LocalDestFile localDestFile = new FileSystemFile(path); + sftp.get(path, localDestFile); + return localDestFile; } @Override @@ -729,7 +725,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { private class PutFileAction implements SshAction<Void> { // TODO support backup as a property? - + private SFTPClient sftp; private final String path; private final int permissionsMask; @@ -738,7 +734,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { private final int uid; private final Supplier<InputStream> contentsSupplier; private final Integer length; - + PutFileAction(Map<String,?> props, String path, Supplier<InputStream> contentsSupplier, long length) { String permissions = getOptionalVal(props, PROP_PERMISSIONS); long lastModificationDateVal = getOptionalVal(props, PROP_LAST_MODIFICATION_DATE); @@ -817,7 +813,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { } }; } - + protected SshAction<Session> newSessionAction() { return new SshAction<Session>() { @@ -853,18 +849,18 @@ public class SshjTool extends SshAbstractTool implements SshTool { private final OutputStream out; private final OutputStream err; private final Duration timeout; - + private Session session; private Shell shell; private StreamGobbler outgobbler; private StreamGobbler errgobbler; - + ExecAction(String command, OutputStream out, OutputStream err, Duration timeout) { this.command = checkNotNull(command, "command"); this.out = out; this.err = err; - Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) - ? Duration.PRACTICALLY_FOREVER + Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) + ? Duration.PRACTICALLY_FOREVER : Duration.millis(sshClientConnection.getSessionTimeout()); this.timeout = (timeout == null) ? sessionTimeout : Duration.min(timeout, sessionTimeout); } @@ -883,9 +879,9 @@ public class SshjTool extends SshAbstractTool implements SshTool { public Command create() throws Exception { try { session = acquire(newSessionAction()); - + Command output = session.exec(checkNotNull(command, "command")); - + if (out != null) { outgobbler = new StreamGobbler(output.getInputStream(), out, (Logger)null); outgobbler.start(); @@ -897,7 +893,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { try { output.join((int)Math.min(timeout.toMilliseconds(), Integer.MAX_VALUE), TimeUnit.MILLISECONDS); return output; - + } finally { // wait for all stdout/stderr to have been re-directed try { @@ -910,7 +906,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { Thread.currentThread().interrupt(); } } - + } finally { clear(); } @@ -929,7 +925,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { final OutputStream out; @VisibleForTesting final OutputStream err; - + private Session session; private Shell shell; private StreamGobbler outgobbler; @@ -940,8 +936,8 @@ public class SshjTool extends SshAbstractTool implements SshTool { this.commands = checkNotNull(commands, "commands"); this.out = out; this.err = err; - Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) - ? Duration.PRACTICALLY_FOREVER + Duration sessionTimeout = (sshClientConnection.getSessionTimeout() == 0) + ? Duration.PRACTICALLY_FOREVER : Duration.millis(sshClientConnection.getSessionTimeout()); this.timeout = (timeout == null) ? sessionTimeout : Duration.min(timeout, sessionTimeout); } @@ -960,9 +956,9 @@ public class SshjTool extends SshAbstractTool implements SshTool { 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); @@ -973,7 +969,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { errgobbler = new StreamGobbler(errstream, err, (Logger)null); errgobbler.start(); } - + OutputStream output = shell.getOutputStream(); for (CharSequence cmd : commands) { @@ -990,12 +986,8 @@ public class SshjTool extends SshAbstractTool implements SshTool { } } } - // 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); @@ -1006,8 +998,8 @@ public class SshjTool extends SshAbstractTool implements SshTool { // 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); + // 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) { @@ -1016,7 +1008,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { 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 + // 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; @@ -1051,7 +1043,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { Thread.currentThread().interrupt(); } } - + } finally { clear(); } @@ -1066,7 +1058,7 @@ public class SshjTool extends SshAbstractTool implements SshTool { private byte[] toUTF8ByteArray(String string) { return org.bouncycastle.util.Strings.toUTF8ByteArray(string); } - + private Supplier<InputStream> newInputStreamSupplier(final byte[] contents) { return new Supplier<InputStream>() { @Override public InputStream get() { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c3de6287/parent/pom.xml ---------------------------------------------------------------------- diff --git a/parent/pom.xml b/parent/pom.xml index fea6e19..3cf38b2 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -430,7 +430,7 @@ <version>${commons-compress.version}</version> </dependency> <dependency> - <groupId>net.schmizz</groupId> + <groupId>com.hierynomus</groupId> <artifactId>sshj</artifactId> <version>${sshj.version}</version> </dependency> http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c3de6287/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 9ca5234..7e7e0d1 100644 --- a/pom.xml +++ b/pom.xml @@ -120,7 +120,7 @@ <ivy.version>2.2.0</ivy.version> <mx4j.version>3.0.1</mx4j.version> <bouncycastle.version>1.49</bouncycastle.version> - <sshj.version>0.8.1</sshj.version> + <sshj.version>0.12.0</sshj.version> <felix.framework.version>5.4.0</felix.framework.version> <reflections.version>0.9.9-RC1</reflections.version> <jetty.version>9.2.13.v20150730</jetty.version>