brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [10/72] [abbrv] incubator-brooklyn git commit: BROOKLYN-162 - apply org.apache package prefix to software-base, tidying package names, and moving a few sensory things to core
Date Wed, 19 Aug 2015 11:09:28 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java
new file mode 100644
index 0000000..3250ee3
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/AbstractChefToyMySqlEntityLiveTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.testng.annotations.Test;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.entity.chef.ChefLiveTestSupport;
+import org.apache.brooklyn.entity.software.base.test.mysql.AbstractToyMySqlEntityTest;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+public abstract class AbstractChefToyMySqlEntityLiveTest extends AbstractToyMySqlEntityTest {
+
+    @Override
+    // mark as live here
+    @Test(groups = "Live")
+    public void testMySqlOnProvisioningLocation() throws Exception {
+        super.testMySqlOnProvisioningLocation();
+    }
+    
+    protected MachineProvisioningLocation<? extends SshMachineLocation> createLocation() {
+        return ChefLiveTestSupport.createLocation(mgmt);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java
new file mode 100644
index 0000000..e6c7b27
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverMySqlEntityLiveTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.sensor.ssh.SshEffectorTasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.testng.annotations.Test;
+
+public class ChefSoloDriverMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest {
+
+    // test here just so Eclipse IDE picks it up
+    @Override @Test(groups="Live")
+    public void testMySqlOnProvisioningLocation() throws Exception {
+        super.testMySqlOnProvisioningLocation();
+    }
+
+    @Override
+    protected Integer getPid(Entity mysql) {
+        ProcessTaskWrapper<Integer> t = Entities.submit(mysql, SshEffectorTasks.ssh("sudo cat "+ChefSoloDriverToyMySqlEntity.PID_FILE));
+        return Integer.parseInt(t.block().getStdout().trim());
+    }
+    
+    @Override
+    protected Entity createMysql() {
+        return app.createAndManageChild(EntitySpec.create(Entity.class, ChefSoloDriverToyMySqlEntity.class).
+                additionalInterfaces(SoftwareProcess.class));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java
new file mode 100644
index 0000000..c36dc21
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/ChefSoloDriverToyMySqlEntity.java
@@ -0,0 +1,89 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.api.mgmt.TaskFactory;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.entity.chef.ChefConfig;
+import org.apache.brooklyn.entity.chef.ChefConfigs;
+import org.apache.brooklyn.entity.chef.ChefSoloDriver;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
+import org.apache.brooklyn.sensor.feed.ssh.SshFeed;
+import org.apache.brooklyn.sensor.feed.ssh.SshPollConfig;
+import org.apache.brooklyn.sensor.ssh.SshEffectorTasks;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.time.Duration;
+
+@Deprecated /** @deprecated since 0.7.0 use see examples {Dynamic,Typed}ToyMySqlEntityChef */
+public class ChefSoloDriverToyMySqlEntity extends SoftwareProcessImpl implements ChefConfig {
+
+    public static final String PID_FILE = "/var/run/mysqld/mysqld.pid";
+    public static final ConfigKey<TaskFactory<? extends TaskAdaptable<Boolean>>> IS_RUNNING_TASK =
+            ConfigKeys.newConfigKeyWithDefault(ChefSoloDriver.IS_RUNNING_TASK, 
+            SshEffectorTasks.isPidFromFileRunning(PID_FILE).runAsRoot());
+
+    public static final ConfigKey<TaskFactory<?>> STOP_TASK =
+            ConfigKeys.newConfigKeyWithDefault(ChefSoloDriver.STOP_TASK, 
+            SshEffectorTasks.ssh("/etc/init.d/mysql stop").allowingNonZeroExitCode().runAsRoot());
+
+    private SshFeed upFeed;
+    
+    @Override
+    public Class<?> getDriverInterface() {
+        return ChefSoloDriver.class;
+    }
+
+    @Override
+    protected void connectSensors() {
+        super.connectSensors();
+        
+        // TODO have a TaskFactoryFeed which reuses the IS_RUNNING_TASK
+        upFeed = SshFeed.builder().entity(this).period(Duration.FIVE_SECONDS.toMilliseconds())
+            .poll(new SshPollConfig<Boolean>(SERVICE_UP)
+                    .command("ps -p `sudo cat /var/run/mysqld/mysqld.pid`")
+                    .setOnSuccess(true).setOnFailureOrException(false))
+            .build();
+    }
+    
+    @Override
+    protected void disconnectSensors() {
+        // TODO nicer way to disconnect
+        if (upFeed != null) upFeed.stop();
+        super.disconnectSensors();
+    }
+    
+    @Override
+    public void init() {
+        super.init();
+        ChefConfigs.addToLaunchRunList(this, "mysql::server");
+        ChefConfigs.addToCookbooksFromGithub(this, "mysql", "build-essential", "openssl");
+        ChefConfigs.setLaunchAttribute(this, "mysql",  
+                MutableMap.of()
+                    .add("server_root_password", "MyPassword")
+                    .add("server_debian_password", "MyPassword")
+                    .add("server_repl_password", "MyPassword")
+                );
+        
+        // TODO other attributes, eg:
+        // node['mysql']['port']
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java
new file mode 100644
index 0000000..0102f6e
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefAutodetectToyMySqlEntityLiveTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+public class DynamicChefAutodetectToyMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest {
+
+    private static final Logger log = LoggerFactory.getLogger(DynamicChefAutodetectToyMySqlEntityLiveTest.class);
+    
+    // test here just so Eclipse IDE picks it up
+    @Override @Test(groups="Live")
+    public void testMySqlOnProvisioningLocation() throws Exception {
+        super.testMySqlOnProvisioningLocation();
+    }
+    
+    @Override
+    protected Entity createMysql() {
+        Entity mysql = app.createAndManageChild(DynamicToyMySqlEntityChef.spec());
+        log.debug("created "+mysql);
+        return mysql;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java
new file mode 100644
index 0000000..566a96e
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefServerToyMySqlEntityLiveTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.entity.chef.ChefLiveTestSupport;
+import org.apache.brooklyn.entity.chef.ChefServerTasksIntegrationTest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+/** Expects knife on the path, but will use Brooklyn registered account,
+ * and that account has the mysql recipe installed.
+ * <p>
+ * See {@link ChefServerTasksIntegrationTest} for more info. */
+public class DynamicChefServerToyMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest {
+
+    private static final Logger log = LoggerFactory.getLogger(DynamicChefServerToyMySqlEntityLiveTest.class);
+    
+    // test here just so Eclipse IDE picks it up
+    @Override @Test(groups="Live")
+    public void testMySqlOnProvisioningLocation() throws Exception {
+        super.testMySqlOnProvisioningLocation();
+    }
+    
+    @Override
+    protected Entity createMysql() {
+        ChefLiveTestSupport.installBrooklynChefHostedConfig(app);
+        Entity mysql = app.createAndManageChild(DynamicToyMySqlEntityChef.specKnife());
+        log.debug("created "+mysql);
+        return mysql;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java
new file mode 100644
index 0000000..ba6c6d9
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicChefSoloToyMySqlEntityLiveTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+public class DynamicChefSoloToyMySqlEntityLiveTest extends AbstractChefToyMySqlEntityLiveTest {
+
+    private static final Logger log = LoggerFactory.getLogger(DynamicChefSoloToyMySqlEntityLiveTest.class);
+    
+    // test here just so Eclipse IDE picks it up
+    @Override @Test(groups="Live")
+    public void testMySqlOnProvisioningLocation() throws Exception {
+        super.testMySqlOnProvisioningLocation();
+    }
+    
+    @Override
+    protected Entity createMysql() {
+        Entity mysql = app.createAndManageChild(DynamicToyMySqlEntityChef.specSolo());
+        log.debug("created "+mysql);
+        return mysql;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java
new file mode 100644
index 0000000..2edd8a1
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/DynamicToyMySqlEntityChef.java
@@ -0,0 +1,81 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.entity.chef.ChefConfig;
+import org.apache.brooklyn.entity.chef.ChefConfigs;
+import org.apache.brooklyn.entity.chef.ChefEntity;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Builds up a MySql entity via chef using specs only */
+public class DynamicToyMySqlEntityChef implements ChefConfig {
+
+    private static final Logger log = LoggerFactory.getLogger(DynamicToyMySqlEntityChef.class);
+
+    protected static EntitySpec<? extends Entity> specBase() {
+        EntitySpec<ChefEntity> spec = EntitySpec.create(ChefEntity.class);
+        
+        ChefConfigs.addToLaunchRunList(spec, "mysql::server");
+        spec.configure(PID_FILE, "/var/run/mysqld/mysql*.pid");
+        // init.d service name is sometimes mysql, sometimes mysqld, depending ubuntu/centos
+        // we use pid file above instead, but this (with the right name) could be used:
+//        spec.configure(SERVICE_NAME, "mysql");
+        
+        // chef mysql fails on first run but works on second if switching between server and solo modes
+        spec.configure(ChefConfig.CHEF_RUN_CONVERGE_TWICE, true);
+
+        // only used for solo, but safely ignored for knife
+        ChefConfigs.addToCookbooksFromGithub(spec, "mysql", "build-essential", "openssl");
+        // we always need dependent cookbooks set, and mysql requires password set
+        // (TODO for knife we might wish to prefer things from the server)
+        ChefConfigs.addLaunchAttributes(spec, MutableMap.of("mysql",  
+                MutableMap.of()
+                .add("server_root_password", "MyPassword")
+                .add("server_debian_password", "MyPassword")
+                .add("server_repl_password", "MyPassword")
+            ));
+        
+        return spec;
+    }
+
+    public static EntitySpec<? extends Entity> spec() {
+        EntitySpec<? extends Entity> spec = specBase();
+        log.debug("Created entity spec for MySql: "+spec);
+        return spec;
+    }
+
+    public static EntitySpec<? extends Entity> specSolo() {
+        EntitySpec<? extends Entity> spec = specBase();
+        spec.configure(ChefConfig.CHEF_MODE, ChefConfig.ChefModes.SOLO);
+        log.debug("Created entity spec for MySql: "+spec);
+        return spec;
+    }
+
+    public static EntitySpec<? extends Entity> specKnife() {
+        EntitySpec<? extends Entity> spec = specBase();
+        spec.configure(ChefConfig.CHEF_MODE, ChefConfig.ChefModes.KNIFE);
+        log.debug("Created entity spec for MySql: "+spec);
+        return spec;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java
new file mode 100644
index 0000000..44a622a
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/chef/mysql/TypedToyMySqlEntityChef.java
@@ -0,0 +1,55 @@
+/*
+ * 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 org.apache.brooklyn.entity.chef.mysql;
+
+import org.apache.brooklyn.entity.chef.ChefConfig;
+import org.apache.brooklyn.entity.chef.ChefEntityImpl;
+import org.apache.brooklyn.util.git.GithubUrls;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+/** Illustrates how to define an entity using Java as a Java class, extending ChefEntityImpl */
+public class TypedToyMySqlEntityChef extends ChefEntityImpl {
+
+    @Override
+    public void init() {
+        super.init();
+
+        String password = "p4ssw0rd";
+        
+        setConfig(CHEF_COOKBOOK_PRIMARY_NAME, "mysql");
+        setConfig(CHEF_COOKBOOK_URLS, ImmutableMap.of(
+            "mysql", GithubUrls.tgz("opscode-cookbooks", "mysql", "v4.0.12"),
+            "openssl", GithubUrls.tgz("opscode-cookbooks", "openssl", "v1.1.0"),
+            "mysql", GithubUrls.tgz("opscode-cookbooks", "build-essential", "v1.4.4")));
+        
+        setConfig(CHEF_LAUNCH_RUN_LIST, ImmutableSet.of("mysql::server"));
+        setConfig(CHEF_LAUNCH_ATTRIBUTES, ImmutableMap.<String,Object>of(
+            "mysql", ImmutableMap.of(
+                "server_root_password", password,
+                "server_repl_password", password,
+                "server_debian_password", password)));
+        
+        setConfig(ChefConfig.PID_FILE, "/var/run/mysqld/mysqld.pid");
+        
+        setConfig(CHEF_MODE, ChefModes.SOLO);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java
new file mode 100644
index 0000000..b49e235
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/EntityPollingTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.java.UsesJmx;
+import org.apache.brooklyn.entity.java.VanillaJavaAppImpl;
+import org.apache.brooklyn.entity.java.VanillaJavaAppSshDriver;
+import org.apache.brooklyn.entity.java.UsesJmx.JmxAgentModes;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.software.base.test.jmx.JmxService;
+import org.apache.brooklyn.sensor.core.BasicAttributeSensor;
+import org.apache.brooklyn.sensor.feed.jmx.JmxAttributePollConfig;
+import org.apache.brooklyn.sensor.feed.jmx.JmxFeed;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class EntityPollingTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(EntityPollingTest.class);
+
+    private JmxService jmxService;
+    private TestApplication app;
+    private SoftwareProcess entity;
+    
+    private static final BasicAttributeSensor<String> stringAttribute = new BasicAttributeSensor<String>(
+            String.class, "brooklyn.test.stringAttribute", "Brooklyn testing int attribute");
+    private String objectName = "Brooklyn:type=MyTestMBean,name=myname";
+    
+    private static final ObjectName jmxObjectName;
+    static {
+        try {
+            jmxObjectName = new ObjectName("Brooklyn:type=MyTestMBean,name=myname");
+        } catch (MalformedObjectNameException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    private static final String attributeName = "myattrib";
+
+    public static class SubVanillaJavaApp extends VanillaJavaAppImpl {
+        private JmxFeed feed;
+        
+        @Override protected void connectSensors() {
+            super.connectSensors();
+   
+            // Add a sensor that we can explicitly set in jmx
+            feed = JmxFeed.builder()
+                .entity(this)
+                .pollAttribute(new JmxAttributePollConfig<String>(stringAttribute)
+                    .objectName(jmxObjectName)
+                    .attributeName(attributeName))
+                .build();
+        }
+
+        @Override
+        public void disconnectSensors() {
+            super.disconnectSensors();
+            if (feed != null) feed.stop();
+        }
+        
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        @Override
+        public Class getDriverInterface() {
+            return null;
+        }
+
+        @Override
+        public VanillaJavaAppSshDriver newDriver(MachineLocation loc) {
+            return new VanillaJavaAppSshDriver(this, (SshMachineLocation)loc) {
+                @Override public void install() {
+                    // no-op
+                }
+                @Override public void customize() {
+                    // no-op
+                }
+                @Override public void launch() {
+                    // no-op
+                }
+                @Override public boolean isRunning() {
+                    return true;
+                }
+                @Override public void stop() {
+                    // no-op
+                }
+                @Override public void kill() {
+                    // no-op
+                }
+            };
+        }
+    };        
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        
+        /*
+         * Create an entity, using real entity code, but that swaps out the external process
+         * for a JmxService that we can control in the test.        
+         */
+        entity = app.createAndManageChild(EntitySpec.create(SoftwareProcess.class).impl(SubVanillaJavaApp.class)
+                .configure("rmiRegistryPort", 40123)
+                .configure("mxbeanStatsEnabled", false)
+                .configure(UsesJmx.JMX_AGENT_MODE, JmxAgentModes.JMX_RMI_CUSTOM_AGENT));
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        if (jmxService != null) jmxService.shutdown();
+    }
+
+    // Tests that the happy path works
+    @Test(groups="Integration")
+    public void testSimpleConnection() throws Exception {
+        jmxService = new JmxService("localhost", 40123);
+        jmxService.registerMBean(ImmutableMap.of(attributeName, "myval"), objectName);
+
+        app.start(ImmutableList.of(new SshMachineLocation(MutableMap.of("address", "localhost"))));
+        
+        // Starts with value defined when registering...
+        EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval");
+    }
+
+    // Test that connect will keep retrying (e.g. start script returns before the JMX server is up)
+    @Test(groups="Integration")
+    public void testEntityWithDelayedJmxStartupWillKeepRetrying() {
+        // In 2 seconds time, we'll start the JMX server
+        Thread t = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    Thread.sleep(2000);
+                    jmxService = new JmxService("localhost", 40123);
+                    jmxService.registerMBean(ImmutableMap.of(attributeName, "myval"), objectName);
+                } catch (Exception e) {
+                    LOG.error("Error in testEntityWithDelayedJmxStartupWillKeepRetrying", e);
+                    throw Exceptions.propagate(e);
+                }
+            }});
+
+        try {
+            t.start();
+            app.start(ImmutableList.of(new SshMachineLocation(MutableMap.of("address", "localhost"))));
+
+            EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval");
+            
+        } finally {
+            t.interrupt();
+        }
+    }
+    
+    @Test(groups="Integration")
+    public void testJmxConnectionGoesDownRequiringReconnect() throws Exception {
+        jmxService = new JmxService("localhost", 40123);
+        jmxService.registerMBean(ImmutableMap.of(attributeName, "myval"), objectName);
+
+        app.start(ImmutableList.of(new SshMachineLocation(MutableMap.of("address", "localhost"))));
+        
+        EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval");
+        
+        // Shutdown the MBeanServer - simulates network failure so can't connect
+        jmxService.shutdown();
+        
+        // TODO Want a better way of determining that the entity is down; ideally should have 
+        // sensor for entity-down that's wired up to a JMX attribute?
+        Thread.sleep(5000);
+
+        // Restart MBeanServer, and set attribute to different value; expect it to be polled again
+        jmxService = new JmxService("localhost", 40123);
+        jmxService.registerMBean(ImmutableMap.of(attributeName, "myval2"), objectName);
+        
+        EntityTestUtils.assertAttributeEqualsEventually(entity, stringAttribute, "myval2");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java
new file mode 100644
index 0000000..114d45d
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMain.java
@@ -0,0 +1,26 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+public class ExampleVanillaMain {
+    public static void main(String[] args) throws Exception {
+        System.out.println("In VanillaJavaExampleMain.main");
+        Thread.sleep(100*1000);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java
new file mode 100644
index 0000000..e45be44
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/ExampleVanillaMainCpuHungry.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+public class ExampleVanillaMainCpuHungry {
+    private static final int MAX_TIME_MILLIS = 100*1000;
+    private static final int CALCULATIONS_PER_CYCLE = 100000;
+    private static final int SLEEP_PER_CYCLE_MILLIS = 1;
+    
+    public static void main(String[] args) throws Exception {
+        System.out.println("In ExampleVanillaMainCpuHungry.main");
+        long startTime = System.currentTimeMillis();
+        long count = 0;
+        double total = 0;
+        do {
+            for (int i = 0; i < CALCULATIONS_PER_CYCLE; i++) {
+                total += Math.sqrt(Math.random());
+                count++;
+            }
+            Thread.sleep(SLEEP_PER_CYCLE_MILLIS);
+        } while ((System.currentTimeMillis() - startTime) < MAX_TIME_MILLIS);
+        
+        System.out.println("Did "+count+" random square roots, took "+(System.currentTimeMillis()-startTime)+"ms; total = "+total);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java
new file mode 100644
index 0000000..a9da253
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaOptsTest.java
@@ -0,0 +1,361 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+import static org.testng.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.entity.java.UsesJmx;
+import org.apache.brooklyn.entity.java.VanillaJavaApp;
+import org.apache.brooklyn.entity.java.VanillaJavaAppImpl;
+import org.apache.brooklyn.entity.java.VanillaJavaAppSshDriver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool;
+import org.apache.brooklyn.util.core.internal.ssh.SshTool;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.ExecCmd;
+import org.apache.brooklyn.util.jmx.jmxmp.JmxmpAgent;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.MapDifference.ValueDifference;
+import com.google.common.collect.Maps;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class JavaOptsTest extends BrooklynAppUnitTestSupport {
+
+    // TODO Test setting classpath; but this works by customize() copying all the artifacts into /lib/*
+    // so that we can simply set this on the classpath...
+    
+    private static final Logger log = LoggerFactory.getLogger(JavaOptsTest.class);
+    
+    private SshMachineLocation loc;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        RecordingSshTool.execScriptCmds.clear();
+        super.setUp();
+        loc = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                .configure("address", "localhost")
+                .configure(SshTool.PROP_TOOL_CLASS, RecordingSshTool.class.getName()));
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        RecordingSshTool.execScriptCmds.clear();
+    }
+    
+    @Test
+    public void testSimpleLaunchesJavaProcess() {
+        VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+            .configure("main", "my.Main").configure("useJmx", false));
+        app.start(ImmutableList.of(loc));
+        
+        String runDir = javaProcess.getRunDir();
+        Map<String,String> expectedEnvs = ImmutableMap.<String,String>of();
+        List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main  >> %1$s/console 2>&1 </dev/null &", runDir));
+        
+        assertHasExpectedCmds(expectedCmds, expectedEnvs);
+    }
+    
+    @Test
+    public void testPassesJavaArgs() {
+        VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+            .configure("main", "my.Main").configure("useJmx", false).configure("args", ImmutableList.of("a1", "a2")));
+        app.start(ImmutableList.of(loc));
+        
+        String runDir = javaProcess.getRunDir();
+        Map<String,String> expectedEnvs = ImmutableMap.<String,String>of();
+        List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main \"a1\" \"a2\" >> %1$s/console 2>&1 </dev/null &", runDir));
+        
+        assertHasExpectedCmds(expectedCmds, expectedEnvs);
+    }
+    
+    @Test
+    public void testPassesJavaOpts() {
+        VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+            .configure("main", "my.Main").configure("useJmx", false).configure("javaOpts", ImmutableList.of("-abc")));
+        app.start(ImmutableList.of(loc));
+        
+        String runDir = javaProcess.getRunDir();
+        String defaultJavaOpts = "-Xms128m -Xmx512m -XX:MaxPermSize=512m";
+        String expectedJavaOpts = defaultJavaOpts+" -abc";
+        Map<String,String> expectedEnvs = ImmutableMap.<String,String>of("JAVA_OPTS", expectedJavaOpts);
+        List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main  >> %1$s/console 2>&1 </dev/null &", runDir));
+        
+        assertHasExpectedCmds(expectedCmds, expectedEnvs);
+    }
+
+    @Test
+    public void testPassesJavaSysProps() {
+        VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+            .configure("main", "my.Main").configure("useJmx", false).configure("javaSysProps", ImmutableMap.of("mykey", "myval")));
+
+        app.start(ImmutableList.of(loc));
+        
+        String runDir = javaProcess.getRunDir();
+        String defaultJavaOpts = "-Xms128m -Xmx512m -XX:MaxPermSize=512m";
+        String expectedJavaOpts = defaultJavaOpts+" -Dmykey=myval";
+        Map<String,String> expectedEnvs = ImmutableMap.<String,String>of("JAVA_OPTS", expectedJavaOpts);
+        List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main  >> %1$s/console 2>&1 </dev/null &", runDir));
+        
+        assertHasExpectedCmds(expectedCmds, expectedEnvs);
+    }
+    
+    @Test
+    public void testPassesJavaOptsOverridingDefaults() {
+        VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class)
+            .configure("main", "my.Main").configure("useJmx", false).configure("javaOpts", ImmutableList.of("-Xmx567m", "-XX:MaxPermSize=567m")));
+        app.start(ImmutableList.of(loc));
+        
+        String runDir = javaProcess.getRunDir();
+        Object expectedJavaOpts = MutableSet.of("-Xms128m", "-Xmx567m", "-XX:MaxPermSize=567m");
+        Map<String,Object> expectedEnvs = ImmutableMap.<String,Object>of("JAVA_OPTS", expectedJavaOpts);
+        List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main  >> %1$s/console 2>&1 </dev/null &", runDir));
+
+        assertHasExpectedCmds(expectedCmds, expectedEnvs);
+    }
+    
+    public static class TestingJavaOptsVanillaJavaAppImpl extends VanillaJavaAppImpl {
+        @Override public VanillaJavaAppSshDriver newDriver(MachineLocation loc) {
+            return new VanillaJavaAppSshDriver(this, (SshMachineLocation)loc) {
+                @Override protected List<String> getCustomJavaConfigOptions() {
+                    return MutableList.<String>builder()
+                        .addAll(super.getCustomJavaConfigOptions())
+                        .add("-server")
+                        .build();
+                };
+            };
+        }
+    }
+    
+    @Test
+    public void testPassesJavaOptsObeyingMutualExclusions() {
+        VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class, TestingJavaOptsVanillaJavaAppImpl.class)
+            .configure("main", "my.Main").configure("useJmx", false).configure("javaOpts", ImmutableList.of("-client")));
+        app.start(ImmutableList.of(loc));
+        
+        String runDir = javaProcess.getRunDir();
+        String defaultJavaOpts = "-Xms128m -Xmx512m -XX:MaxPermSize=512m";
+        String expectedJavaOpts = defaultJavaOpts+" -client";
+        Map<String,String> expectedEnvs = ImmutableMap.<String,String>of("JAVA_OPTS", expectedJavaOpts);
+        List<String> expectedCmds = ImmutableList.of(String.format("java $JAVA_OPTS -cp \"%1$s/lib\" my.Main  >> %1$s/console 2>&1 </dev/null &", runDir));
+        
+        assertHasExpectedCmds(expectedCmds, expectedEnvs);
+    }
+    
+    /**
+     * Asserts that one of the scripts executed had these commands, and these environment variables.
+     * There could be other commands in between though.
+     */
+    private void assertHasExpectedCmds(List<String> expectedCmds, Map<String,?> expectedEnvs) {
+        if (RecordingSshTool.execScriptCmds.isEmpty())
+            fail("No commands recorded");
+        
+        for (ExecCmd cmd : RecordingSshTool.execScriptCmds) {
+            // TODO Check expectedCmds in-order
+            
+            // check if expectedEnv is a set, then the string value contains all elements in the set
+            Map<Object, ValueDifference<Object>> difference = Maps.<Object,Object>difference(cmd.env, expectedEnvs).entriesDiffering();
+            boolean same = difference.isEmpty();
+            if (!same) {
+                Set<Object> differingKeys = new LinkedHashSet<Object>(difference.keySet());
+                Iterator<Object> ki = differingKeys.iterator();
+                while (ki.hasNext()) {
+                    Object key = ki.next();
+                    Object expectationHere = expectedEnvs.get(key);
+                    Object valueHere = cmd.env.get(key);
+                    if (valueHere==null) break;
+                    
+                    if (expectationHere instanceof Set) {
+                        Set mutableExpectationHere = new LinkedHashSet(((Set)expectationHere));
+                        Iterator si = ((Set)mutableExpectationHere).iterator();
+                        while (si.hasNext()) {
+                            Object oneExpectationHere = si.next();
+                            if (valueHere.toString().contains(Strings.toString(oneExpectationHere)))
+                                si.remove();
+                            else break;
+                        }
+                        if (mutableExpectationHere.isEmpty())
+                            differingKeys.remove(key);
+                        else
+                            // not the same
+                            break;
+                    } else {
+                        // not the same
+                        break;
+                    }
+                }
+                if (differingKeys.isEmpty())
+                    same = true;
+            }
+            
+            if (cmd.commands.containsAll(expectedCmds) && same) {
+                return;
+            }
+        }
+        
+        for (ExecCmd cmd : RecordingSshTool.execScriptCmds) {
+            log.info("Command:");
+            log.info("\tEnv:");
+            for (Map.Entry<?,?> entry : cmd.env.entrySet()) {
+                log.info("\t\t"+entry.getKey()+" = "+entry.getValue());
+            }
+            log.info("\tCmds:");
+            for (String c : cmd.commands) {
+                log.info("\t\t"+c);
+            }
+        }
+        
+        fail("Cmd not present: expected="+expectedCmds+"/"+expectedEnvs+"; actual="+RecordingSshTool.execScriptCmds);
+    }
+
+    public static class TestingNoSensorsVanillaJavaAppImpl extends VanillaJavaAppImpl {
+        protected void connectSensors() {
+            /* nothing here */
+            setAttribute(SERVICE_UP, true);
+        }
+    }
+    
+    private void assertJmxWithPropsHasPhrases(Map props,
+            List<String> expectedPhrases,
+            List<String> forbiddenPhrases) {
+        if (!props.containsKey("main")) props.put("main", "my.Main");
+        @SuppressWarnings({ "unused" })
+        VanillaJavaApp javaProcess = app.createAndManageChild(EntitySpec.create(VanillaJavaApp.class, TestingNoSensorsVanillaJavaAppImpl.class)
+            .configure(props));
+        app.start(ImmutableList.of(loc));
+        
+        List<String> phrases = new ArrayList<String>(expectedPhrases);
+        Set<String> forbiddenPhrasesFound = new LinkedHashSet<String>();
+        for (ExecCmd cmd : RecordingSshTool.execScriptCmds) {
+            String biggun = ""+cmd.env+" "+cmd.commands;
+            Iterator<String> pi = phrases.iterator();
+            while (pi.hasNext()) {
+                String phrase = pi.next();
+                if (biggun.contains(phrase)) pi.remove();
+            }
+            if (forbiddenPhrases!=null)
+                for (String p: forbiddenPhrases)
+                    if (biggun.contains(p)) forbiddenPhrasesFound.add(p);
+        }
+        
+        if (!phrases.isEmpty()) {
+            log.warn("Missing phrases in commands: "+phrases+"\nCOMMANDS: "+RecordingSshTool.execScriptCmds);
+            fail("Missing phrases in commands: "+phrases);
+        }
+        if (!forbiddenPhrasesFound.isEmpty()) {
+            log.warn("Forbidden phrases found in commands: "+forbiddenPhrasesFound+"\nCOMMANDS: "+RecordingSshTool.execScriptCmds);
+            fail("Forbidden phrases found in commands: "+forbiddenPhrasesFound);
+        }
+    }
+    
+    private static final List<String> EXPECTED_BASIC_JMX_OPTS = Arrays.asList(
+            "-Dcom.sun.management.jmxremote",
+            "-Dcom.sun.management.jmxremote.ssl=false",
+            "-Dcom.sun.management.jmxremote.authenticate=false"
+        );
+
+    private static final List<String> FORBIDDEN_BASIC_JMX_OPTS = Arrays.asList(
+            "-Dcom.sun.management.jmxremote.ssl=true",
+            
+            // often breaks things, as this is an advertised hostname usually;
+            // it typically listens on all interfaces anyway
+            "-Djava.rmi.server.hostname=0.0.0.0"
+        );
+
+    @Test
+    public void testBasicJmxFromFlag() {
+        assertJmxWithPropsHasPhrases(
+                MutableMap.builder().
+                put("useJmx", true).
+                build(), 
+            EXPECTED_BASIC_JMX_OPTS,
+            FORBIDDEN_BASIC_JMX_OPTS);
+    }
+
+    @Test
+    public void testBasicJmxFromConfig() {
+        assertJmxWithPropsHasPhrases(
+                MutableMap.builder().
+                put(UsesJmx.USE_JMX, true).
+                build(), 
+            EXPECTED_BASIC_JMX_OPTS,
+            FORBIDDEN_BASIC_JMX_OPTS);
+    }
+
+    @Test
+    public void testBasicJmxConfigFromDefault() {
+        assertJmxWithPropsHasPhrases(
+                MutableMap.builder().
+                build(), 
+            EXPECTED_BASIC_JMX_OPTS,
+            FORBIDDEN_BASIC_JMX_OPTS);
+    }
+    
+    @Test
+    public void testSecureJmxConfigFromDefault() {
+        final List<String> EXPECTED_SECURE_JMX_OPTS = Arrays.asList(
+                "-Dcom.sun.management.jmxremote",
+                "-Dbrooklyn.jmxmp.port=31009",
+                "-Dcom.sun.management.jmxremote.ssl=true",
+                "-D"+JmxmpAgent.AUTHENTICATE_CLIENTS_PROPERTY+"=true",
+                "keyStore", "/jmx-keystore",
+                "trustStore", "/jmx-truststore",
+                "-javaagent", "brooklyn-jmxmp-agent"
+            );
+
+        final List<String> FORBIDDEN_SECURE_JMX_OPTS = Arrays.asList(
+                "-Dcom.sun.management.jmxremote.authenticate=true",
+                "-Dcom.sun.management.jmxremote.ssl=false",
+                // hostname isn't forbidden -- but it is generally not used now
+                "-Djava.rmi.server.hostname="
+            );
+        
+        assertJmxWithPropsHasPhrases(
+                MutableMap.builder()
+                        .put(UsesJmx.JMX_SSL_ENABLED, true)
+                        .put(UsesJmx.JMX_PORT, 31009)
+                        .build(), 
+                EXPECTED_SECURE_JMX_OPTS,
+                FORBIDDEN_SECURE_JMX_OPTS);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.java
new file mode 100644
index 0000000..68cdc61
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/JavaSoftwareProcessSshDriverIntegrationTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.internal.EntityLocal;
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.core.BrooklynConfigKeys;
+import org.apache.brooklyn.entity.java.JavaSoftwareProcessSshDriver;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.software.base.lifecycle.MyEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+public class JavaSoftwareProcessSshDriverIntegrationTest extends BrooklynAppLiveTestSupport {
+
+    private static final long TIMEOUT_MS = 10 * 1000;
+
+    private static final Logger LOG = LoggerFactory.getLogger(JavaSoftwareProcessSshDriverIntegrationTest.class);
+
+    private MachineProvisioningLocation<?> localhost;
+
+    private static class ConcreteJavaSoftwareProcessSshDriver extends JavaSoftwareProcessSshDriver {
+        public ConcreteJavaSoftwareProcessSshDriver(EntityLocal entity, SshMachineLocation machine) {
+            super(entity, machine);
+        }
+        @Override protected String getLogFileLocation() { return null; }
+        @Override public boolean isRunning() { return false; }
+        @Override public void stop() {}
+        @Override public void install() {}
+        @Override public void customize() {}
+        @Override public void launch() {}
+    }
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        localhost = app.newLocalhostProvisioningLocation();
+    }
+
+    @Test(groups = "Integration")
+    public void testJavaStartStopSshDriverStartsAndStopsApp() throws Exception {
+        final MyEntity entity = app.createAndManageChild(EntitySpec.create(MyEntity.class));
+        app.start(ImmutableList.of(localhost));
+        Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                assertTrue(entity.getAttribute(SoftwareProcess.SERVICE_UP));
+            }});
+
+        entity.stop();
+        assertFalse(entity.getAttribute(SoftwareProcess.SERVICE_UP));
+    }
+
+    @Test(groups = "Integration")
+    public void testGetJavaVersion() throws Exception {
+        SshMachineLocation sshLocation = app.getManagementContext().getLocationManager().createLocation(
+                LocationSpec.create(SshMachineLocation.class).configure("address", "localhost"));
+        JavaSoftwareProcessSshDriver driver = new ConcreteJavaSoftwareProcessSshDriver(app, sshLocation);
+        Optional<String> version = driver.getInstalledJavaVersion();
+        assertNotNull(version);
+        assertTrue(version.isPresent());
+        LOG.info("{}.testGetJavaVersion found: {} on localhost", getClass(), version.get());
+    }
+
+    @Test(groups = "Integration")
+    public void testStartsInMgmtSpecifiedDirectory() throws Exception {
+        String dir = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4));
+        tearDown();
+        mgmt = new LocalManagementContextForTests();
+        mgmt.getBrooklynProperties().put(BrooklynConfigKeys.ONBOX_BASE_DIR, dir);
+        setUp();
+
+        doTestSpecifiedDirectory(dir, dir);
+        Os.deleteRecursively(dir);
+    }
+
+    @Test(groups = "Integration")
+    public void testStartsInAppSpecifiedDirectoryUnderHome() throws Exception {
+        String dir = Os.mergePathsUnix("~/.brooklyn-test-"+Strings.makeRandomId(4));
+        try {
+            app.config().set(BrooklynConfigKeys.ONBOX_BASE_DIR, dir);
+            doTestSpecifiedDirectory(dir, dir);
+        } finally {
+            Os.deleteRecursively(dir);
+        }
+    }
+
+    @Test(groups = "Integration")
+    public void testStartsInDifferentRunAndInstallSpecifiedDirectories() throws Exception {
+        String dir1 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4));
+        String dir2 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4));
+        app.config().set(BrooklynConfigKeys.INSTALL_DIR, dir1);
+        app.config().set(BrooklynConfigKeys.RUN_DIR, dir2);
+        doTestSpecifiedDirectory(dir1, dir2);
+        Os.deleteRecursively(dir1);
+        Os.deleteRecursively(dir2);
+    }
+
+    @Test(groups = "Integration")
+    public void testStartsInLegacySpecifiedDirectory() throws Exception {
+        String dir1 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4));
+        String dir2 = Os.mergePathsUnix(Os.tmp(), "/brooklyn-test-"+Strings.makeRandomId(4));
+        tearDown();
+        mgmt = new LocalManagementContextForTests();
+        mgmt.getBrooklynProperties().put("brooklyn.dirs.install", dir1);
+        mgmt.getBrooklynProperties().put("brooklyn.dirs.run", dir2);
+        setUp();
+
+        app.config().set(BrooklynConfigKeys.RUN_DIR, dir2);
+        doTestSpecifiedDirectory(dir1, dir2);
+        Os.deleteRecursively(dir1);
+        Os.deleteRecursively(dir2);
+    }
+
+    protected void doTestSpecifiedDirectory(final String installDirPrefix, final String runDirPrefix) throws Exception {
+        final MyEntity entity = app.createAndManageChild(EntitySpec.create(MyEntity.class));
+        app.start(ImmutableList.of(localhost));
+        Asserts.succeedsEventually(MutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                assertTrue(entity.getAttribute(SoftwareProcess.SERVICE_UP));
+
+                String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR);
+                Assert.assertNotNull(installDir);
+
+                String runDir = entity.getAttribute(SoftwareProcess.RUN_DIR);
+                Assert.assertNotNull(runDir);
+            }});
+
+        String installDir = entity.getAttribute(SoftwareProcess.INSTALL_DIR);
+        String runDir = entity.getAttribute(SoftwareProcess.RUN_DIR);
+        LOG.info("dirs for " + app + " are: install=" + installDir + ", run=" + runDir);
+        assertTrue(installDir.startsWith(Os.tidyPath(installDirPrefix)), "INSTALL_DIR is "+installDir+", does not start with expected prefix "+installDirPrefix);
+        assertTrue(runDir.startsWith(Os.tidyPath(runDirPrefix)), "RUN_DIR is "+runDir+", does not start with expected prefix "+runDirPrefix);
+
+        entity.stop();
+        assertFalse(entity.getAttribute(SoftwareProcess.SERVICE_UP));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java
new file mode 100644
index 0000000..39d6089
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/JmxSupportTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.java.JmxSupport;
+import org.apache.brooklyn.entity.java.UsesJmx.JmxAgentModes;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.maven.MavenRetriever;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+@Test
+public class JmxSupportTest {
+
+    private static final Logger log = LoggerFactory.getLogger(JmxSupportTest.class);
+    
+    private TestApplication app;
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() {
+        if (app!=null) Entities.destroyAll(app.getManagementContext());
+    }
+    
+    // defaults to JMXMP for most locations (or, in this case, if it does not yet know the location)
+    public void testJmxAutodetect() {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        JmxSupport support = new JmxSupport(app, null);
+        
+        Assert.assertEquals(support.getJmxAgentMode(), JmxAgentModes.JMXMP_AND_RMI);
+    }
+
+    public void testJmxmpJarExistence() {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMXMP);
+        JmxSupport support = new JmxSupport(app, null);
+        
+        Assert.assertEquals(support.getJmxAgentJarMavenArtifact().getArtifactId(),
+                "brooklyn-jmxmp-agent");
+        
+        Assert.assertTrue(ResourceUtils.create(this).doesUrlExist(support.getJmxAgentJarUrl()), support.getJmxAgentJarUrl());
+        Assert.assertTrue(support.getJmxAgentJarUrl().contains("-shaded-"), support.getJmxAgentJarUrl());
+    }
+    
+    public void testJmxrmiJarExistence() {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        JmxSupport support = new JmxSupport(app, null);
+        app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+        
+        Assert.assertEquals(support.getJmxAgentJarMavenArtifact().getArtifactId(),
+                "brooklyn-jmxrmi-agent");
+        
+        Assert.assertTrue(ResourceUtils.create(this).doesUrlExist(support.getJmxAgentJarUrl()), support.getJmxAgentJarUrl());
+    }
+
+    @Test
+    public void testCoerceStringtoJmxAgentModes() {
+        // Test coercions
+        assertEquals(TypeCoercions.coerce("AUTODETECT", JmxAgentModes.class), JmxAgentModes.AUTODETECT);
+        assertEquals(TypeCoercions.coerce("JMXMP_AND_RMI", JmxAgentModes.class), JmxAgentModes.JMXMP_AND_RMI);
+        assertEquals(TypeCoercions.coerce("JMX_RMI_CUSTOM_AGENT", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+
+        // Test different case format options
+        assertEquals(TypeCoercions.coerce("jmxRmiCustomAgent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+        assertEquals(TypeCoercions.coerce("jmx_rmi_custom_agent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+        assertEquals(TypeCoercions.coerce("jmx-rmi-custom-agent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+        assertEquals(TypeCoercions.coerce("JmxRmiCustomAgent", JmxAgentModes.class), JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+    }
+
+
+    @Test(groups="Integration")
+    public void testJmxmpJarHostedValidity() {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMXMP);
+        JmxSupport support = new JmxSupport(app, null);
+
+        // make sure we get a valid jar, big enough (no redirect, and classifier correclty set for this!)
+        // (we don't want the unshaded jar, that would be no good!)
+        checkValidArchive(MavenRetriever.hostedUrl(support.getJmxAgentJarMavenArtifact()), 100*1000);
+    }
+    
+    @Test(groups="Integration")
+    public void testJmxrmiJarHostedValidity() {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        JmxSupport support = new JmxSupport(app, null);
+        app.setConfig(JmxSupport.JMX_AGENT_MODE, JmxAgentModes.JMX_RMI_CUSTOM_AGENT);
+        
+        // make sure we get a valid jar, big enough (no redirect)
+        checkValidArchive(MavenRetriever.hostedUrl(support.getJmxAgentJarMavenArtifact()), 4000);
+    }
+
+    private void checkValidArchive(String url, long minSize) {
+        byte[] bytes;
+        try {
+            bytes = Streams.readFully(ResourceUtils.create(this).getResourceFromUrl(url));
+            log.info("read "+bytes.length+" bytes from "+url+" for "+JavaClassNames.callerNiceClassAndMethod(1));
+        } catch (Exception e) {
+            log.warn("Unable to read URL "+url+" for " +JavaClassNames.callerNiceClassAndMethod(1)+
+                    "; this test may require hosted (sonatype/mavencentral) repo to be populated");
+            Assert.fail("Unable to read URL "+url+"; this test may require hosted (sonatype/mavencentral) repo to be populated");
+            throw Exceptions.propagate(e);
+        }
+        // confirm this follow redirects!
+        Assert.assertTrue(bytes.length > minSize, "download of "+url+" is suspect ("+Strings.makeSizeString(bytes.length)+")");
+        // (could also check it is a zip etc)
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java
new file mode 100644
index 0000000..24ce2c9
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/SslKeyConfigTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
+
+import org.apache.brooklyn.util.core.crypto.FluentKeySigner;
+import org.apache.brooklyn.util.core.crypto.SecureKeys;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class SslKeyConfigTest {
+
+    @Test
+    public void testWriteKeyAndCertThenReadThem() throws Exception {
+        FluentKeySigner signer = new FluentKeySigner("brooklyn-test").selfsign();
+        
+        KeyStore ks = SecureKeys.newKeyStore();
+        ks.setKeyEntry("key1", 
+                signer.getKey().getPrivate(), "s3cr3t".toCharArray(), new Certificate[] { signer.getAuthorityCertificate() });
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        ks.store(bytes, "5t0r3".toCharArray());
+        
+        KeyStore ks2 = SecureKeys.newKeyStore(new ByteArrayInputStream(bytes.toByteArray()), "5t0r3");
+        String firstAlias = ks2.aliases().nextElement();
+        Assert.assertEquals(firstAlias, "key1");
+        Key k = ks2.getKey(firstAlias, "s3cr3t".toCharArray());
+        Assert.assertEquals(k, signer.getKey().getPrivate());
+        Certificate[] cc = ks2.getCertificateChain(firstAlias);
+        Assert.assertEquals(cc, new Certificate[] { signer.getAuthorityCertificate() });
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.java
new file mode 100644
index 0000000..d3a97d3
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/java/VanillaJavaAppRebindTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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 org.apache.brooklyn.entity.java;
+
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestUtils;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.java.VanillaJavaApp;
+import org.apache.brooklyn.entity.java.VanillaJavaAppImpl;
+import org.apache.brooklyn.entity.java.JavaOptsTest.TestingJavaOptsVanillaJavaAppImpl;
+import org.apache.brooklyn.policy.enricher.RollingTimeWindowMeanEnricher;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+
+public class VanillaJavaAppRebindTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(VanillaJavaAppRebindTest.class);
+    
+    private static String BROOKLYN_THIS_CLASSPATH = null;
+    private static Class<?> MAIN_CLASS = ExampleVanillaMain.class;
+
+    private ClassLoader classLoader = getClass().getClassLoader();
+    private LocalManagementContext managementContext;
+    private File mementoDir;
+    private TestApplication app;
+    private LocalhostMachineProvisioningLocation loc;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        mementoDir = Files.createTempDir();
+        managementContext = RebindTestUtils.newPersistingManagementContext(mementoDir, classLoader);
+        
+        if (BROOKLYN_THIS_CLASSPATH==null) {
+            BROOKLYN_THIS_CLASSPATH = ResourceUtils.create(MAIN_CLASS).getClassLoaderDir();
+        }
+        app = TestApplication.Factory.newManagedInstanceForTests(managementContext);
+        loc = app.newLocalhostProvisioningLocation(MutableMap.of("address", "localhost"));
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        if (mementoDir != null) RebindTestUtils.deleteMementoDir(mementoDir);
+    }
+    
+    private void rebind() throws Exception {
+        RebindTestUtils.waitForPersisted(app);
+        managementContext.terminate();
+        
+        app = (TestApplication) RebindTestUtils.rebind(mementoDir, getClass().getClassLoader());
+        managementContext = (LocalManagementContext) app.getManagementContext();
+        loc = (LocalhostMachineProvisioningLocation) Iterables.get(app.getLocations(), 0, null);
+    }
+    
+    @Test(groups="Integration")
+    public void testRebindToJavaApp() throws Exception {
+        VanillaJavaApp javaProcess = app.addChild(EntitySpec.create(VanillaJavaApp.class, TestingJavaOptsVanillaJavaAppImpl.class)
+            .configure("main", MAIN_CLASS.getCanonicalName()).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH)));
+
+        Entities.manage(javaProcess);
+        app.start(ImmutableList.of(loc));
+
+        rebind();
+        VanillaJavaApp javaProcess2 = (VanillaJavaApp) Iterables.find(app.getChildren(), Predicates.instanceOf(VanillaJavaApp.class));
+        
+        EntityTestUtils.assertAttributeEqualsEventually(javaProcess2, VanillaJavaApp.SERVICE_UP, true);
+    }
+
+    @Test(groups="Integration")
+    public void testRebindToKilledJavaApp() throws Exception {
+        VanillaJavaApp javaProcess = app.addChild(EntitySpec.create(VanillaJavaApp.class, TestingJavaOptsVanillaJavaAppImpl.class)
+            .configure("main", MAIN_CLASS.getCanonicalName()).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH)));
+        Entities.manage(javaProcess);
+        app.start(ImmutableList.of(loc));
+        javaProcess.kill();
+        
+        long starttime = System.currentTimeMillis();
+        rebind();
+        long rebindTime = System.currentTimeMillis() - starttime;
+        
+        VanillaJavaApp javaProcess2 = (VanillaJavaApp) Iterables.find(app.getChildren(), Predicates.instanceOf(VanillaJavaApp.class));
+        EntityTestUtils.assertAttributeEqualsEventually(javaProcess2, VanillaJavaApp.SERVICE_UP, false);
+        
+        // check that it was quick (previously it hung)
+        assertTrue(rebindTime < 30*1000, "rebindTime="+rebindTime);
+    }
+    
+    
+    @Test(groups="Integration")
+    public void testEnrichersOnRebindJavaApp() throws Exception {
+        VanillaJavaApp javaProcess = app.addChild(EntitySpec.create(VanillaJavaApp.class, EnrichedVanillaJavaAppImpl.class)
+            .configure("main", MAIN_CLASS.getCanonicalName()).configure("classpath", ImmutableList.of(BROOKLYN_THIS_CLASSPATH)));
+
+        Entities.manage(javaProcess);
+        app.start(ImmutableList.of(loc));
+
+        EntityTestUtils.assertAttributeEventuallyNonNull(javaProcess, EnrichedVanillaJavaAppImpl.AVG1);
+        EntityTestUtils.assertAttributeEventuallyNonNull(javaProcess, EnrichedVanillaJavaAppImpl.AVG2);
+        LOG.info("Got avg "+javaProcess.getAttribute(EnrichedVanillaJavaAppImpl.AVG1));
+
+        rebind();
+        VanillaJavaApp javaProcess2 = (VanillaJavaApp) Iterables.find(app.getChildren(), Predicates.instanceOf(VanillaJavaApp.class));
+
+        // check sensors working
+        EntityTestUtils.assertAttributeChangesEventually(javaProcess2, EnrichedVanillaJavaAppImpl.PROCESS_CPU_TIME); 
+        LOG.info("Avg now "+javaProcess2.getAttribute(EnrichedVanillaJavaAppImpl.AVG1));
+        
+        // check enrichers are functioning
+        EntityTestUtils.assertAttributeChangesEventually(javaProcess2, EnrichedVanillaJavaAppImpl.AVG1); 
+        EntityTestUtils.assertAttributeChangesEventually(javaProcess2, EnrichedVanillaJavaAppImpl.AVG2);
+        LOG.info("Avg now "+javaProcess2.getAttribute(EnrichedVanillaJavaAppImpl.AVG1));
+        
+        // and check we don't have too many
+        Assert.assertEquals(javaProcess2.getEnrichers().size(), javaProcess.getEnrichers().size());
+    }
+
+    public static class EnrichedVanillaJavaAppImpl extends VanillaJavaAppImpl {
+        private static final AttributeSensor<Double> AVG1 = Sensors.newDoubleSensor("avg1");
+        private static final AttributeSensor<Double> AVG2 = Sensors.newDoubleSensor("avg2");
+        
+        @Override
+        public void onManagementStarted() {
+            super.onManagementStarted();
+            LOG.info("mgmt started for "+this);
+            addEnricher(new RollingTimeWindowMeanEnricher<Double>(this, PROCESS_CPU_TIME, AVG1, Duration.TEN_SECONDS));
+        }
+        @Override
+        protected void connectSensors() {
+            super.connectSensors();
+            LOG.info("connecting sensors for "+this);
+            addEnricher(new RollingTimeWindowMeanEnricher<Double>(this, PROCESS_CPU_TIME, AVG2, Duration.TEN_SECONDS));
+        }
+    }
+
+}


Mime
View raw message