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 2015/01/15 11:11:59 UTC

[1/7] incubator-brooklyn git commit: Avoid NPE in PortForwardManager.rebindSupport

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 6455a9da2 -> e4ecb3871


Avoid NPE in PortForwardManager.rebindSupport

- caused by publicIpIdToHostname containing a null key or null value
  (not sure what; it’s deprecated so not worrying about it!).
- Instead of ImmutableMap for copying it, use MutableMap.copyOf
  so will not fail on null.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/c61a3093
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/c61a3093
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/c61a3093

Branch: refs/heads/master
Commit: c61a309302240c6108243ef25341802a12031d56
Parents: d89062b
Author: Aled Sage <al...@gmail.com>
Authored: Fri Jan 9 12:12:12 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Jan 14 22:38:08 2015 +0000

----------------------------------------------------------------------
 .../java/brooklyn/location/access/PortForwardManagerImpl.java    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c61a3093/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java b/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
index 7d30a69..c9a596b 100644
--- a/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
+++ b/core/src/main/java/brooklyn/location/access/PortForwardManagerImpl.java
@@ -117,8 +117,8 @@ public class PortForwardManagerImpl extends AbstractLocation implements PortForw
                 Map<String, PortMapping> mappingsCopy;
                 Map<String,String> publicIpIdToHostnameCopy;
                 synchronized (mutex) {
-                    mappingsCopy = ImmutableMap.copyOf(mappings);
-                    publicIpIdToHostnameCopy = ImmutableMap.copyOf(publicIpIdToHostname);
+                    mappingsCopy = MutableMap.copyOf(mappings);
+                    publicIpIdToHostnameCopy = MutableMap.copyOf(publicIpIdToHostname);
                 }
                 return getMementoWithProperties(MutableMap.<String,Object>of(
                         "mappings", mappingsCopy, 


[2/7] incubator-brooklyn git commit: BROOKLYN-106: Fix sshj’s async-exec

Posted by al...@apache.org.
BROOKLYN-106: Fix sshj’s async-exec

- on repeated timeouts of longPoll, then keep
  going.
- On long-poll, reduce the sshTriesTimeout.
- On timeout/error, don’t wait forever for
  stdout/stderr streams to close.
- Potential improvement to disconnect/reconnect.
- Adds Duration.isShorterThan(Stopwatch)


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/c06a2297
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/c06a2297
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/c06a2297

Branch: refs/heads/master
Commit: c06a2297eadfd5b6322bde303a65cbc0e477c7d1
Parents: 0673146
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jan 8 21:12:58 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Jan 14 22:38:08 2015 +0000

----------------------------------------------------------------------
 .../util/internal/ssh/ShellAbstractTool.java    |  34 +++--
 .../internal/ssh/sshj/SshjClientConnection.java |   2 +-
 .../util/internal/ssh/sshj/SshjTool.java        | 150 ++++++++++++++++---
 .../sshj/SshjToolAsyncStubIntegrationTest.java  |  14 +-
 .../ssh/sshj/SshjToolIntegrationTest.java       |  80 ++++++----
 .../main/java/brooklyn/util/time/Duration.java  |   9 +-
 6 files changed, 219 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c06a2297/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java b/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java
index 82624d7..e677399 100644
--- a/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java
+++ b/core/src/main/java/brooklyn/util/internal/ssh/ShellAbstractTool.java
@@ -247,7 +247,7 @@ public abstract class ShellAbstractTool implements ShellTool {
             }
             this.scriptNameWithoutExtension = "brooklyn-"+
                     Time.makeDateStampString()+"-"+Identifiers.makeRandomId(4)+
-                    (summary==null ? "" : "-"+summary);
+                    (Strings.isBlank(summary) ? "" : "-"+summary);
             this.scriptPath = Os.mergePathsUnix(scriptDir, scriptNameWithoutExtension+".sh");
         }
 
@@ -367,14 +367,13 @@ public abstract class ShellAbstractTool implements ShellTool {
          * An offset can be given, to only retrieve data starting at a particular character (indexed from 0).
          */
         protected List<String> buildLongPollCommand(int stdoutPosition, int stderrPosition, Duration timeout) {
-            // Note that `tail -c +1` means start at the *first* character (i.e. start counting from 1, not 0)
-            // TODO Relies on commands terminating when ssh connection dropped (because not run with `nohup`)
-            String catStdoutCmd = "tail -c +"+(stdoutPosition+1)+" -f "+stdoutPath+" &";
-            String catStderrCmd = "tail -c +"+(stderrPosition+1)+" -f "+stderrPath+" 1>&2 &";
             long maxTime = Math.max(1, timeout.toSeconds());
             
+            // Note that `tail -c +1` means start at the *first* character (i.e. start counting from 1, not 0)
             List<String> waitForExitStatusParts = ImmutableList.of(
                     "# Long poll", // comment is to aid testing - see SshjToolAsyncStubIntegrationTest
+                    "tail -c +"+(stdoutPosition+1)+" -f "+stdoutPath+" & export TAIL_STDOUT_PID=$!",
+                    "tail -c +"+(stderrPosition+1)+" -f "+stderrPath+" 1>&2 & export TAIL_STDERR_PID=$!",
                     "EXIT_STATUS_PATH="+exitStatusPath,
                     "PID_PATH="+pidPath,
                     "MAX_TIME="+maxTime,
@@ -390,9 +389,11 @@ public abstract class ShellAbstractTool implements ShellTool {
                     "            sleep 3",
                     "            if test -s $EXIT_STATUS_PATH; then",
                     "                EXIT_STATUS=`cat $EXIT_STATUS_PATH`",
+                    "                kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID}",
                     "                exit $EXIT_STATUS",
                     "            else",
                     "                echo \"No exit status in $EXIT_STATUS_PATH, and pid in $PID_PATH ($PID) not executing\"",
+                    "                kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID}",
                     "                exit 126",
                     "            fi",
                     "        fi",
@@ -401,25 +402,32 @@ public abstract class ShellAbstractTool implements ShellTool {
                     "    sleep 1",
                     "    COUNTER+=1",
                     "done",
+                    "kill ${TAIL_STDERR_PID} ${TAIL_STDOUT_PID}",
                     "exit 125"+"\n");
             String waitForExitStatus = Joiner.on("\n").join(waitForExitStatusParts);
 
-            MutableList.Builder<String> cmds = MutableList.<String>builder()
-                    .add(runAsRoot ? BashCommands.sudo(catStdoutCmd) : catStdoutCmd)
-                    .add(runAsRoot ? BashCommands.sudo(catStderrCmd) : catStderrCmd)
-                    .add(runAsRoot ? BashCommands.sudo(waitForExitStatus) : waitForExitStatus);
-            return cmds.build();
+            return ImmutableList.of(runAsRoot ? BashCommands.sudo(waitForExitStatus) : waitForExitStatus);
         }
 
         protected List<String> deleteTemporaryFilesCommand() {
+            ImmutableList.Builder<String> cmdParts = ImmutableList.builder();
+            
             if (!Boolean.TRUE.equals(noDeleteAfterExec)) {
                 // use "-f" because some systems have "rm" aliased to "rm -i"
                 // use "< /dev/null" to guarantee doesn't hang
-                return ImmutableList.of(
+                cmdParts.add(
                         "rm -f "+scriptPath+" "+stdoutPath+" "+stderrPath+" "+exitStatusPath+" "+pidPath+" < /dev/null");
-            } else {
-                return ImmutableList.<String>of();
             }
+            
+            // If the buildLongPollCommand didn't complete properly then it might have left tail command running;
+            // ensure they are killed.
+            cmdParts.add(
+                    "ps aux | grep \"tail -c\" | grep \""+stdoutPath+"\" | grep -v grep | awk '{ printf $2 }' | xargs kill",
+                    "ps aux | grep \"tail -c\" | grep \""+stderrPath+"\" | grep -v grep | awk '{ printf $2 }' | xargs kill");
+
+            String cmd = Joiner.on("\n").join(cmdParts.build());
+            
+            return ImmutableList.of(runAsRoot ? BashCommands.sudo(cmd) : cmd);
         }
 
         public abstract int run();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c06a2297/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java b/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java
index 52347d2..982022a 100644
--- a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java
+++ b/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjClientConnection.java
@@ -169,8 +169,8 @@ public class SshjClientConnection implements SshAction<SSHClient> {
             } catch (IOException e) {
                 if (LOG.isDebugEnabled()) LOG.debug("<< exception disconnecting from {}: {}", e, e.getMessage());
             }
-            ssh = null;
         }
+        ssh = null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c06a2297/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java b/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
index e7ecd0c..8fa38ad 100644
--- a/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
+++ b/core/src/main/java/brooklyn/util/internal/ssh/sshj/SshjTool.java
@@ -55,7 +55,9 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.RuntimeTimeoutException;
 import brooklyn.util.internal.ssh.BackoffLimitedRetryHandler;
+import brooklyn.util.internal.ssh.ShellTool;
 import brooklyn.util.internal.ssh.SshAbstractTool;
 import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.io.FileUtil;
@@ -333,6 +335,24 @@ 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
+     */
     protected int execScriptAsyncAndPoll(final Map<String,?> props, final List<String> commands, final Map<String,?> env) {
         return new ToolAbstractAsyncExecScript(props) {
             private int maxConsecutiveSshFailures = 3;
@@ -346,9 +366,37 @@ public class SshjTool extends SshAbstractTool implements SshTool {
             
             public int run() {
                 timer = Stopwatch.createStarted();
-                String scriptContents = toScript(props, commands, env);
+                final String scriptContents = toScript(props, commands, env);
                 if (LOG.isTraceEnabled()) LOG.trace("Running shell command at {} as async script: {}", host, scriptContents);
-                copyToServer(ImmutableMap.of("permissions", "0700"), scriptContents.getBytes(), scriptPath);
+                
+                // 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)
+                        .limitIterationsTo(3)
+                        .rethrowException()
+                        .until(new Callable<Boolean>() {
+                            @Override
+                            public Boolean call() throws Exception {
+                                iteration++;
+                                if (LOG.isDebugEnabled()) {
+                                    String msg = "Uploading (iteration="+iteration+") for async script on "+SshjTool.this.toString()+" (for "+getSummary()+")";
+                                    if (iteration == 1) {
+                                        LOG.trace(msg);
+                                    } else {
+                                        LOG.debug(msg);
+                                    }
+                                }
+                                copyToServer(ImmutableMap.of("permissions", "0700"), scriptContents.getBytes(), scriptPath);
+                                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);
@@ -357,7 +405,7 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                 // Long polling to get the status
                 try {
                     final AtomicReference<Integer> result = new AtomicReference<Integer>();
-                    boolean success = Repeater.create("async script on "+SshjTool.this.toString()+" (for "+getSummary()+")")
+                    boolean success = Repeater.create("async script long-poll on "+SshjTool.this.toString()+" (for "+getSummary()+")")
                             .backoffTo(maxDelayBetweenPolls)
                             .limitTimeTo(execTimeout)
                             .until(new Callable<Boolean>() {
@@ -377,13 +425,28 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                         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);
+                } finally {
+                    // Delete the temporary files created (and the `tail -c` commands that might have been left behind by long-polls).
+                    // Using pollTimeout so doesn't wait forever, but waits for a reasonable (configurable) length of time.
+                    // TODO also execute this if the `buildRunScriptCommand` fails, as that might have left files behind?
+                    try {
+                        int execDeleteResult = asInt(acquire(new ShellAction(deleteTemporaryFilesCommand(), out, err, pollTimeout)), -1);
+                        if (execDeleteResult != 0) {
+                            LOG.debug("Problem deleting temporary files of async script on "+SshjTool.this.toString()+" (for "+getSummary()+"): exit status "+execDeleteResult);
+                        }
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.debug("Problem deleting temporary files of async script on "+SshjTool.this.toString()+" (for "+getSummary()+"); continuing", e);
+                    }
                 }
             }
+            
             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.
@@ -391,11 +454,20 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                 // If we disconnect, we will pick up from that char of the stream.
                 // TODO Additional stdout/stderr written by buildLongPollCommand() could interfere, 
                 //      causing us to miss some characters.
-                // TODO Want to include timeout for long-polling
                 Duration nextPollTimeout = Duration.min(pollTimeout, Duration.millis(execTimeout.toMilliseconds()-timer.elapsed(TimeUnit.MILLISECONDS)));
                 CountingOutputStream countingOut = (out == null) ? null : new CountingOutputStream(out);
                 CountingOutputStream countingErr = (err == null) ? null : new CountingOutputStream(err);
-                int longPollResult = asInt(acquire(new ShellAction(buildLongPollCommand(stdoutCount, stderrCount, nextPollTimeout), countingOut, countingErr, nextPollTimeout)), -1);
+                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);
+                } catch (RuntimeTimeoutException e) {
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll timed out on "+SshjTool.this.toString()+" (for "+getSummary()+"): "+e);
+                    return null;
+                }
                 stdoutCount += (countingOut == null) ? 0 : countingOut.getCount();
                 stderrCount += (countingErr == null) ? 0 : countingErr.getCount();
                 
@@ -407,7 +479,14 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                     // 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()+")");
                     return null;
-                    
+
+                } else if (longPollResult == 125) {
+                    // 125 is the special code for timeout in long-poll (see buildLongPollCommand).
+                    // However, there is a tiny chance that the underlying command might have returned that exact exit code!
+                    // Don't treat a timeout as a "consecutiveSshFailure".
+                    if (LOG.isDebugEnabled()) LOG.debug("Long-poll received exit status "+longPollResult+"; most likely timeout; retrieving actual status on "+SshjTool.this.toString()+" (for "+getSummary()+")");
+                    return retrieveStatusCommand();
+
                 } else {
                     // want to double-check whether this is the exit-code from the async process, or
                     // some unexpected failure in our long-poll command.
@@ -427,6 +506,7 @@ 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.
@@ -469,6 +549,7 @@ public class SshjTool extends SshAbstractTool implements SshTool {
             }
         }.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);
@@ -493,6 +574,13 @@ 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)) {
+            return execScriptAsyncAndPoll(props, commands, env);
+        }
+
         OutputStream out = getOptionalVal(props, PROP_OUT_STREAM);
         OutputStream err = getOptionalVal(props, PROP_ERR_STREAM);
         String separator = getOptionalVal(props, PROP_SEPARATOR);
@@ -526,6 +614,10 @@ 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++) {
@@ -561,7 +653,7 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                 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)), 
-                        (sshTriesTimeout > 0 ? Time.makeTimeStringRounded(sshTriesTimeout) : "unlimited"));
+                        (sshTriesTimeout.equals(Duration.PRACTICALLY_FOREVER) ? "unlimited" : Time.makeTimeStringRounded(sshTriesTimeout)));
                 try {
                     disconnect();
                 } catch (Exception e2) {
@@ -570,9 +662,9 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                 if (i + 1 == sshTries) {
                     LOG.debug("<< {} (rethrowing, out of retries): {}", fullMessage, e.getMessage());
                     throw propagate(e, fullMessage + "; out of retries");
-                } else if (sshTriesTimeout > 0 && stopwatch.elapsed(TimeUnit.MILLISECONDS) > sshTriesTimeout) {
-                    LOG.debug("<< {} (rethrowing, out of time - max {}): {}", new Object[] { fullMessage, Time.makeTimeString(sshTriesTimeout, true), e.getMessage() });
-                    throw propagate(e, fullMessage + "; out of time");
+                } else if (sshTriesTimeout.isShorterThan(stopwatch)) {
+                    LOG.debug("<< {} (rethrowing, out of time - max {}): {}", new Object[] { fullMessage, Time.makeTimeStringRounded(sshTriesTimeout), e.getMessage() });
+                    throw new RuntimeTimeoutException(fullMessage + "; out of time", e);
                 } else {
                     if (LOG.isDebugEnabled()) LOG.debug("<< {}: {}", fullMessage, e.getMessage());
                     backoffForAttempt(i + 1, errorMessage + ": " + e.getMessage());
@@ -804,15 +896,16 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                     errgobbler.start();
                 }
                 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 {
-                        if (outgobbler != null) outgobbler.join();
-                        if (errgobbler != null) errgobbler.join();
+                        // Don't use forever (i.e. 0) because BROOKLYN-106: ssh hangs
+                        long joinTimeout = 10*1000;
+                        if (outgobbler != null) outgobbler.join(joinTimeout);
+                        if (errgobbler != null) errgobbler.join(joinTimeout);
                     } catch (InterruptedException e) {
                         LOG.warn("Interrupted gobbling streams from ssh: "+command, e);
                         Thread.currentThread().interrupt();
@@ -904,6 +997,7 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                 }
                 closeWhispering(output, this);
                 
+                boolean timedOut = false;
                 try {
                     long timeoutMillis = Math.min(timeout.toMilliseconds(), Integer.MAX_VALUE);
                     long timeoutEnd = System.currentTimeMillis() + timeoutMillis;
@@ -917,19 +1011,25 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                             (!shell.isOpen() || ((SessionChannel)session).getExitStatus()!=null);
                         try {
                             shell.join(1000, TimeUnit.MILLISECONDS);
-                        } catch (ConnectionException e) { last = e; }
-                        if (endBecauseReturned)
+                        } 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 {}", sshClientConnection.getSessionTimeout(), this);
-                        // we timed out, or other problem -- reproduce the error
-                        throw last;
+                        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 {
@@ -937,8 +1037,16 @@ public class SshjTool extends SshAbstractTool implements SshTool {
                     closeWhispering(shell, this);
                     shell = null;
                     try {
-                        if (outgobbler != null) outgobbler.join();
-                        if (errgobbler != null) errgobbler.join();
+                        // 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();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c06a2297/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
index 2acb200..caebac1 100644
--- a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
+++ b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolAsyncStubIntegrationTest.java
@@ -29,7 +29,9 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.internal.ssh.SshAbstractTool.SshAction;
 import brooklyn.util.internal.ssh.sshj.SshjTool.ShellAction;
 import brooklyn.util.time.Duration;
 
@@ -58,15 +60,17 @@ public class SshjToolAsyncStubIntegrationTest {
     private SshjTool tool;
     private List<InjectedResult> sequence;
     int counter = 0;
+    private boolean origFeatureEnablement;
     
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
+        origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
         sequence = Lists.newArrayList();
         counter = 0;
         
         tool = new SshjTool(ImmutableMap.<String,Object>of("host", "localhost")) {
             @SuppressWarnings("unchecked")
-            protected <T, C extends SshAction<T>> T acquire(C action) {
+            protected <T, C extends SshAction<T>> T acquire(C action, int sshTries, Duration sshTriesTimeout) {
                 if (action instanceof SshjTool.ShellAction) {
                     SshjTool.ShellAction shellAction = (SshjTool.ShellAction) action;
                     InjectedResult injectedResult = sequence.get(counter);
@@ -74,14 +78,18 @@ public class SshjToolAsyncStubIntegrationTest {
                     counter++;
                     return (T) injectedResult.result.apply(shellAction);
                 }
-                return super.acquire(action);
+                return super.acquire(action, sshTries, sshTriesTimeout);
             }
         };
     }
 
     @AfterMethod(alwaysRun=true)
     public void tearDown() throws Exception {
-        if (tool != null) tool.disconnect();
+        try {
+            if (tool != null) tool.disconnect();
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
     }
     
     private Predicate<SshjTool.ShellAction> containsCmd(final String cmd) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c06a2297/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
index b81712d..c809ad1 100644
--- a/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
+++ b/core/src/test/java/brooklyn/util/internal/ssh/sshj/SshjToolIntegrationTest.java
@@ -37,8 +37,10 @@ import net.schmizz.sshj.connection.channel.direct.Session;
 
 import org.testng.annotations.Test;
 
+import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.test.Asserts;
 import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.exceptions.RuntimeTimeoutException;
 import brooklyn.util.internal.ssh.SshException;
 import brooklyn.util.internal.ssh.SshTool;
 import brooklyn.util.internal.ssh.SshToolAbstractIntegrationTest;
@@ -129,7 +131,7 @@ public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest {
         try {
             localtool.execScript(ImmutableMap.<String,Object>of(), ImmutableList.of("true"));
             fail();
-        } catch (SshException e) {
+        } catch (RuntimeTimeoutException e) {
             if (!e.toString().contains("out of time")) throw e;
             assertEquals(callCount.get(), 2);
         }
@@ -165,45 +167,56 @@ public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest {
 
     @Test(groups = {"Integration"})
     public void testAsyncExecStdoutAndStderr() throws Exception {
-        // Include a sleep, to ensure that the contents retrieved in first poll and subsequent polls are appended
-        List<String> cmds = ImmutableList.of(
-                "echo mystringToStdout",
-                "echo mystringToStderr 1>&2",
-                "sleep 3",
-                "echo mystringPostSleepToStdout",
-                "echo mystringPostSleepToStderr 1>&2");
-        
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        ByteArrayOutputStream err = new ByteArrayOutputStream();
-        int exitCode = tool.execScript(
-                ImmutableMap.of(
-                        "out", out, 
-                        "err", err, 
-                        SshjTool.PROP_EXEC_ASYNC.getName(), true, 
-                        SshjTool.PROP_NO_EXTRA_OUTPUT.getName(), true,
-                        SshjTool.PROP_EXEC_ASYNC_POLLING_TIMEOUT.getName(), Duration.ONE_SECOND), 
-                cmds, 
-                ImmutableMap.<String,String>of());
-        String outStr = new String(out.toByteArray());
-        String errStr = new String(err.toByteArray());
-
-        assertEquals(exitCode, 0);
-        assertEquals(outStr.trim(), "mystringToStdout\nmystringPostSleepToStdout");
-        assertEquals(errStr.trim(), "mystringToStderr\nmystringPostSleepToStderr");
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
+        try {
+            // Include a sleep, to ensure that the contents retrieved in first poll and subsequent polls are appended
+            List<String> cmds = ImmutableList.of(
+                    "echo mystringToStdout",
+                    "echo mystringToStderr 1>&2",
+                    "sleep 5",
+                    "echo mystringPostSleepToStdout",
+                    "echo mystringPostSleepToStderr 1>&2");
+            
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            ByteArrayOutputStream err = new ByteArrayOutputStream();
+            int exitCode = tool.execScript(
+                    ImmutableMap.of(
+                            "out", out, 
+                            "err", err, 
+                            SshjTool.PROP_EXEC_ASYNC.getName(), true, 
+                            SshjTool.PROP_NO_EXTRA_OUTPUT.getName(), true,
+                            SshjTool.PROP_EXEC_ASYNC_POLLING_TIMEOUT.getName(), Duration.ONE_SECOND), 
+                    cmds, 
+                    ImmutableMap.<String,String>of());
+            String outStr = new String(out.toByteArray());
+            String errStr = new String(err.toByteArray());
+    
+            assertEquals(exitCode, 0);
+            assertEquals(outStr.trim(), "mystringToStdout\nmystringPostSleepToStdout");
+            assertEquals(errStr.trim(), "mystringToStderr\nmystringPostSleepToStderr");
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
     }
 
     @Test(groups = {"Integration"})
     public void testAsyncExecReturnsExitCode() throws Exception {
-        int exitCode = tool.execScript(
-                ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true), 
-                ImmutableList.of("exit 123"), 
-                ImmutableMap.<String,String>of());
-        assertEquals(exitCode, 123);
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
+        try {
+            int exitCode = tool.execScript(
+                    ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true), 
+                    ImmutableList.of("exit 123"), 
+                    ImmutableMap.<String,String>of());
+            assertEquals(exitCode, 123);
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
+        }
     }
 
     @Test(groups = {"Integration"})
     public void testAsyncExecTimesOut() throws Exception {
         Stopwatch stopwatch = Stopwatch.createStarted();
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
         try {
             tool.execScript(
                 ImmutableMap.of(SshjTool.PROP_EXEC_ASYNC.getName(), true, SshjTool.PROP_EXEC_TIMEOUT.getName(), Duration.millis(1)), 
@@ -213,6 +226,8 @@ public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest {
         } catch (Exception e) {
             TimeoutException te = Exceptions.getFirstThrowableOfType(e, TimeoutException.class);
             if (te == null) throw e;
+        } finally {
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
         }
         
         long seconds = stopwatch.elapsed(TimeUnit.SECONDS);
@@ -234,6 +249,8 @@ public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest {
                 long seconds = stopwatch.elapsed(TimeUnit.SECONDS);
                 assertTrue(seconds < 30, "exec took "+seconds+" seconds");
             }});
+        
+        boolean origFeatureEnablement = BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC);
         try {
             thread.start();
             
@@ -249,6 +266,7 @@ public class SshjToolIntegrationTest extends SshToolAbstractIntegrationTest {
             assertFalse(thread.isAlive());
         } finally {
             thread.interrupt();
+            BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_SSH_ASYNC_EXEC, origFeatureEnablement);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c06a2297/utils/common/src/main/java/brooklyn/util/time/Duration.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/time/Duration.java b/utils/common/src/main/java/brooklyn/util/time/Duration.java
index 3950bc7..0f73ef8 100644
--- a/utils/common/src/main/java/brooklyn/util/time/Duration.java
+++ b/utils/common/src/main/java/brooklyn/util/time/Duration.java
@@ -277,10 +277,18 @@ public class Duration implements Comparable<Duration>, Serializable {
         return compareTo(x) > 0;
     }
 
+    public boolean isLongerThan(Stopwatch stopwatch) {
+        return isLongerThan(Duration.millis(stopwatch.elapsed(TimeUnit.MILLISECONDS)));
+    }
+
     public boolean isShorterThan(Duration x) {
         return compareTo(x) < 0;
     }
 
+    public boolean isShorterThan(Stopwatch stopwatch) {
+        return isShorterThan(Duration.millis(stopwatch.elapsed(TimeUnit.MILLISECONDS)));
+    }
+
     /** returns the larger of this value or the argument */
     public Duration lowerBound(Duration alternateMinimumValue) {
         if (isShorterThan(alternateMinimumValue)) return alternateMinimumValue;
@@ -303,5 +311,4 @@ public class Duration implements Comparable<Duration>, Serializable {
     public Duration maximum(Duration alternateMaximumValue) {
         return upperBound(alternateMaximumValue);
     }
-    
 }


[6/7] incubator-brooklyn git commit: BROOKLYN-113: curl retries and times out sooner

Posted by al...@apache.org.
BROOKLYN-113: curl retries and times out sooner


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/6fe7994e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/6fe7994e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/6fe7994e

Branch: refs/heads/master
Commit: 6fe7994ef585f04fa8bcdf143cfb2f174e84cb9b
Parents: c61a309
Author: Aled Sage <al...@gmail.com>
Authored: Tue Jan 13 13:54:00 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jan 15 10:10:09 2015 +0000

----------------------------------------------------------------------
 utils/common/src/main/java/brooklyn/util/ssh/BashCommands.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6fe7994e/utils/common/src/main/java/brooklyn/util/ssh/BashCommands.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/ssh/BashCommands.java b/utils/common/src/main/java/brooklyn/util/ssh/BashCommands.java
index 457a60a..e60b332 100644
--- a/utils/common/src/main/java/brooklyn/util/ssh/BashCommands.java
+++ b/utils/common/src/main/java/brooklyn/util/ssh/BashCommands.java
@@ -505,7 +505,7 @@ public class BashCommands {
         
         List<String> commands = new ArrayList<String>();
         for (String url : urls) {
-            String command = "curl -f -L -k ";
+            String command = "curl -f -L -k --retry 10 --keepalive-time 30 --speed-time 30 ";
             if (user!=null && password!=null) {
                command = command + format("-u %s:%s ", user, password);
             }


[7/7] incubator-brooklyn git commit: This closes #448

Posted by al...@apache.org.
This closes #448


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/e4ecb387
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/e4ecb387
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/e4ecb387

Branch: refs/heads/master
Commit: e4ecb38710abe55308dac90895c9ace0f854f073
Parents: 6455a9d 6fe7994
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jan 15 10:11:17 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jan 15 10:11:17 2015 +0000

----------------------------------------------------------------------
 .../internal/BrooklynFeatureEnablement.java     |  47 +++++-
 .../location/access/PortForwardManagerImpl.java |   4 +-
 .../location/basic/SshMachineLocation.java      |   5 +-
 .../internal/LocalManagementContext.java        |  10 +-
 .../util/internal/ssh/ShellAbstractTool.java    |  34 +++--
 .../internal/ssh/sshj/SshjClientConnection.java |   2 +-
 .../util/internal/ssh/sshj/SshjTool.java        | 150 ++++++++++++++++---
 .../internal/BrooklynFeatureEnablementTest.java |  27 ++++
 .../sshj/SshjToolAsyncStubIntegrationTest.java  |  14 +-
 .../ssh/sshj/SshjToolIntegrationTest.java       |  80 ++++++----
 .../java/brooklyn/util/ssh/BashCommands.java    |   2 +-
 .../main/java/brooklyn/util/time/Duration.java  |   9 +-
 .../brooklyn/util/stream/StreamGobblerTest.java |  88 +++++++++++
 13 files changed, 386 insertions(+), 86 deletions(-)
----------------------------------------------------------------------



[4/7] incubator-brooklyn git commit: Adds StreamGobblerTest

Posted by al...@apache.org.
Adds StreamGobblerTest


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/d89062b8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/d89062b8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/d89062b8

Branch: refs/heads/master
Commit: d89062b86f99d2cfae0d1558c4697b23ab7d3c7c
Parents: ce16828
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jan 8 22:19:47 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Jan 14 22:38:08 2015 +0000

----------------------------------------------------------------------
 .../brooklyn/util/stream/StreamGobblerTest.java | 88 ++++++++++++++++++++
 1 file changed, 88 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d89062b8/utils/common/src/test/java/brooklyn/util/stream/StreamGobblerTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/brooklyn/util/stream/StreamGobblerTest.java b/utils/common/src/test/java/brooklyn/util/stream/StreamGobblerTest.java
new file mode 100644
index 0000000..4722829
--- /dev/null
+++ b/utils/common/src/test/java/brooklyn/util/stream/StreamGobblerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.util.stream;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import org.testng.annotations.Test;
+
+import brooklyn.test.Asserts;
+
+public class StreamGobblerTest {
+
+    @Test
+    public void testGobbleStream() throws Exception {
+        byte[] bytes = new byte[] {'a','b','c'};
+        InputStream stream = new ByteArrayInputStream(bytes);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        StreamGobbler gobbler = new StreamGobbler(stream, out, null);
+        gobbler.start();
+        try {
+            gobbler.join(10*1000);
+            assertFalse(gobbler.isAlive());
+            assertEquals(new String(out.toByteArray()), "abc\n");
+        } finally {
+            gobbler.close();
+            gobbler.interrupt();
+        }
+    }
+    
+    @Test
+    public void testGobbleMultiLineBlockingStream() throws Exception {
+        PipedOutputStream pipedOutputStream = new PipedOutputStream();
+        PipedInputStream stream = new PipedInputStream(pipedOutputStream);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        StreamGobbler gobbler = new StreamGobbler(stream, out, null);
+        gobbler.start();
+        try {
+            pipedOutputStream.write("line1\n".getBytes());
+            assertEqualsEventually(out, "line1\n");
+
+            pipedOutputStream.write("line2\n".getBytes());
+            assertEqualsEventually(out, "line1\nline2\n");
+
+            pipedOutputStream.write("line".getBytes());
+            pipedOutputStream.write("3\n".getBytes());
+            assertEqualsEventually(out, "line1\nline2\nline3\n");
+
+            pipedOutputStream.close();
+            
+            gobbler.join(10*1000);
+            assertFalse(gobbler.isAlive());
+            assertEquals(new String(out.toByteArray()), "line1\nline2\nline3\n");
+        } finally {
+            gobbler.close();
+            gobbler.interrupt();
+        }
+    }
+    
+    private void assertEqualsEventually(final ByteArrayOutputStream out, final String expected) {
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                assertEquals(new String(out.toByteArray()), expected);
+            }});
+    }
+}


[5/7] incubator-brooklyn git commit: Feature-enablement: init from brooklyn properties

Posted by al...@apache.org.
Feature-enablement: init from brooklyn properties


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/ce168285
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/ce168285
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/ce168285

Branch: refs/heads/master
Commit: ce168285ff8c71f460701e5d818c58c343dae64a
Parents: c06a229
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jan 8 21:19:31 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Jan 14 22:38:08 2015 +0000

----------------------------------------------------------------------
 .../internal/BrooklynFeatureEnablement.java     | 47 ++++++++++++++++----
 .../internal/LocalManagementContext.java        | 10 ++++-
 .../internal/BrooklynFeatureEnablementTest.java | 27 +++++++++++
 3 files changed, 74 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ce168285/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java b/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
index 9946ac7..7ecd7cd 100644
--- a/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
+++ b/core/src/main/java/brooklyn/internal/BrooklynFeatureEnablement.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.config.BrooklynProperties;
 import brooklyn.internal.storage.BrooklynStorage;
 import brooklyn.management.ha.HighAvailabilityMode;
 import brooklyn.util.internal.ssh.ShellTool;
@@ -43,27 +44,29 @@ public class BrooklynFeatureEnablement {
 
     private static final Logger LOG = LoggerFactory.getLogger(BrooklynFeatureEnablement.class);
 
-    public static final String FEATURE_POLICY_PERSISTENCE_PROPERTY = "brooklyn.experimental.feature.policyPersistence";
+    public static final String FEATURE_PROPERTY_PREFIX = "brooklyn.experimental.feature";
     
-    public static final String FEATURE_ENRICHER_PERSISTENCE_PROPERTY = "brooklyn.experimental.feature.enricherPersistence";
+    public static final String FEATURE_POLICY_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".policyPersistence";
+    
+    public static final String FEATURE_ENRICHER_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".enricherPersistence";
 
-    public static final String FEATURE_FEED_PERSISTENCE_PROPERTY = "brooklyn.experimental.feature.feedPersistence";
+    public static final String FEATURE_FEED_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedPersistence";
     
     /** whether feeds are automatically registered when set on entities, so that they are persisted */
-    public static final String FEATURE_FEED_REGISTRATION_PROPERTY = "brooklyn.experimental.feature.feedRegistration";
+    public static final String FEATURE_FEED_REGISTRATION_PROPERTY = FEATURE_PROPERTY_PREFIX+".feedRegistration";
 
-    public static final String FEATURE_CATALOG_PERSISTENCE_PROPERTY = "brooklyn.experimental.feature.catalogPersistence";
+    public static final String FEATURE_CATALOG_PERSISTENCE_PROPERTY = FEATURE_PROPERTY_PREFIX+".catalogPersistence";
     
     /** whether the default standby mode is {@link HighAvailabilityMode#HOT_STANDBY} or falling back to the traditional
      * {@link HighAvailabilityMode#STANDBY} */
-    public static final String FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY = "brooklyn.experimental.feature.defaultStandbyIsHot";
+    public static final String FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY = FEATURE_PROPERTY_PREFIX+".defaultStandbyIsHot";
     
     /** whether to attempt to use {@link BrooklynStorage} (datagrid) as a backing store for data;
      * note this is <b>not</b> compatible with {@link #FEATURE_DEFAULT_STANDBY_IS_HOT_PROPERTY} 
      * which uses a blob/file store and a larger-granularity rebind process than was intended with the datagrid */
     /* not sure if we still even need this? now the rebind/read-only feature reloads on demand from the persistence store;
      * the data-grid backing  */
-    public static final String FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE = "brooklyn.experimental.feature.useBrooklynLiveObjectsDatagridStorage";
+    public static final String FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE = FEATURE_PROPERTY_PREFIX+".useBrooklynLiveObjectsDatagridStorage";
 
     /**
      * Renaming threads can really helps with debugging etc; however it's a massive performance hit (2x)
@@ -91,7 +94,7 @@ public class BrooklynFeatureEnablement {
      * If this feature is disabled, then even if the {@link ShellTool#PROP_EXEC_ASYNC} is configured it
      * will still use the classic ssh approach.
      */
-    public static final String FEATURE_SSH_ASYNC_EXEC = "brooklyn.experimental.feature.ssh.asyncExec";
+    public static final String FEATURE_SSH_ASYNC_EXEC = FEATURE_PROPERTY_PREFIX+".ssh.asyncExec";
 
     private static final Map<String, Boolean> FEATURE_ENABLEMENTS = Maps.newLinkedHashMap();
 
@@ -118,6 +121,34 @@ public class BrooklynFeatureEnablement {
         setDefaults();
     }
     
+    /**
+     * Initialises the feature-enablement from brooklyn properties. For each
+     * property, prefer a system-property if present; otherwise use the value 
+     * from brooklyn properties.
+     */
+    public static void init(BrooklynProperties props) {
+        boolean changed = false;
+        for (Map.Entry<String, Object> entry : props.asMapWithStringKeys().entrySet()) {
+            String property = entry.getKey();
+            if (property.startsWith(FEATURE_PROPERTY_PREFIX)) {
+                if (!FEATURE_ENABLEMENTS.containsKey(property)) {
+                    Object rawVal = System.getProperty(property);
+                    if (rawVal == null) {
+                        rawVal = entry.getValue();
+                    }
+                    boolean val = Boolean.parseBoolean(""+rawVal);
+                    FEATURE_ENABLEMENTS.put(property, val);
+                    
+                    changed = true;
+                    LOG.debug("Init feature enablement of "+property+" set to "+val);
+                }
+            }
+        }
+        if (!changed) {
+            LOG.debug("Init feature enablement did nothing, as no settings in brooklyn properties");
+        }
+    }
+    
     public static boolean isEnabled(String property) {
         synchronized (MUTEX) {
             if (!FEATURE_ENABLEMENTS.containsKey(property)) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ce168285/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java b/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
index 257e1f6..202e4a6 100644
--- a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
@@ -44,6 +44,7 @@ import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.proxying.InternalEntityFactory;
 import brooklyn.entity.proxying.InternalLocationFactory;
 import brooklyn.entity.proxying.InternalPolicyFactory;
+import brooklyn.internal.BrooklynFeatureEnablement;
 import brooklyn.internal.storage.DataGridFactory;
 import brooklyn.location.Location;
 import brooklyn.management.AccessController;
@@ -171,16 +172,19 @@ public class LocalManagementContext extends AbstractManagementContext {
     
     public LocalManagementContext(Builder builder, Map<String, Object> brooklynAdditionalProperties, DataGridFactory datagridFactory) {
         super(builder.build(), datagridFactory);
+        
+        checkNotNull(configMap, "brooklynProperties");
+        
         // TODO in a persisted world the planeId may be injected
         this.managementPlaneId = Strings.makeRandomId(8);
-        
         this.managementNodeId = Strings.makeRandomId(8);
-        checkNotNull(configMap, "brooklynProperties");
         this.builder = builder;
         this.brooklynAdditionalProperties = brooklynAdditionalProperties;
         if (brooklynAdditionalProperties != null)
             configMap.addFromMap(brooklynAdditionalProperties);
         
+        BrooklynFeatureEnablement.init(configMap);
+        
         this.locationManager = new LocalLocationManager(this);
         this.accessManager = new LocalAccessManager();
         this.usageManager = new LocalUsageManager(this);
@@ -385,6 +389,8 @@ public class LocalManagementContext extends AbstractManagementContext {
 
         clearLocationRegistry();
         
+        BrooklynFeatureEnablement.init(configMap);
+        
         // Notify listeners that properties have been reloaded
         for (PropertiesReloadListener listener : reloadListeners) {
             listener.reloaded();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ce168285/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java b/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java
index 38b9ee5..ea0fed6 100644
--- a/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java
+++ b/core/src/test/java/brooklyn/internal/BrooklynFeatureEnablementTest.java
@@ -25,6 +25,8 @@ import static org.testng.Assert.assertTrue;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.config.BrooklynProperties;
+
 public class BrooklynFeatureEnablementTest {
 
     @BeforeMethod(alwaysRun=true)
@@ -88,4 +90,29 @@ public class BrooklynFeatureEnablementTest {
             System.clearProperty(featureProperty);
         }
     }
+    
+    @Test
+    public void testCanSetDefaultWhichIsIgnoredIfBrooklynProps() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testCanSetDefaultWhichIsIgnoredIfBrooklynProps";
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(featureProperty, false);
+        BrooklynFeatureEnablement.init(props);
+        BrooklynFeatureEnablement.setDefault(featureProperty, true);
+        assertFalse(BrooklynFeatureEnablement.isEnabled(featureProperty));
+    }
+    
+    @Test
+    public void testPrefersSysPropOverBrooklynProps() throws Exception {
+        String featureProperty = "brooklyn.experimental.feature.testPrefersSysPropOverBrooklynProps";
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put(featureProperty, false);
+        System.setProperty(featureProperty, "true");
+        try {
+            BrooklynFeatureEnablement.init(props);
+            BrooklynFeatureEnablement.setDefault(featureProperty, true);
+            assertTrue(BrooklynFeatureEnablement.isEnabled(featureProperty));
+        } finally {
+            System.clearProperty(featureProperty);
+        }
+    }
 }


[3/7] incubator-brooklyn git commit: Fix SshMachineLocation pool’s displayName

Posted by al...@apache.org.
Fix SshMachineLocation pool’s displayName


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/06731468
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/06731468
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/06731468

Branch: refs/heads/master
Commit: 0673146862730a07b6469981979b2bb2ea20c0b8
Parents: 6455a9d
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jan 8 21:09:07 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Jan 14 22:38:08 2015 +0000

----------------------------------------------------------------------
 .../main/java/brooklyn/location/basic/SshMachineLocation.java   | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/06731468/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java b/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
index 59a273e..c7b202a 100644
--- a/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
+++ b/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
@@ -304,10 +304,9 @@ public class SshMachineLocation extends AbstractLocation implements MachineLocat
 
     private BasicPool<SshTool> buildPool(final Map<String, ?> properties) {
         return BasicPool.<SshTool>builder()
-                .name(getDisplayName()+"@"+address+
+                .name(getDisplayName()+"@"+address+":"+getPort()+
                         (hasConfig(SSH_HOST, true) ? "("+getConfig(SSH_HOST)+":"+getConfig(SSH_PORT)+")" : "")+
-                        ":"+
-                        System.identityHashCode(this))
+                        ":hash"+System.identityHashCode(this))
                 .supplier(new Supplier<SshTool>() {
                         @Override public SshTool get() {
                             return connectSsh(properties);