Merge pull request #1030 from Nakomis/postgresql-disconnect-on-stop

Removed .failOnNonZeroResultCode(), following review from ahgittin
diff --git a/examples/simple-nosql-cluster/pom.xml b/examples/simple-nosql-cluster/pom.xml
index 3ec813b..b54cb54 100644
--- a/examples/simple-nosql-cluster/pom.xml
+++ b/examples/simple-nosql-cluster/pom.xml
@@ -27,6 +27,13 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        
+        <dependency>
+            <groupId>io.brooklyn</groupId>
+            <artifactId>brooklyn-logback-xml</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/sandbox/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/sandbox/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
index 341a002..01b9c65 100644
--- a/sandbox/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
+++ b/sandbox/camp/src/main/java/io/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
@@ -48,6 +48,7 @@
 
 public class BrooklynAssemblyTemplateInstantiator implements AssemblyTemplateInstantiator {
 
+    public static String TARGET_LOCATION = "localhost";
     private static final Logger log = LoggerFactory.getLogger(BrooklynAssemblyTemplateInstantiator.class);
     
     @Override
@@ -167,7 +168,7 @@
 
         // TODO support other places besides localhost
         List<Location> locations = 
-                getBrooklynManagementContext(platform).getLocationRegistry().resolve(Arrays.asList("localhost"));
+                getBrooklynManagementContext(platform).getLocationRegistry().resolve(Arrays.asList(TARGET_LOCATION));
         
         return Entities.invokeEffector((EntityLocal)app, app, Startable.START,
                 MutableMap.of("locations", locations));
diff --git a/sandbox/camp/src/test/java/io/brooklyn/camp/brooklyn/YamlLauncher.java b/sandbox/camp/src/test/java/io/brooklyn/camp/brooklyn/YamlLauncher.java
new file mode 100644
index 0000000..260596f
--- /dev/null
+++ b/sandbox/camp/src/test/java/io/brooklyn/camp/brooklyn/YamlLauncher.java
@@ -0,0 +1,84 @@
+package io.brooklyn.camp.brooklyn;
+
+import io.brooklyn.camp.CampServer;
+import io.brooklyn.camp.brooklyn.spi.creation.BrooklynAssemblyTemplateInstantiator;
+import io.brooklyn.camp.brooklyn.spi.lookup.BrooklynUrlLookup;
+import io.brooklyn.camp.spi.Assembly;
+import io.brooklyn.camp.spi.AssemblyTemplate;
+import io.brooklyn.camp.spi.PlatformRootSummary;
+
+import java.io.Reader;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynProperties;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.BrooklynTasks;
+import brooklyn.entity.basic.Entities;
+import brooklyn.launcher.BrooklynLauncher;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.Task;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.stream.Streams;
+
+public class YamlLauncher {
+
+    private static final Logger log = LoggerFactory.getLogger(YamlLauncher.class);
+    
+    private ManagementContext brooklynMgmt;
+    private BrooklynCampPlatform platform;
+
+    public void launchPlatform() {
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance()
+              .start();
+        ((BrooklynProperties)launcher.getServerDetails().getManagementContext().getConfig()).
+          put(BrooklynUrlLookup.BROOKLYN_ROOT_URL, launcher.getServerDetails().getWebServerUrl());
+        brooklynMgmt = launcher.getServerDetails().getManagementContext();
+      
+        platform = new BrooklynCampPlatform(
+              PlatformRootSummary.builder().name("Brooklyn CAMP Platform").build(),
+              brooklynMgmt);
+        
+        new CampServer(platform, "").start();
+    }
+    
+    public void launchAppYaml(String filename) {
+        try {
+            Reader input = Streams.reader(new ResourceUtils(this).getResourceFromUrl(filename));
+            AssemblyTemplate at = platform.pdp().registerDeploymentPlan(input);
+
+            Assembly assembly = at.getInstantiator().newInstance().instantiate(at, platform);
+            Entity app = brooklynMgmt.getEntityManager().getEntity(assembly.getId());
+            log.info("Launching "+app);
+
+            Set<Task<?>> tasks = BrooklynTasks.getTasksInEntityContext(brooklynMgmt.getExecutionManager(), app);
+            log.info("Waiting on "+tasks.size()+" task(s)");
+            for (Task<?> t: tasks) t.blockUntilEnded();
+
+            log.info("App started:");
+            Entities.dumpInfo(app);
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    public static void main(String[] args) {
+        BrooklynAssemblyTemplateInstantiator.TARGET_LOCATION =
+            "localhost"
+            //"named:hpcloud-compute-us-west-az1"
+            //"aws-ec2:us-west-2"
+            ;
+        
+        YamlLauncher l = new YamlLauncher();
+        l.launchPlatform();
+        
+//        l.launchAppYaml("java-web-app-and-db-with-function.yaml");
+//        l.launchAppYaml("java-web-app-and-memsql.yaml");
+//        l.launchAppYaml("memsql.yaml");
+        l.launchAppYaml("playing.yaml");
+    }
+    
+}
diff --git a/sandbox/camp/src/test/resources/playing.yaml b/sandbox/camp/src/test/resources/playing.yaml
new file mode 100644
index 0000000..a47f7a0
--- /dev/null
+++ b/sandbox/camp/src/test/resources/playing.yaml
@@ -0,0 +1,3 @@
+name: cassandra node
+services:
+- type: brooklyn.entity.nosql.cassandra.CassandraNode
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java
index 1234421..0c71930 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeDriver.java
@@ -19,4 +19,6 @@
 
     String getCassandraConfigFileName();
 
+    boolean isClustered();
+
 }
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java
index cede56b..01ad632 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeImpl.java
@@ -114,11 +114,14 @@
             // http://www.datastax.com/documentation/cassandra/2.0/mobile/cassandra/architecture/architectureSnitchEC2MultiRegion_c.html
             // describes that the listen_address is set to the private IP, and the broadcast_address is set to the public IP.
             return getPublicIp();
+        } else if (!getDriver().isClustered()) {
+            return getListenAddress();
         } else {
             // In other situations, prefer the hostname so other regions can see it
             return getAttribute(CassandraNode.HOSTNAME);
         }
     }
