You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2016/12/13 15:58:57 UTC

[03/13] brooklyn-server git commit: bump sshj version

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 <an...@gmail.com>
Authored: Fri Sep 16 17:50:03 2016 +0200
Committer: Andrea Turli <an...@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>