+    
     public String getPrivateIp() {
         if (requiresAlwaysPublicIp()) {
             return getAttribute(CassandraNode.ADDRESS);
@@ -202,6 +205,10 @@
     public Class<CassandraNodeDriver> getDriverInterface() {
         return CassandraNodeDriver.class;
     }
+    
+    public CassandraNodeDriver getDriver() {
+        return (CassandraNodeDriver) super.getDriver();
+    }
 
     @Override
     public void init() {
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java
index 1d54fad..b2c331f 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/cassandra/CassandraNodeSshDriver.java
@@ -176,7 +176,8 @@
         }
     }
 
-    protected boolean isClustered() {
+    @Override
+    public boolean isClustered() {
         return entity.getParent() instanceof CassandraCluster;
     }
 
diff --git a/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java b/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
index 9679e12..95ac997 100644
--- a/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
+++ b/software/webapp/src/main/java/brooklyn/entity/proxy/AbstractControllerImpl.java
@@ -268,7 +268,7 @@
     @Override
     public void update() {
         Task<?> task = updateAsync();
-        task.getUnchecked();
+        if (task != null) task.getUnchecked();
     }
     
     public synchronized Task<?> updateAsync() {
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6Server.java b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6Server.java
index ae82371..7c10af8 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6Server.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6Server.java
@@ -15,6 +15,11 @@
 @ImplementedBy(JBoss6ServerImpl.class)
 public interface JBoss6Server extends JavaWebAppSoftwareProcess, JavaWebAppService, UsesJmx {
 
+    // TODO Instead of using portIncrement, would prefer to use http_port as "8080+" etc.
+    // On localhost, if an existing jboss6 is running and consuming the required port(s), 
+    // then we don't spot that and don't claim a different port.
+    // Things then fail silently!
+    
     @SetFromFlag("version")
     ConfigKey<String> SUGGESTED_VERSION =
             ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "6.0.0.Final");
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java
index 4855c4f..be8c585 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/jboss/JBoss6SshDriver.java
@@ -11,8 +11,11 @@
 
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.drivers.downloads.DownloadResolver;
+import brooklyn.entity.java.UsesJmx;
+import brooklyn.entity.java.UsesJmx.JmxAgentModes;
 import brooklyn.entity.webapp.JavaWebAppSshDriver;
 import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Networking;
 import brooklyn.util.ssh.BashCommands;
 
@@ -115,8 +118,8 @@
         String clusterArg = isEmpty(getClusterName()) ? "":"-g "+getClusterName();
         // run.sh must be backgrounded otherwise the script will never return.
 
-        Map<String,Object> flags = new HashMap<String, Object>();
-        flags.put("usePidFile",false);
+        // Don't automatically create pid; instead set JBOSS_PIDFILE to create the pid file we need
+        Map<String,?> flags = MutableMap.of("usePidFile", false);
 
         // We wait for evidence of tomcat running because, using 
         // brooklyn.ssh.config.tool.class=brooklyn.util.internal.ssh.cli.SshCliTool,
@@ -125,6 +128,7 @@
         newScript(flags, LAUNCHING).
             body.append(
                 format("export JBOSS_CLASSPATH=%s/lib/jboss-logmanager.jar",getExpandedInstallDir()),
+                format("export JBOSS_PIDFILE=%s/%s", getRunDir(), PID_FILENAME),
                 format("%s/bin/run.sh -Djboss.service.binding.set=%s -Djboss.server.base.dir=$RUN_DIR/server ",getExpandedInstallDir(),PORT_GROUP_NAME) +
                         format("-Djboss.server.base.url=file://$RUN_DIR/server -Djboss.messaging.ServerPeerID=%s ",entity.getId())+
                         format("-Djboss.boot.server.log.dir=%s/server/%s/log ",getRunDir(),SERVER_TYPE) +
@@ -141,39 +145,51 @@
 
     @Override
     public boolean isRunning() {
-        String host = entity.getAttribute(Attributes.HOSTNAME);
-        Integer port = entity.getAttribute(Attributes.JMX_PORT);
-
-        List<String> checkRunningScript = new LinkedList<String>();
-        checkRunningScript.add(
-                format("%s/bin/twiddle.sh --host %s --port %s get \"jboss.system:type=Server\" Started | grep true || exit 1",
-                        getExpandedInstallDir(), host, port));
-
-        //have to override the CLI/JMX options
-
-        Map<String, Object> flags = new LinkedHashMap<String, Object>();
-        flags.put("env", new LinkedHashMap<String, String>());
-
-        int result = execute(flags, checkRunningScript, "checkRunning " + entity + " on " + getMachine());
-        if (result == 0) return true;
-        if (result == 1) return false;
-        throw new IllegalStateException(format("%s running check gave result code %s",getEntity(),result));
+        JmxAgentModes jmxMode = entity.getConfig(UsesJmx.JMX_AGENT_MODE);
+        if (jmxMode == JmxAgentModes.JMX_RMI_CUSTOM_AGENT) {
+            String host = entity.getAttribute(Attributes.HOSTNAME);
+            Integer port = entity.getAttribute(UsesJmx.RMI_REGISTRY_PORT);
+    
+            List<String> checkRunningScript = new LinkedList<String>();
+            checkRunningScript.add(
+                    format("%s/bin/twiddle.sh --host %s --port %s get \"jboss.system:type=Server\" Started | grep true || exit 1",
+                            getExpandedInstallDir(), host, port));
+    
+            //have to override the CLI/JMX options
+    
+            Map<String, Object> flags = new LinkedHashMap<String, Object>();
+            flags.put("env", new LinkedHashMap<String, String>());
+    
+            int result = execute(flags, checkRunningScript, "checkRunning " + entity + " on " + getMachine());
+            if (result == 0) return true;
+            if (result == 1) return false;
+            throw new IllegalStateException(format("%s running check gave result code %s",getEntity(),result));
+        } else {
+            Map<String,?> flags = MutableMap.of("usePidFile", true);
+            return newScript(flags, CHECK_RUNNING).execute() == 0;
+        }
     }
 
     @Override
     public void stop() {
-        String host = entity.getAttribute(Attributes.HOSTNAME);
-        Integer port = entity.getAttribute(Attributes.JMX_PORT);
-        List<String> shutdownScript = new LinkedList<String>();
-        shutdownScript.add(format("%s/bin/shutdown.sh --host %s --port %s -S", getExpandedInstallDir(), host, port));
-
-        //again, messy copy of parent; but new driver scheme could allow script-helper to customise parameters
-        log.debug("invoking shutdown script for {}: {}", entity, shutdownScript);
-        Map<String, Object> flags = new LinkedHashMap<String, Object>();
-        flags.put("env", new LinkedHashMap<String, String>());
-        int result = execute(flags, shutdownScript, "shutdown " + entity + " on " + getMachine());
-        if (result != 0) log.warn("non-zero result code terminating {}: {}", entity, result);
-        log.debug("done invoking shutdown script for {}", entity);
+        JmxAgentModes jmxMode = entity.getConfig(UsesJmx.JMX_AGENT_MODE);
+        if (jmxMode == JmxAgentModes.JMX_RMI_CUSTOM_AGENT) {
+            String host = entity.getAttribute(Attributes.HOSTNAME);
+            Integer port = entity.getAttribute(UsesJmx.RMI_REGISTRY_PORT);
+            List<String> shutdownScript = new LinkedList<String>();
+            shutdownScript.add(format("%s/bin/shutdown.sh --host %s --port %s -S", getExpandedInstallDir(), host, port));
+    
+            //again, messy copy of parent; but new driver scheme could allow script-helper to customise parameters
+            log.debug("invoking shutdown script for {}: {}", entity, shutdownScript);
+            Map<String, Object> flags = new LinkedHashMap<String, Object>();
+            flags.put("env", new LinkedHashMap<String, String>());
+            int result = execute(flags, shutdownScript, "shutdown " + entity + " on " + getMachine());
+            if (result != 0) log.warn("non-zero result code terminating {}: {}", entity, result);
+            log.debug("done invoking shutdown script for {}", entity);
+        } else {
+            Map<String,?> flags = MutableMap.of("usePidFile", true);
+            newScript(flags, STOPPING).execute();
+        }
     }
 
     @Override
diff --git a/software/webapp/src/main/java/brooklyn/entity/webapp/tomcat/TomcatServerImpl.java b/software/webapp/src/main/java/brooklyn/entity/webapp/tomcat/TomcatServerImpl.java
index 596eb7e..a6c1b72 100644
--- a/software/webapp/src/main/java/brooklyn/entity/webapp/tomcat/TomcatServerImpl.java
+++ b/software/webapp/src/main/java/brooklyn/entity/webapp/tomcat/TomcatServerImpl.java
@@ -1,6 +1,6 @@
 package brooklyn.entity.webapp.tomcat;
 
-import static java.lang.String.*;
+import static java.lang.String.format;
 
 import java.util.concurrent.TimeUnit;
 
@@ -71,7 +71,7 @@
     public void disconnectSensors() {
         super.disconnectSensors();
         if (getDriver().isJmxEnabled()) {
-           if (jmxFeed.isActivated()) jmxFeed.stop();
+           if (jmxFeed != null) jmxFeed.stop();
         } else {
             disconnectServiceUpIsRunning();
         }
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/TomcatAutoScalerPolicyTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/TomcatAutoScalerPolicyTest.java
index 251510a..4196442 100644
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/TomcatAutoScalerPolicyTest.java
+++ b/software/webapp/src/test/java/brooklyn/entity/webapp/TomcatAutoScalerPolicyTest.java
@@ -4,6 +4,8 @@
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -25,7 +27,10 @@
 import com.google.common.collect.Iterables;
 
 public class TomcatAutoScalerPolicyTest {
-    
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(TomcatAutoScalerPolicyTest.class);
+
     // TODO Test is time-sensitive: we send two web-requests in rapid succession, and expect the average workrate to
     // be 2 msgs/sec; we then expect resizing to kick-in.
     // P speculate that... if for some reason things are running slow (e.g. GC during that one second), then brooklyn 
@@ -33,7 +38,7 @@
 
     private LocalhostMachineProvisioningLocation loc;
     private TestApplication app;
-
+    
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
         loc = new LocalhostMachineProvisioningLocation(MutableMap.of("name", "london"));
@@ -78,23 +83,29 @@
         
         app.start(ImmutableList.of(loc));
         
-        assertEquals((Integer)1, cluster.getCurrentSize());
+        assertEquals(cluster.getCurrentSize(), (Integer)1);
         
+        // Scaling based on *total requests* processed, rather than the requests per second.
+        // So just hit it with 2 requests.
+        // Alternatively could hit each tomcat server's URL twice per second; but that's less deterministic.
         TomcatServer tc = (TomcatServer) Iterables.getOnlyElement(cluster.getMembers());
         for (int i = 0; i < 2; i++) {
             connectToUrl(tc.getAttribute(TomcatServerImpl.ROOT_URL));
         }
         
-        Asserts.succeedsEventually(MutableMap.of("timeout", 3000), new Runnable() {
-            public void run() {
-                assertEquals(2.0d/cluster.getCurrentSize(), cluster.getAttribute(DynamicWebAppCluster.AVERAGE_REQUEST_COUNT));
-            }});
-
+        // We'll scale to two members as soon as the policy detects it.
+        // But the second member won't count in the requests-per-node until it has started up.
+        // Expect to see (if we polled at convenient times):
+        //  - zero requests per node (because haven't yet retrieved over JMX the metric)
+        //  - two requests per node, with one member
+        //  - two requests per node, with two members (one of whom is still starting up, so doesn't count)
+        //  - one request per node (i.e. two divided across the two active members)
         Asserts.succeedsEventually(MutableMap.of("timeout", 5*60*1000), new Runnable() {
-            public void run() {
-                assertTrue(policy.isRunning());
-                assertEquals((Integer)2, cluster.getCurrentSize());
-                assertEquals(1.0d, cluster.getAttribute(DynamicWebAppCluster.AVERAGE_REQUEST_COUNT));
+            @Override public void run() {
+                String err = "policy="+policy.isRunning()+"; size="+cluster.getCurrentSize()+"; reqCountPerNode="+cluster.getAttribute(DynamicWebAppCluster.REQUEST_COUNT_PER_NODE);
+                assertTrue(policy.isRunning(), "err="+err);
+                assertEquals(cluster.getCurrentSize(), (Integer)2, "err="+err);
+                assertEquals(cluster.getAttribute(DynamicWebAppCluster.REQUEST_COUNT_PER_NODE), 1.0d, "err="+err);
             }});
     }
 }
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/jboss/Jboss6ServerIntegrationTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/jboss/Jboss6ServerIntegrationTest.java
new file mode 100644
index 0000000..b7f261f
--- /dev/null
+++ b/software/webapp/src/test/java/brooklyn/entity/webapp/jboss/Jboss6ServerIntegrationTest.java
@@ -0,0 +1,88 @@
+package brooklyn.entity.webapp.jboss;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.net.URL;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.java.UsesJmx;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.test.Asserts;
+import brooklyn.test.HttpTestUtils;
+import brooklyn.test.entity.TestApplication;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * TODO re-write this like WebAppIntegrationTest, rather than being jboss6 specific.
+ */
+public class Jboss6ServerIntegrationTest {
+    
+    // Port increment for JBoss 6.
+    public static final int PORT_INCREMENT = 400;
+
+    private URL warUrl;
+    private LocalhostMachineProvisioningLocation localhostProvisioningLocation;
+    private TestApplication app;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        String warPath = "hello-world.war";
+        warUrl = getClass().getClassLoader().getResource(warPath);
+
+        localhostProvisioningLocation = new LocalhostMachineProvisioningLocation();
+        app = ApplicationBuilder.newManagedApp(TestApplication.class);
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+    }
+
+    @Test(groups = "Integration")
+    public void testJmxmp() throws Exception {
+        runTest(UsesJmx.JmxAgentModes.JMXMP);
+    }
+
+    @Test(groups = "Integration")
+    public void testJmxRmi() throws Exception {
+        runTest(UsesJmx.JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+    }
+    
+    @Test(groups = "Integration")
+    public void testJmxAutodetect() throws Exception {
+        runTest(UsesJmx.JmxAgentModes.AUTODETECT);
+    }
+    
+    protected void runTest(UsesJmx.JmxAgentModes jmxAgentMode) throws Exception {
+        final JBoss6Server server = app.createAndManageChild(EntitySpec.create(JBoss6Server.class)
+                .configure(JBoss6Server.PORT_INCREMENT, PORT_INCREMENT)
+                .configure(UsesJmx.JMX_AGENT_MODE, jmxAgentMode)
+                .configure("war", warUrl.toString()));
+
+        app.start(ImmutableList.of(localhostProvisioningLocation));
+        
+        String httpUrl = "http://"+server.getAttribute(JBoss6Server.HOSTNAME)+":"+server.getAttribute(JBoss6Server.HTTP_PORT)+"/";
+        
+        assertEquals(server.getAttribute(JBoss6Server.ROOT_URL).toLowerCase(), httpUrl.toLowerCase());
+        
+        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(httpUrl, 200);
+        HttpTestUtils.assertContentContainsText(httpUrl, "Hello");
+
+
+        Asserts.succeedsEventually(new Runnable() {
+            @Override public void run() {
+                // TODO Could test other attributes as well; see jboss7 test
+                assertNotNull(server.getAttribute(JBoss6Server.REQUEST_COUNT));
+                assertNotNull(server.getAttribute(JBoss6Server.ERROR_COUNT));
+                assertNotNull(server.getAttribute(JBoss6Server.TOTAL_PROCESSING_TIME));
+            }});
+    }
+}
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSimpleIntegrationTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSimpleIntegrationTest.java
index 8233dc4..3c6c04d 100644
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSimpleIntegrationTest.java
+++ b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSimpleIntegrationTest.java
@@ -1,10 +1,10 @@
 package brooklyn.entity.webapp.tomcat;
 
-import static brooklyn.test.TestUtils.isPortInUse;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.fail;
 
 import java.net.ServerSocket;
+import java.util.Iterator;
 
 import org.jclouds.util.Throwables2;
 import org.slf4j.Logger;
@@ -16,8 +16,11 @@
 import brooklyn.entity.basic.ApplicationBuilder;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.PortRange;
 import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.location.basic.PortRanges;
 import brooklyn.test.entity.TestApplication;
+import brooklyn.util.net.Networking;
 
 import com.google.common.collect.ImmutableList;
 
@@ -30,20 +33,23 @@
     @SuppressWarnings("unused")
     private static final Logger LOG = LoggerFactory.getLogger(TomcatServerSimpleIntegrationTest.class);
     
-    /** don't use 8080 since that is commonly used by testing software */
-    static int DEFAULT_HTTP_PORT = 7880;
-
-    static boolean httpPortLeftOpen = false;
+    /** don't use 8080 since that is commonly used by testing software; use different from other tests. */
+    static PortRange DEFAULT_HTTP_PORT_RANGE = PortRanges.fromString("7880-7980");
     
     private TestApplication app;
     private TomcatServer tc;
+    private int httpPort;
     
     @BeforeMethod(alwaysRun=true)
-    public void failIfHttpPortInUse() {
-        if (isPortInUse(DEFAULT_HTTP_PORT, 5000L)) {
-            httpPortLeftOpen = true;
-            fail("someone is already listening on port "+DEFAULT_HTTP_PORT+"; tests assume that port "+DEFAULT_HTTP_PORT+" is free on localhost");
+    public void pickFreePort() {
+        for (Iterator<Integer> iter = DEFAULT_HTTP_PORT_RANGE.iterator(); iter.hasNext();) {
+            Integer port = iter.next();
+            if (Networking.isPortAvailable(port)) {
+                httpPort = port;
+                return;
+            }
         }
+        fail("someone is already listening on ports "+DEFAULT_HTTP_PORT_RANGE+"; tests assume that port is free on localhost");
     }
  
     @AfterMethod(alwaysRun=true)
@@ -53,10 +59,10 @@
     
     @Test(groups="Integration")
     public void detectFailureIfTomcatCantBindToPort() throws Exception {
-        ServerSocket listener = new ServerSocket(DEFAULT_HTTP_PORT);
+        ServerSocket listener = new ServerSocket(httpPort);
         try {
             app = ApplicationBuilder.newManagedApp(TestApplication.class);
-            tc = app.createAndManageChild(EntitySpec.create(TomcatServer.class).configure("httpPort",DEFAULT_HTTP_PORT));
+            tc = app.createAndManageChild(EntitySpec.create(TomcatServer.class).configure("httpPort", httpPort));
             
             try {
                 tc.start(ImmutableList.of(app.getManagementContext().getLocationManager().manage(new LocalhostMachineProvisioningLocation())));