You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/08/19 13:09:23 UTC

[05/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

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxFeedTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxFeedTest.java b/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxFeedTest.java
new file mode 100644
index 0000000..124e00a
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxFeedTest.java
@@ -0,0 +1,422 @@
+/*
+ * 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.sensor.feed.jmx;
+
+import static org.apache.brooklyn.test.TestUtils.executeUntilSucceeds;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.StandardEmitterMBean;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.api.sensor.SensorEvent;
+import org.apache.brooklyn.api.sensor.SensorEventListener;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.core.test.entity.TestApplicationImpl;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.core.test.entity.TestEntityImpl;
+import org.apache.brooklyn.entity.core.AbstractEntity;
+import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.java.JmxSupport;
+import org.apache.brooklyn.entity.java.UsesJmx;
+import org.apache.brooklyn.entity.java.UsesJmx.JmxAgentModes;
+import org.apache.brooklyn.entity.software.base.test.jmx.GeneralisedDynamicMBean;
+import org.apache.brooklyn.entity.software.base.test.jmx.JmxService;
+import org.apache.brooklyn.sensor.core.BasicAttributeSensor;
+import org.apache.brooklyn.sensor.core.BasicNotificationSensor;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.sensor.feed.ConfigToAttributes;
+import org.apache.brooklyn.sensor.feed.jmx.JmxAttributePollConfig;
+import org.apache.brooklyn.sensor.feed.jmx.JmxFeed;
+import org.apache.brooklyn.sensor.feed.jmx.JmxHelper;
+import org.apache.brooklyn.sensor.feed.jmx.JmxNotificationFilters;
+import org.apache.brooklyn.sensor.feed.jmx.JmxNotificationSubscriptionConfig;
+import org.apache.brooklyn.sensor.feed.jmx.JmxOperationPollConfig;
+import org.apache.brooklyn.sensor.feed.jmx.JmxValueFunctions;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.test.TestUtils;
+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.testng.collections.Lists;
+import org.apache.brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.PortRanges;
+import org.apache.brooklyn.location.basic.SimulatedLocation;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Test the operation of the {@link JmxFeed} class.
+ * <p>
+ * Also confirm some of the JMX setup done by {@link JmxSupport} and {@link JmxHelper},
+ * based on ports in {@link UsesJmx}.
+ * <p>
+ * TODO tests of other JMX_AGENT_MODE are done in ActiveMqIntegrationTest; 
+ * would be nice to promote some to live here
+ */
+public class JmxFeedTest {
+    
+    // FIXME Move out the JmxHelper tests into the JmxHelperTest class
+    
+    // FIXME Also test that setting poll period takes effect
+    
+    private static final Logger log = LoggerFactory.getLogger(JmxFeedTest.class);
+
+    private static final int TIMEOUT_MS = 5000;
+    private static final int SHORT_WAIT_MS = 250;
+    
+    private JmxService jmxService;
+    private TestApplication app;
+    private TestEntity entity;
+    private JmxFeed feed;
+    private JmxHelper jmxHelper;
+    
+    private AttributeSensor<Integer> intAttribute = Sensors.newIntegerSensor("brooklyn.test.intAttribute", "Brooklyn testing int attribute");
+    private AttributeSensor<String> stringAttribute = Sensors.newStringSensor("brooklyn.test.stringAttribute", "Brooklyn testing string attribute");
+    private BasicAttributeSensor<Map> mapAttribute = new BasicAttributeSensor<Map>(Map.class, "brooklyn.test.mapAttribute", "Brooklyn testing map attribute");
+    private String objectName = "Brooklyn:type=MyTestMBean,name=myname";
+    private ObjectName jmxObjectName;
+    private String attributeName = "myattrib";
+    private String opName = "myop";
+    
+    public static class TestEntityWithJmx extends TestEntityImpl {
+        @Override public void init() {
+            setAttribute(Attributes.HOSTNAME, "localhost");
+            setAttribute(UsesJmx.JMX_PORT, 
+                    LocalhostMachineProvisioningLocation.obtainPort(PortRanges.fromString("40123+")));
+            // only supports no-agent, at the moment
+            setConfig(UsesJmx.JMX_AGENT_MODE, JmxAgentModes.NONE);
+            setAttribute(UsesJmx.RMI_REGISTRY_PORT, -1);  // -1 means to use the JMX_PORT only
+            ConfigToAttributes.apply(this, UsesJmx.JMX_CONTEXT);
+        }
+    }
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        jmxObjectName = new ObjectName(objectName);
+        
+        // Create an entity and configure it with the above JMX service
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).impl(TestEntityWithJmx.class));
+        app.start(ImmutableList.of(new SimulatedLocation()));
+
+        jmxHelper = new JmxHelper(entity);
+        
+        jmxService = new JmxService(entity);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (feed != null) feed.stop();
+        if (jmxHelper != null) jmxHelper.disconnect();
+        if (jmxService != null) jmxService.shutdown();
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+        feed = null;
+    }
+
+    @Test
+    public void testJmxAttributePollerReturnsMBeanAttribute() throws Exception {
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(ImmutableMap.of(attributeName, 42), objectName);
+
+        feed = JmxFeed.builder()
+                .entity(entity)
+                .pollAttribute(new JmxAttributePollConfig<Integer>(intAttribute)
+                        .objectName(objectName)
+                        .period(50)
+                        .attributeName(attributeName))
+                .build();
+        
+        // Starts with value defined when registering...
+        assertSensorEventually(intAttribute, 42, TIMEOUT_MS);
+
+        // Change the value and check it updates
+        mbean.updateAttributeValue(attributeName, 64);
+        assertSensorEventually(intAttribute, 64, TIMEOUT_MS);
+    }
+
+    @Test
+    public void testJmxAttributeOfTypeTabularDataProviderConvertedToMap() throws Exception {
+        // Create the CompositeType and TabularData
+        CompositeType compositeType = new CompositeType(
+                "typeName",
+                "description",
+                new String[] {"myint", "mystring", "mybool"},    // item names
+                new String[] {"myint", "mystring", "mybool"},    // item descriptions, can't be null or empty string
+                new OpenType<?>[] {SimpleType.INTEGER, SimpleType.STRING, SimpleType.BOOLEAN}
+        );
+        TabularType tt = new TabularType(
+                "typeName",
+                "description",
+                compositeType,
+                new String[] {"myint"}
+        );
+        TabularDataSupport tds = new TabularDataSupport(tt);
+        tds.put(new CompositeDataSupport(
+                compositeType,
+                new String[] {"mybool", "myint", "mystring"},
+                new Object[] {true, 1234, "on"}
+        ));
+
+        // Create MBean
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(ImmutableMap.of(attributeName, tds), objectName);
+
+        feed = JmxFeed.builder()
+                .entity(entity)
+                .pollAttribute(new JmxAttributePollConfig<Map>(mapAttribute)
+                        .objectName(objectName)
+                        .attributeName(attributeName)
+                        .onSuccess((Function)JmxValueFunctions.tabularDataToMap()))
+                .build();
+
+        // Starts with value defined when registering...
+        assertSensorEventually(
+                mapAttribute, 
+                ImmutableMap.of("myint", 1234, "mystring", "on", "mybool", Boolean.TRUE), 
+                TIMEOUT_MS);
+    }
+
+    @Test
+    public void testJmxOperationPolledForSensor() throws Exception {
+        // This is awful syntax...
+        final int opReturnVal = 123;
+        final AtomicInteger invocationCount = new AtomicInteger();
+        MBeanOperationInfo opInfo = new MBeanOperationInfo(opName, "my descr", new MBeanParameterInfo[0], Integer.class.getName(), MBeanOperationInfo.ACTION);
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(
+                Collections.emptyMap(), 
+                ImmutableMap.of(opInfo, new Function<Object[], Integer>() {
+                        public Integer apply(Object[] args) {
+                            invocationCount.incrementAndGet(); return opReturnVal;
+                        }}),
+                objectName);
+
+        feed = JmxFeed.builder()
+                .entity(entity)
+                .pollOperation(new JmxOperationPollConfig<Integer>(intAttribute)
+                        .objectName(objectName)
+                        .operationName(opName))
+                .build();
+        
+        TestUtils.executeUntilSucceeds(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                assertTrue(invocationCount.get() > 0, "invocationCount="+invocationCount);
+                assertEquals(entity.getAttribute(intAttribute), (Integer)opReturnVal);
+            }});
+    }
+
+    @Test
+    public void testJmxOperationWithArgPolledForSensor() throws Exception {
+        // This is awful syntax...
+        MBeanParameterInfo paramInfo = new MBeanParameterInfo("param1", String.class.getName(), "my param1");
+        MBeanParameterInfo[] paramInfos = new MBeanParameterInfo[] {paramInfo};
+        MBeanOperationInfo opInfo = new MBeanOperationInfo(opName, "my descr", paramInfos, String.class.getName(), MBeanOperationInfo.ACTION);
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(
+                Collections.emptyMap(), 
+                ImmutableMap.of(opInfo, new Function<Object[], String>() {
+                        public String apply(Object[] args) {
+                            return args[0]+"suffix";
+                        }}),
+                objectName);
+        
+        feed = JmxFeed.builder()
+                .entity(entity)
+                .pollOperation(new JmxOperationPollConfig<String>(stringAttribute)
+                        .objectName(objectName)
+                        .operationName(opName)
+                        .operationParams(ImmutableList.of("myprefix")))
+                .build();
+        
+        assertSensorEventually(stringAttribute, "myprefix"+"suffix", TIMEOUT_MS);
+    }
+
+    @Test
+    public void testJmxNotificationSubscriptionForSensor() throws Exception {
+        final String one = "notification.one", two = "notification.two";
+        final StandardEmitterMBean mbean = jmxService.registerMBean(ImmutableList.of(one, two), objectName);
+        final AtomicInteger sequence = new AtomicInteger(0);
+
+        feed = JmxFeed.builder()
+                .entity(entity)
+                .subscribeToNotification(new JmxNotificationSubscriptionConfig<Integer>(intAttribute)
+                        .objectName(objectName)
+                        .notificationFilter(JmxNotificationFilters.matchesType(one)))
+                .build();        
+
+        // Notification updates the sensor
+        // Note that subscription is done async, so can't just send notification immediately during test.
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                sendNotification(mbean, one, sequence.getAndIncrement(), 123);
+                assertEquals(entity.getAttribute(intAttribute), (Integer)123);
+            }});
+        
+        // But other notification types are ignored
+        sendNotification(mbean, two, sequence.getAndIncrement(), -1);
+            
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                assertEquals(entity.getAttribute(intAttribute), (Integer)123);
+            }});
+    }
+    
+    @Test
+    public void testJmxNotificationSubscriptionForSensorParsingNotification() throws Exception {
+        final String one = "notification.one", two = "notification.two";
+        final StandardEmitterMBean mbean = jmxService.registerMBean(ImmutableList.of(one, two), objectName);
+        final AtomicInteger sequence = new AtomicInteger(0);
+        
+        feed = JmxFeed.builder()
+                .entity(entity)
+                .subscribeToNotification(new JmxNotificationSubscriptionConfig<Integer>(intAttribute)
+                        .objectName(objectName)
+                        .notificationFilter(JmxNotificationFilters.matchesType(one))
+                        .onNotification(new Function<Notification, Integer>() {
+                            public Integer apply(Notification notif) {
+                                return (Integer) notif.getUserData();
+                            }
+                        }))
+                .build();
+        
+        
+        // Notification updates the sensor
+        // Note that subscription is done async, so can't just send notification immediately during test.
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                sendNotification(mbean, one, sequence.getAndIncrement(), 123);
+                assertEquals(entity.getAttribute(intAttribute), (Integer)123);
+            }});
+    }
+
+    @Test
+    public void testJmxNotificationMultipleSubscriptionUsingListener() throws Exception {
+        final String one = "notification.one";
+        final String two = "notification.two";
+        final StandardEmitterMBean mbean = jmxService.registerMBean(ImmutableList.of(one, two), objectName);
+        final AtomicInteger sequence = new AtomicInteger(0);
+
+        feed = JmxFeed.builder()
+                .entity(entity)
+                .subscribeToNotification(new JmxNotificationSubscriptionConfig<Integer>(intAttribute)
+                        .objectName(objectName)
+                        .notificationFilter(JmxNotificationFilters.matchesTypes(one, two)))
+                .build();
+        
+        // Notification updates the sensor
+        // Note that subscription is done async, so can't just send notification immediately during test.
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                sendNotification(mbean, one, sequence.getAndIncrement(), 123);
+                assertEquals(entity.getAttribute(intAttribute), (Integer)123);
+            }});
+
+        // And wildcard means other notifications also received
+        sendNotification(mbean, two, sequence.getAndIncrement(), 456);
+        assertSensorEventually(intAttribute, 456, TIMEOUT_MS);
+    }
+
+    // Test reproduces functionality used in Monterey, for Venue entity being told of requestActor
+    @Test
+    public void testSubscribeToJmxNotificationAndEmitCorrespondingNotificationSensor() throws Exception {
+        TestApplication app2 = new TestApplicationImpl();
+        final EntityWithEmitter entity = new EntityWithEmitter(app2);
+        Entities.startManagement(app2);
+        try {
+            app2.start(ImmutableList.of(new SimulatedLocation()));
+            
+            final List<SensorEvent<String>> received = Lists.newArrayList();
+            app2.subscribe(null, EntityWithEmitter.MY_NOTIF, new SensorEventListener<String>() {
+                public void onEvent(SensorEvent<String> event) {
+                    received.add(event);
+                }});
+    
+            final StandardEmitterMBean mbean = jmxService.registerMBean(ImmutableList.of("one"), objectName);
+            final AtomicInteger sequence = new AtomicInteger(0);
+            
+            jmxHelper.connect(TIMEOUT_MS);
+            jmxHelper.addNotificationListener(jmxObjectName, new NotificationListener() {
+                    public void handleNotification(Notification notif, Object callback) {
+                        if (notif.getType().equals("one")) {
+                            entity.emit(EntityWithEmitter.MY_NOTIF, (String) notif.getUserData());
+                        }
+                    }});
+            
+
+            Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+                public void run() {
+                    sendNotification(mbean, "one", sequence.getAndIncrement(), "abc");
+                    assertTrue(received.size() > 0, "received size should be bigger than 0");
+                    assertEquals(received.get(0).getValue(), "abc");
+                }});
+        } finally {
+            Entities.destroyAll(app2.getManagementContext());
+        }
+    }
+    
+    public static class EntityWithEmitter extends AbstractEntity {
+        public static final BasicNotificationSensor<String> MY_NOTIF = new BasicNotificationSensor<String>(String.class, "test.myNotif", "My notif");
+        
+        public EntityWithEmitter(Entity owner) {
+            super(owner);
+        }
+        public EntityWithEmitter(Map flags) {
+            super(flags);
+        }
+        public EntityWithEmitter(Map flags, Entity owner) {
+            super(flags, owner);
+        }
+    }
+    
+    private Notification sendNotification(StandardEmitterMBean mbean, String type, long seq, Object userData) {
+        Notification notif = new Notification(type, mbean, seq);
+        notif.setUserData(userData);
+        mbean.sendNotification(notif);
+        return notif;
+    }
+    
+    private <T> void assertSensorEventually(final AttributeSensor<T> sensor, final T expectedVal, long timeout) {
+        executeUntilSucceeds(ImmutableMap.of("timeout", timeout), new Callable<Void>() {
+            public Void call() {
+                assertEquals(entity.getAttribute(sensor), expectedVal);
+                return null;
+            }});
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxHelperTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxHelperTest.java b/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxHelperTest.java
new file mode 100644
index 0000000..058947c
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/JmxHelperTest.java
@@ -0,0 +1,311 @@
+/*
+ * 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.sensor.feed.jmx;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.management.DynamicMBean;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.StandardEmitterMBean;
+
+import org.apache.brooklyn.entity.software.base.test.jmx.GeneralisedDynamicMBean;
+import org.apache.brooklyn.entity.software.base.test.jmx.JmxService;
+import org.apache.brooklyn.sensor.feed.jmx.JmxHelper;
+import org.apache.brooklyn.test.TestUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.jclouds.util.Throwables2;
+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.testng.collections.Lists;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class JmxHelperTest {
+
+    private static final Logger log = LoggerFactory.getLogger(JmxHelperTest.class);
+    
+    private static final String LOCALHOST_NAME = "localhost";
+    
+    private static final int TIMEOUT_MS = 5000;
+    private static final int SHORT_WAIT_MS = 250;
+
+    private JmxService jmxService;
+    private JmxHelper jmxHelper;
+    
+    private String objectName = "Brooklyn:type=MyTestMBean,name=myname";
+    private String objectNameWithWildcard = "Brooklyn:type=MyTestMBean,name=mynam*";
+    private ObjectName jmxObjectName;
+    private ObjectName jmxObjectNameWithWildcard;
+    private String attributeName = "myattrib";
+    private String opName = "myop";
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        jmxObjectName = new ObjectName(objectName);
+        jmxObjectNameWithWildcard = new ObjectName(objectNameWithWildcard);
+        jmxService = newJmxServiceRetrying(LOCALHOST_NAME, 5);
+        jmxHelper = new JmxHelper(jmxService.getUrl());
+        jmxHelper.setMinTimeBetweenReconnectAttempts(0);
+        jmxHelper.connect(TIMEOUT_MS);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (jmxHelper != null) jmxHelper.disconnect();
+        if (jmxService != null) jmxService.shutdown();
+        jmxHelper = null;
+        jmxService = null;
+    }
+
+    @Test
+    public void testGetAttribute() throws Exception {
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(MutableMap.of("myattr", "myval"), objectName);
+        assertEquals(jmxHelper.getAttribute(jmxObjectName, "myattr"), "myval");
+    }
+
+    @Test
+    public void testGetAttributeUsingObjectNameWildcard() throws Exception {
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(MutableMap.of("myattr", "myval"), objectName);
+        assertEquals(jmxHelper.getAttribute(jmxObjectNameWithWildcard, "myattr"), "myval");
+    }
+
+    @Test
+    public void testSetAttribute() throws Exception {
+        DynamicMBean mbean = jmxService.registerMBean(MutableMap.of("myattr", "myval"), objectName);
+
+        jmxHelper.setAttribute(jmxObjectName, "myattr", "abc");
+        Object actual = jmxHelper.getAttribute(jmxObjectName, "myattr");
+        assertEquals(actual, "abc");
+    }
+
+    @Test
+    public void testSetAttributeUsingObjectNameWildcard() throws Exception {
+        DynamicMBean mbean = jmxService.registerMBean(MutableMap.of("myattr", "myval"), objectName);
+
+        jmxHelper.setAttribute(jmxObjectNameWithWildcard, "myattr", "abc");
+        Object actual = jmxHelper.getAttribute(jmxObjectName, "myattr");
+        assertEquals(actual, "abc");
+    }
+
+    @Test
+    public void testInvokeOperationWithNoArgs() throws Exception {
+        final String opReturnVal = "my result";
+        MBeanOperationInfo opInfo = new MBeanOperationInfo(opName, "my descr", new MBeanParameterInfo[0], String.class.getName(), MBeanOperationInfo.ACTION);
+        Function<Object[], String> opImpl = new Function<Object[], String>() {
+            @Override public String apply(Object[] args) {
+                assertEquals(args.length, 0, "args="+args);
+                return opReturnVal;
+            }
+        };
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(ImmutableMap.of(), ImmutableMap.of(opInfo, opImpl), objectName);
+        
+        assertEquals(jmxHelper.operation(objectName, opName), opReturnVal);
+    }
+
+    @Test
+    public void testInvokeOperationUsingObjectNameWildcard() throws Exception {
+        final String opReturnVal = "my result";
+        MBeanOperationInfo opInfo = new MBeanOperationInfo(opName, "my descr", new MBeanParameterInfo[0], String.class.getName(), MBeanOperationInfo.ACTION);
+        Function<Object[], String> opImpl = new Function<Object[], String>() {
+            @Override public String apply(Object[] args) {
+                assertEquals(args.length, 0, "args="+args);
+                return opReturnVal;
+            }
+        };
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(ImmutableMap.of(), ImmutableMap.of(opInfo, opImpl), objectName);
+        
+        assertEquals(jmxHelper.operation(objectNameWithWildcard, opName), opReturnVal);
+    }
+
+    @Test
+    public void testInvokeOperationWithArgs() throws Exception {
+        final String opReturnPrefix = "my result prefix/";
+        String opParam1 = "my param 1";
+        MBeanOperationInfo opInfo = new MBeanOperationInfo(
+                opName, 
+                "my descr", 
+                new MBeanParameterInfo[] {new MBeanParameterInfo("myParam1", String.class.getName(), "my param1 descr")}, 
+                String.class.getName(), 
+                MBeanOperationInfo.ACTION);
+        Function<Object[],String> opImpl = new Function<Object[],String>() {
+            public String apply(Object[] input) {
+                return opReturnPrefix+input[0];
+            }
+        };
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(ImmutableMap.of(), ImmutableMap.of(opInfo, opImpl), objectName);
+        
+        assertEquals(jmxHelper.operation(objectName, opName, opParam1), opReturnPrefix+opParam1);
+    }
+
+    @Test
+    public void testReconnectsOnJmxServerTemporaryFailure() throws Exception {
+        int port = jmxService.getJmxPort();
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(MutableMap.of("myattr", "myval"), objectName);
+        assertEquals(jmxHelper.getAttribute(jmxObjectName, "myattr"), "myval");
+        
+        // Simulate temporary network-failure
+        jmxService.shutdown();
+
+        // Ensure that we have a failed query while the "network is down"         
+        try {
+            jmxHelper.getAttribute(jmxObjectName, attributeName);
+            fail();
+        } catch (Exception e) {
+            if (Throwables2.getFirstThrowableOfType(e, IOException.class) == null) {
+                throw e;
+            }
+        }
+
+        // Simulate the network restarting
+        jmxService = new JmxService(LOCALHOST_NAME, port);
+        
+        GeneralisedDynamicMBean mbean2 = jmxService.registerMBean(MutableMap.of("myattr", "myval2"), objectName);
+        assertEquals(jmxHelper.getAttribute(jmxObjectName, "myattr"), "myval2");
+    }
+    
+    @Test(expectedExceptions = {IllegalStateException.class})
+    public void testJmxCheckInstanceExistsEventuallyThrowsIfNotFound() throws Exception {
+        jmxHelper.assertMBeanExistsEventually(new ObjectName("Brooklyn:type=DoesNotExist,name=doesNotExist"), 1L);
+    }
+
+    @Test
+    public void testJmxObjectCheckExistsEventuallyReturnsIfFoundImmediately() throws Exception {
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(objectName);
+        jmxHelper.assertMBeanExistsEventually(jmxObjectName, 1L);
+    }
+
+    @Test
+    public void testJmxObjectCheckExistsEventuallyTakingLongReturnsIfFoundImmediately() throws Exception {
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(objectName);
+        jmxHelper.assertMBeanExistsEventually(jmxObjectName, 1L);
+    }
+
+    @Test
+    public void testJmxObjectCheckExistsEventuallyReturnsIfCreatedDuringPolling() throws Exception {
+        Thread t = new Thread(new Runnable() {
+                public void run() {
+                    try {
+                        Thread.sleep(SHORT_WAIT_MS);
+                        GeneralisedDynamicMBean mbean = jmxService.registerMBean(objectName);
+                    } catch (InterruptedException e) {
+                        return; // graceful return
+                    } catch (Exception e) {
+                        throw Exceptions.propagate(e);
+                    }
+                }});
+        try {
+            t.start();
+            
+            jmxHelper.assertMBeanExistsEventually(jmxObjectName, TIMEOUT_MS);
+        } finally {
+            t.interrupt();
+            t.join(TIMEOUT_MS);
+            assertFalse(t.isAlive());
+        }        
+    }
+
+    @Test
+    public void testSubscribeToJmxNotificationsDirectlyWithJmxHelper() throws Exception {
+        StandardEmitterMBean mbean = jmxService.registerMBean(ImmutableList.of("one"), objectName);
+        int sequence = 0;
+        final List<Notification> received = Lists.newArrayList();
+
+        jmxHelper.addNotificationListener(jmxObjectName, new NotificationListener() {
+            public void handleNotification(Notification notif, Object callback) {
+                received.add(notif);
+            }});
+                    
+
+        final Notification notif = sendNotification(mbean, "one", sequence++, "abc");
+
+        TestUtils.executeUntilSucceeds(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+            public void run() {
+                assertEquals(received.size(), 1);
+                assertNotificationsEqual(received.get(0), notif);
+            }});
+    }
+
+    // Visual-inspection test that LOG.warn happens only once; TODO setup a listener to the logging output
+    @Test
+    public void testMBeanNotFoundLoggedOnlyOncePerUrl() throws Exception {
+        ObjectName wrongObjectName = new ObjectName("DoesNotExist:type=DoesNotExist");
+
+        // Expect just one log message about:
+        //     JMX object DoesNotExist:type=DoesNotExist not found at service:jmx:rmi://localhost:1099/jndi/rmi://localhost:9001/jmxrmi"
+        for (int i = 0; i < 10; i++) {
+            jmxHelper.findMBean(wrongObjectName);
+        }
+
+        jmxService.shutdown();
+        jmxHelper.disconnect();
+        
+        jmxService = newJmxServiceRetrying(LOCALHOST_NAME, 5);
+        jmxHelper = new JmxHelper(jmxService.getUrl());
+        jmxHelper.connect();
+        
+        // Expect just one log message about:
+        //     JMX object DoesNotExist:type=DoesNotExist not found at service:jmx:rmi://localhost:1099/jndi/rmi://localhost:9001/jmxrmi"
+        for (int i = 0; i < 10; i++) {
+            jmxHelper.findMBean(wrongObjectName);
+        }
+    }
+
+    private JmxService newJmxServiceRetrying(String host, int retries) throws Exception {
+        Exception lastexception = null;
+        for (int i = 0; i < retries; i++) {
+            try {
+                return new JmxService(host, (int)(11000+(500*Math.random())));
+            } catch (Exception e) {
+                log.debug("Unable to create JMX service during test - "+retries+" retries remaining");
+                lastexception = e;
+            }
+        }
+        throw lastexception;
+    }
+
+    private Notification sendNotification(StandardEmitterMBean mbean, String type, long seq, Object userData) {
+        Notification notif = new Notification(type, mbean, seq);
+        notif.setUserData(userData);
+        mbean.sendNotification(notif);
+        return notif;
+    }
+    
+    private void assertNotificationsEqual(Notification n1, Notification n2) {
+        assertEquals(n1.getType(), n2.getType());
+        assertEquals(n1.getSequenceNumber(), n2.getSequenceNumber());
+        assertEquals(n1.getUserData(), n2.getUserData());
+        assertEquals(n1.getTimeStamp(), n2.getTimeStamp());
+        assertEquals(n1.getMessage(), n2.getMessage());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/RebindJmxFeedTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/RebindJmxFeedTest.java b/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/RebindJmxFeedTest.java
new file mode 100644
index 0000000..f4a72cd
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/sensor/feed/jmx/RebindJmxFeedTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.sensor.feed.jmx;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.api.sensor.Feed;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixtureWithApp;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.core.test.entity.TestEntityImpl;
+import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.entity.java.UsesJmx;
+import org.apache.brooklyn.entity.java.UsesJmx.JmxAgentModes;
+import org.apache.brooklyn.entity.software.base.test.jmx.GeneralisedDynamicMBean;
+import org.apache.brooklyn.entity.software.base.test.jmx.JmxService;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.sensor.feed.ConfigToAttributes;
+import org.apache.brooklyn.sensor.feed.jmx.JmxAttributePollConfig;
+import org.apache.brooklyn.sensor.feed.jmx.JmxFeed;
+import org.apache.brooklyn.sensor.feed.jmx.JmxHelper;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.collections.MutableMap;
+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.LocalhostMachineProvisioningLocation;
+import org.apache.brooklyn.location.basic.PortRanges;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class RebindJmxFeedTest extends RebindTestFixtureWithApp {
+
+    private static final Logger log = LoggerFactory.getLogger(RebindJmxFeedTest.class);
+
+    private static final String LOCALHOST_NAME = "localhost";
+
+    static final AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString", "");
+    static final AttributeSensor<Integer> SENSOR_INT = Sensors.newIntegerSensor( "aLong", "");
+
+    static final String JMX_ATTRIBUTE_NAME = "myattr";
+    static final String OBJECT_NAME = "Brooklyn:type=MyTestMBean,name=myname";
+    
+    private JmxService jmxService;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        
+        // Create an entity and configure it with the above JMX service
+        //jmxService = newJmxServiceRetrying(LOCALHOST_NAME, 5);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    @Override
+    public void tearDown() throws Exception {
+        if (jmxService != null) jmxService.shutdown();
+        super.tearDown();
+    }
+
+    @Test
+    public void testJmxFeedIsPersisted() throws Exception {
+        runJmxFeedIsPersisted(false);
+    }
+
+    @Test
+    public void testJmxFeedIsPersistedWithPreCreatedJmxHelper() throws Exception {
+        runJmxFeedIsPersisted(true);
+    }
+
+    protected void runJmxFeedIsPersisted(boolean preCreateJmxHelper) throws Exception {
+        TestEntity origEntity = origApp.createAndManageChild(EntitySpec.create(TestEntity.class).impl(MyEntityWithJmxFeedImpl.class)
+                .configure(MyEntityWithJmxFeedImpl.PRE_CREATE_JMX_HELPER, preCreateJmxHelper));
+        origApp.start(ImmutableList.<Location>of());
+        
+        jmxService = new JmxService(origEntity);
+        GeneralisedDynamicMBean mbean = jmxService.registerMBean(MutableMap.of(JMX_ATTRIBUTE_NAME, "myval"), OBJECT_NAME);
+        
+        EntityTestUtils.assertAttributeEqualsEventually(origEntity, SENSOR_STRING, "myval");
+        assertEquals(origEntity.feeds().getFeeds().size(), 1);
+
+        newApp = rebind();
+        TestEntity newEntity = (TestEntity) Iterables.getOnlyElement(newApp.getChildren());
+        
+        Collection<Feed> newFeeds = newEntity.feeds().getFeeds();
+        assertEquals(newFeeds.size(), 1);
+        
+        // Expect the feed to still be polling
+        newEntity.setAttribute(SENSOR_STRING, null);
+        EntityTestUtils.assertAttributeEqualsEventually(newEntity, SENSOR_STRING, "myval");
+    }
+
+    public static class MyEntityWithJmxFeedImpl extends TestEntityImpl {
+        public static final ConfigKey<Boolean> PRE_CREATE_JMX_HELPER = ConfigKeys.newBooleanConfigKey("test.rebindjmx.preCreateJmxHelper", "", false);
+        
+        @Override
+        public void start(Collection<? extends Location> locs) {
+            // TODO Auto-generated method stub
+            super.start(locs);
+            
+            setAttribute(Attributes.HOSTNAME, "localhost");
+            setAttribute(UsesJmx.JMX_PORT, 
+                    LocalhostMachineProvisioningLocation.obtainPort(PortRanges.fromString("40123+")));
+            // only supports no-agent, at the moment
+            setConfig(UsesJmx.JMX_AGENT_MODE, JmxAgentModes.NONE);
+            setAttribute(UsesJmx.RMI_REGISTRY_PORT, -1);  // -1 means to use the JMX_PORT only
+            ConfigToAttributes.apply(this, UsesJmx.JMX_CONTEXT);
+            
+            JmxFeed.Builder feedBuilder = JmxFeed.builder()
+                    .entity(this)
+                    .pollAttribute(new JmxAttributePollConfig<String>(SENSOR_STRING)
+                            .objectName(OBJECT_NAME)
+                            .period(50)
+                            .attributeName(JMX_ATTRIBUTE_NAME));
+            if (getConfig(PRE_CREATE_JMX_HELPER)) {
+                JmxHelper jmxHelper = new JmxHelper(this);
+                feedBuilder.helper(jmxHelper);
+            }
+            addFeed(feedBuilder.build());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/java/org/apache/brooklyn/sensor/ssh/SshEffectorTasksTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/sensor/ssh/SshEffectorTasksTest.java b/software/base/src/test/java/org/apache/brooklyn/sensor/ssh/SshEffectorTasksTest.java
new file mode 100644
index 0000000..073a8df
--- /dev/null
+++ b/software/base/src/test/java/org/apache/brooklyn/sensor/ssh/SshEffectorTasksTest.java
@@ -0,0 +1,264 @@
+/*
+ * 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.sensor.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.api.mgmt.TaskFactory;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.sensor.ssh.SshEffectorTasks;
+import org.apache.brooklyn.util.core.task.ssh.SshFetchTaskWrapper;
+import org.apache.brooklyn.util.core.task.ssh.SshPutTaskWrapper;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException;
+import org.apache.brooklyn.util.net.Urls;
+import org.apache.commons.io.FileUtils;
+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 org.apache.brooklyn.location.basic.SshMachineLocation;
+
+import com.google.common.io.Files;
+
+public class SshEffectorTasksTest {
+
+    private static final Logger log = LoggerFactory.getLogger(SshEffectorTasksTest.class);
+    
+    TestApplication app;
+    ManagementContext mgmt;
+    SshMachineLocation host;
+    File tempDir;
+    
+    boolean failureExpected;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setup() throws Exception {
+        app = TestApplication.Factory.newManagedInstanceForTests();
+        mgmt = app.getManagementContext();
+        
+        LocalhostMachineProvisioningLocation lhc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+        host = lhc.obtain();
+        app.start(Arrays.asList(host));
+        clearExpectedFailure();
+        tempDir = Files.createTempDir();
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) Entities.destroyAll(mgmt);
+        mgmt = null;
+        FileUtils.deleteDirectory(tempDir);
+        checkExpectedFailure();
+    }
+
+    protected void checkExpectedFailure() {
+        if (failureExpected) {
+            clearExpectedFailure();
+            Assert.fail("Test should have thrown an exception but it did not.");
+        }
+    }
+    
+    protected void clearExpectedFailure() {
+        failureExpected = false;
+    }
+
+    protected void setExpectingFailure() {
+        failureExpected = true;
+    }
+    
+    public <T extends TaskAdaptable<?>> T submit(final TaskFactory<T> taskFactory) {
+        return Entities.submit(app, taskFactory);
+    }
+    
+    // ------------------- basic ssh
+    
+    @Test(groups="Integration")
+    public void testSshEchoHello() {
+        ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.ssh("sleep 1 ; echo hello world"));
+        Assert.assertFalse(t.isDone());
+        Assert.assertEquals(t.get(), (Integer)0);
+        Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0);
+        Assert.assertEquals(t.getStdout().trim(), "hello world");
+    }
+
+    @Test(groups="Integration")
+    public void testSshPut() throws IOException {
+        String fn = Urls.mergePaths(tempDir.getPath(), "f1");
+        SshPutTaskWrapper t = submit(SshEffectorTasks.put(fn).contents("hello world"));
+        t.block();
+        Assert.assertEquals(FileUtils.readFileToString(new File(fn)), "hello world");
+        // and make sure this doesn't throw
+        Assert.assertTrue(t.isDone());
+        Assert.assertTrue(t.isSuccessful());
+        Assert.assertEquals(t.get(), null);
+        Assert.assertEquals(t.getExitCode(), (Integer)0);
+    }
+
+    @Test(groups="Integration")
+    public void testSshFetch() throws IOException {
+        String fn = Urls.mergePaths(tempDir.getPath(), "f2");
+        FileUtils.write(new File(fn), "hello fetched world");
+        
+        SshFetchTaskWrapper t = submit(SshEffectorTasks.fetch(fn));
+        t.block();
+        
+        Assert.assertTrue(t.isDone());
+        Assert.assertEquals(t.get(), "hello fetched world");
+    }
+
+    // ----------------- pid stuff
+    
+    @Test(groups="Integration")
+    public void testNonRunningPid() {
+        ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.codePidRunning(99999));
+        Assert.assertNotEquals(t.getTask().getUnchecked(), (Integer)0);
+        Assert.assertNotEquals(t.getExitCode(), (Integer)0);
+        ProcessTaskWrapper<Boolean> t2 = submit(SshEffectorTasks.isPidRunning(99999));
+        Assert.assertFalse(t2.getTask().getUnchecked());
+    }
+
+    @Test(groups="Integration")
+    public void testNonRunningPidRequired() {
+        ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidRunning(99999));
+        setExpectingFailure();
+        try {
+            t.getTask().getUnchecked();
+        } catch (Exception e) {
+            log.info("The error if required PID is not found is: "+e);
+            clearExpectedFailure();
+            Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e);
+        }
+        checkExpectedFailure();
+    }
+
+    public static Integer getMyPid() {
+        try {
+            java.lang.management.RuntimeMXBean runtime = 
+                    java.lang.management.ManagementFactory.getRuntimeMXBean();
+            java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
+            jvm.setAccessible(true);
+//            sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
+            Object mgmt = jvm.get(runtime);
+            java.lang.reflect.Method pid_method =  
+                    mgmt.getClass().getDeclaredMethod("getProcessId");
+            pid_method.setAccessible(true);
+
+            return (Integer) pid_method.invoke(mgmt);
+        } catch (Exception e) {
+            throw new PropagatedRuntimeException("Test depends on (fragile) getMyPid method which does not work here", e);
+        }
+    }
+
+    @Test(groups="Integration")
+    public void testRunningPid() {
+        ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.codePidRunning(getMyPid()));
+        Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0);
+        ProcessTaskWrapper<Boolean> t2 = submit(SshEffectorTasks.isPidRunning(getMyPid()));
+        Assert.assertTrue(t2.getTask().getUnchecked());
+    }
+
+    @Test(groups="Integration")
+    public void testRunningPidFromFile() throws IOException {
+        File f = File.createTempFile("testBrooklynPid", ".pid");
+        Files.write( (""+getMyPid()).getBytes(), f );
+        ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.codePidFromFileRunning(f.getPath()));
+        Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0);
+        ProcessTaskWrapper<Boolean> t2 = submit(SshEffectorTasks.isPidFromFileRunning(f.getPath()));
+        Assert.assertTrue(t2.getTask().getUnchecked());
+    }
+
+    @Test(groups="Integration")
+    public void testRequirePidFromFileOnFailure() throws IOException {
+        File f = File.createTempFile("testBrooklynPid", ".pid");
+        Files.write( "99999".getBytes(), f );
+        ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning(f.getPath()));
+        
+        setExpectingFailure();
+        try {
+            t.getTask().getUnchecked();
+        } catch (Exception e) {
+            log.info("The error if required PID is not found is: "+e);
+            clearExpectedFailure();
+            Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e);
+            Assert.assertEquals(t.getExitCode(), (Integer)1);
+        }
+        checkExpectedFailure();
+    }
+
+    @Test(groups="Integration")
+    public void testRequirePidFromFileOnFailureNoSuchFile() throws IOException {
+        ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning("/path/does/not/exist/SADVQW"));
+        
+        setExpectingFailure();
+        try {
+            t.getTask().getUnchecked();
+        } catch (Exception e) {
+            log.info("The error if required PID is not found is: "+e);
+            clearExpectedFailure();
+            Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e);
+            Assert.assertEquals(t.getExitCode(), (Integer)1);
+        }
+        checkExpectedFailure();
+    }
+
+    @Test(groups="Integration")
+    public void testRequirePidFromFileOnFailureTooManyFiles() throws IOException {
+        ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning("/*"));
+        
+        setExpectingFailure();
+        try {
+            t.getTask().getUnchecked();
+        } catch (Exception e) {
+            log.info("The error if required PID is not found is: "+e);
+            clearExpectedFailure();
+            Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e);
+            Assert.assertEquals(t.getExitCode(), (Integer)2);
+        }
+        checkExpectedFailure();
+    }
+
+    @Test(groups="Integration")
+    public void testRequirePidFromFileOnSuccess() throws IOException {
+        File f = File.createTempFile("testBrooklynPid", ".pid");
+        Files.write( (""+getMyPid()).getBytes(), f );
+        ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning(f.getPath()));
+        
+        t.getTask().getUnchecked();
+    }
+
+    @Test(groups="Integration")
+    public void testRequirePidFromFileOnSuccessAcceptsWildcards() throws IOException {
+        File f = File.createTempFile("testBrooklynPid", ".pid");
+        Files.write( (""+getMyPid()).getBytes(), f );
+        ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning(f.getPath()+"*"));
+        
+        t.getTask().getUnchecked();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/brooklyn/entity/basic/frogs.txt
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/brooklyn/entity/basic/frogs.txt b/software/base/src/test/resources/brooklyn/entity/basic/frogs.txt
deleted file mode 100644
index a3f617a..0000000
--- a/software/base/src/test/resources/brooklyn/entity/basic/frogs.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-#
-# The Frogs
-
-Frogs:
-> Brekekekex koax koax.
-
-Dionysus:
-> Go to hell with your koax
-> koax and nothing but koax!
-
-- Artistophanes, c. 400 BC

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/brooklyn/entity/basic/template.yaml
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/brooklyn/entity/basic/template.yaml b/software/base/src/test/resources/brooklyn/entity/basic/template.yaml
deleted file mode 100644
index 4d2b29e..0000000
--- a/software/base/src/test/resources/brooklyn/entity/basic/template.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-[#ftl]
-# 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.
-
-entity.hostname:
-    ${driver.hostname}
-entity.address:
-    ${driver.address}
-frogs: 12
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/brooklyn/entity/basic/template_with_extra_substitutions.txt
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/brooklyn/entity/basic/template_with_extra_substitutions.txt b/software/base/src/test/resources/brooklyn/entity/basic/template_with_extra_substitutions.txt
deleted file mode 100644
index 8330e3e..0000000
--- a/software/base/src/test/resources/brooklyn/entity/basic/template_with_extra_substitutions.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-# 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.
-
-${myname}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem b/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem
deleted file mode 100644
index 4ca4d00..0000000
--- a/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEArJZcg6Y9BuPYuLWgcloS/cH6HzqQqQegW+5d7cgRZC1CCo4e
-GxVglEZXuko4N0PVK2E2khGiMEymVXWqmrHAnTSqV1COmRfxxOxGvmQRleDWMhED
-j2KoOwggwsaoHBEdrZ6NX+yv3iFiOS5fz3Klkt3kPXcXZJGiFsOtyZaSxK/m26no
-upS6ouChmk9TD8YcCQzdUGNAWwyzhiDg5DwqnnNQQUKvQyJuV/nyxwRMcpoNazca
-ghr2KalSF92sonnPzMz2gWhKxWVVbnTSafWQcWY+ov/TiyBHsLXO3/OrHaj7h+SF
-UUfWryHyqZI/eQeoWD/cPGtr8qVELIqhCq8OkQIDAQABAoIBAAS2Eg5x8kaG37qj
-Ep8sgEo3Clnh4mMK10DLL/s2s+rVJXFeUcoRelJx3SEzt3civeXyQGgaXSAOZ5f7
-n02bvpNMBb5eb5YURkBG5uN5ndVGjvJM7pjya385CJPoklw5x2Ke6mMM9fwNUz4W
-Wv0xtv1cW0emQZg4NYGDk/Hlz1nZG+y5j0esRlIiknYqdfuXtaQbVI/jGSyNXFVI
-uVkKCwq+k6HnIkjFEZdvQPRPaGMT4cMwczVr8hCIOfCJEVremEJ0GFnazr9iNEVu
-18YJzOEaqPLgVvjt59o+D+CoZS7U74WX6scCsFCkHLdnxlfNC/qkrn2SEaVyrfeP
-cqPLZJUCgYEA1qmmQzuNzWMhsOVty3EI4M2mePF0iM2P/wHc1ry9lddqdqTSGV7x
-NwzGNZ/OqN9UIR/3chyFXQuegz5VZxsrdIyxcgCmM2cDDxny3pr0Lct39gsWuc9g
-BhebUDP7PoF2g2f4AAllVR6atClwXoKfARoXlmbjfOJSB/q1zXWYNW8CgYEAzdJ/
-2o1B4iwDipKxJE2luuaMN4zqRbKdokBJYw0b1TOOcsyNwB5h0TGcJNFMuIqBeWCK
-J3wr7FTOfy3Qsbf3Vc5Le6NdaHomy7+HEwVMzyEPVNKumHqEL9U3Yheu5TVLLKMU
-rYBXXpoN/XXm9XxvRGhGu7Wu99ye37hIa4Qy+/8CgYB/JWlHdWTefJMeFibcU41w
-qh6qkEn4Jdner5nAz3Zz5G447BNN18CEXNqiNI/R0sYgALEuM9qCbDltf2RSd/Nb
-S2JnJh9LXv6e2T3TwHBVF6lsYNELKdu0gBMMhF2SflhWKSTp0KbmrgPwJoNaV4Aa
-xPunqDWiaOMxurwogDixnQKBgGgGQs5P5IOOIUARQeyKPgAHc8jDtMgDLX6KpUyl
-nHKV+yH0VpRKBsA3JabKDc+bWTLiYxDvxjdM6Y0Ht9mKlDxO5oGXoKckTaVeqMMp
-Q5fQKrKBRPMVwOd4COTP+XopBFSMG/BQ1Feg3v9CbreV65qUZWOLwOHPJJEHz7pf
-d3E7AoGBAMPkKjDuhU8YTpB6H4KlCM+9p/6yE7umoWMPJm70301XxBoQA8spkS3o
-K2s2xL7+MuppvLv0xqyZ1vtvqmUMrw/eisQWekGobEiutk+DuxegRlpUkH0s6CH4
-lypeh/FpGdLayAn84ALIEYhCsr0SnJtEL1MEO9qPKJY24Hl6ATCC
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem b/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem
deleted file mode 100644
index 7fb694c..0000000
--- a/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIEowIBAAKCAQEA1L/awgkt+AxAHc76IeJAsZYj0ZoW8pP0Ff2SL8ZmkoGOT3C6
-d6DhQ8H194YTYNZ+w+D4K6DkNf8MqoFnQnLyQHlALd+giCE9mS1SUS0YBBdXbGh/
-320cosPqvMrT16nxbTLz7rgGNYSvgS5tnFYIADv4AzvXZMOZ20VXkDNCiqG0Pqy5
-qAMvmNGJNpDfERebdQUoZpqPJysJYXmy2cKYm5cfsDCxmL9iGyPjHazU6FizC50M
-SJ+ZGtqVEB/0d4p4u/pHAZQJCFdxJszHqBbYfQDFJso9lAdkM/11Tq5NtBH3cTPi
-SfcFIWLSS4qu1RcRmURRtsZTr/4FOKcYWpmLcwIDAQABAoIBACLzzi3YsjuxT4tW
-KNQORtCmOQZDiYea9Rvzx+OfddSWAlpcy9XBkaC/Kfjbhy1+r97ghAUu7q9MUrlk
-fyF0nwYmpXuj5MzYoTUcNAzwGqT5bLx7wp5jX7QB+fvAWuNwoSBuoZLTmvmJ7geM
-KCfqHnBjadCmMTH1zL4wez1Onp0FvRDXGPGjERQGptpejTL8PBaarzF2Ufewb/0z
-2/Icl1RAGHNkJHJjedL2mgtKHzW/JTBLcMjN1+PL4pLMriXCTP3VZZlezx8RXrnW
-DTFWe6G8gcPhYh0ni3wtoI/ENvXORg+wFXa2B/FKRObTu+vyYyvDgUSB2TI5LebX
-XsuWEEECgYEA/f9gkab9+m9JDz7AYDF4f+YzECdZwA0ntAnA5Qb/zWnjbcbtOMMF
-lNEm+m9J9GoorSpO9e/4Z61dtEsuX9Vr2tyKSnamNQT2vI+WIUIbGMWFq/VNi390
-o0jQWhEead/xu5BFbLEBD651+NBQGBCQi8slzOAL6jqP8qEGeaYaauECgYEA1m06
-wdHM3U5wwCGsyzTloGZ0LtTFO6oKYPpU/MItEESqVY1grFh5Sqa6tyJEq0jZSdKM
-eXzlqSyJQbaHd85PJnDkVnJVpdeqRj1eMHtv9+s3A8kOzSww+FL7KJHUrnNBZ7MK
-J7wdPtzYBFL124NAhbTaR6+Kv1ZSUpkGFRCV9NMCgYBtMy5BcJog4Vd3xnLO6HX2
-BvJNL53Wg9FeBhN4Y9n2Zl/xAmVa0f3ETWeEo/QXsMxsJpRsCA+0A0UWDnyRlyAX
-qFmMShaLFOc/ijvxcIpVzBX8KCp+nv12dgedsV5yBmXXTd+LK05Zf5gYsPa+YeDD
-OUO3IVv+B897cN9nzZHuAQKBgGIC44ycXUv7AsaPne/0adF7gze0wcKX4s6ZHie6
-ieaZvFIGoV2lwytAMrBq1YCFd+yqdNNDJ6bAWKzUxe8ZOkyT5YsuD8ASaB5bBqaa
-hX+I4Ei2qjFWNbwMEgllPxXOUOMZj1bCQYvuXj77vK1tvRxgojWKI5150381uvX9
-8s1JAoGBAO73ig60VNqPTHg1KDZ7SdAW0KbNiAV+xojvW9pkH5M1vCllz+lpeW1l
-jZFbnL013r5UvzRgwqIuw4t+gZikfIZgPwP8Z2BGtIzNiIHy/RLzOawsPTTXczZA
-hHI2e54G9DgCi8h4LXhFavVykTkNTP62S6za4VTEjxERdWxIBnfL
------END RSA PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb b/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb
deleted file mode 100644
index d52e492..0000000
--- a/software/base/src/test/resources/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# 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.
-#
-
-current_dir = File.dirname(__FILE__)
-log_level                :info
-log_location             STDOUT
-node_name                "brooklyn-tests"
-client_key               "#{current_dir}/brooklyn-tests.pem"
-validation_client_name   "brooklyn-validator"
-validation_key           "#{current_dir}/brooklyn-validator.pem"
-chef_server_url          "https://api.opscode.com/organizations/brooklyn"

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem b/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem
new file mode 100644
index 0000000..4ca4d00
--- /dev/null
+++ b/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-tests.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEArJZcg6Y9BuPYuLWgcloS/cH6HzqQqQegW+5d7cgRZC1CCo4e
+GxVglEZXuko4N0PVK2E2khGiMEymVXWqmrHAnTSqV1COmRfxxOxGvmQRleDWMhED
+j2KoOwggwsaoHBEdrZ6NX+yv3iFiOS5fz3Klkt3kPXcXZJGiFsOtyZaSxK/m26no
+upS6ouChmk9TD8YcCQzdUGNAWwyzhiDg5DwqnnNQQUKvQyJuV/nyxwRMcpoNazca
+ghr2KalSF92sonnPzMz2gWhKxWVVbnTSafWQcWY+ov/TiyBHsLXO3/OrHaj7h+SF
+UUfWryHyqZI/eQeoWD/cPGtr8qVELIqhCq8OkQIDAQABAoIBAAS2Eg5x8kaG37qj
+Ep8sgEo3Clnh4mMK10DLL/s2s+rVJXFeUcoRelJx3SEzt3civeXyQGgaXSAOZ5f7
+n02bvpNMBb5eb5YURkBG5uN5ndVGjvJM7pjya385CJPoklw5x2Ke6mMM9fwNUz4W
+Wv0xtv1cW0emQZg4NYGDk/Hlz1nZG+y5j0esRlIiknYqdfuXtaQbVI/jGSyNXFVI
+uVkKCwq+k6HnIkjFEZdvQPRPaGMT4cMwczVr8hCIOfCJEVremEJ0GFnazr9iNEVu
+18YJzOEaqPLgVvjt59o+D+CoZS7U74WX6scCsFCkHLdnxlfNC/qkrn2SEaVyrfeP
+cqPLZJUCgYEA1qmmQzuNzWMhsOVty3EI4M2mePF0iM2P/wHc1ry9lddqdqTSGV7x
+NwzGNZ/OqN9UIR/3chyFXQuegz5VZxsrdIyxcgCmM2cDDxny3pr0Lct39gsWuc9g
+BhebUDP7PoF2g2f4AAllVR6atClwXoKfARoXlmbjfOJSB/q1zXWYNW8CgYEAzdJ/
+2o1B4iwDipKxJE2luuaMN4zqRbKdokBJYw0b1TOOcsyNwB5h0TGcJNFMuIqBeWCK
+J3wr7FTOfy3Qsbf3Vc5Le6NdaHomy7+HEwVMzyEPVNKumHqEL9U3Yheu5TVLLKMU
+rYBXXpoN/XXm9XxvRGhGu7Wu99ye37hIa4Qy+/8CgYB/JWlHdWTefJMeFibcU41w
+qh6qkEn4Jdner5nAz3Zz5G447BNN18CEXNqiNI/R0sYgALEuM9qCbDltf2RSd/Nb
+S2JnJh9LXv6e2T3TwHBVF6lsYNELKdu0gBMMhF2SflhWKSTp0KbmrgPwJoNaV4Aa
+xPunqDWiaOMxurwogDixnQKBgGgGQs5P5IOOIUARQeyKPgAHc8jDtMgDLX6KpUyl
+nHKV+yH0VpRKBsA3JabKDc+bWTLiYxDvxjdM6Y0Ht9mKlDxO5oGXoKckTaVeqMMp
+Q5fQKrKBRPMVwOd4COTP+XopBFSMG/BQ1Feg3v9CbreV65qUZWOLwOHPJJEHz7pf
+d3E7AoGBAMPkKjDuhU8YTpB6H4KlCM+9p/6yE7umoWMPJm70301XxBoQA8spkS3o
+K2s2xL7+MuppvLv0xqyZ1vtvqmUMrw/eisQWekGobEiutk+DuxegRlpUkH0s6CH4
+lypeh/FpGdLayAn84ALIEYhCsr0SnJtEL1MEO9qPKJY24Hl6ATCC
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem b/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem
new file mode 100644
index 0000000..7fb694c
--- /dev/null
+++ b/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/brooklyn-validator.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1L/awgkt+AxAHc76IeJAsZYj0ZoW8pP0Ff2SL8ZmkoGOT3C6
+d6DhQ8H194YTYNZ+w+D4K6DkNf8MqoFnQnLyQHlALd+giCE9mS1SUS0YBBdXbGh/
+320cosPqvMrT16nxbTLz7rgGNYSvgS5tnFYIADv4AzvXZMOZ20VXkDNCiqG0Pqy5
+qAMvmNGJNpDfERebdQUoZpqPJysJYXmy2cKYm5cfsDCxmL9iGyPjHazU6FizC50M
+SJ+ZGtqVEB/0d4p4u/pHAZQJCFdxJszHqBbYfQDFJso9lAdkM/11Tq5NtBH3cTPi
+SfcFIWLSS4qu1RcRmURRtsZTr/4FOKcYWpmLcwIDAQABAoIBACLzzi3YsjuxT4tW
+KNQORtCmOQZDiYea9Rvzx+OfddSWAlpcy9XBkaC/Kfjbhy1+r97ghAUu7q9MUrlk
+fyF0nwYmpXuj5MzYoTUcNAzwGqT5bLx7wp5jX7QB+fvAWuNwoSBuoZLTmvmJ7geM
+KCfqHnBjadCmMTH1zL4wez1Onp0FvRDXGPGjERQGptpejTL8PBaarzF2Ufewb/0z
+2/Icl1RAGHNkJHJjedL2mgtKHzW/JTBLcMjN1+PL4pLMriXCTP3VZZlezx8RXrnW
+DTFWe6G8gcPhYh0ni3wtoI/ENvXORg+wFXa2B/FKRObTu+vyYyvDgUSB2TI5LebX
+XsuWEEECgYEA/f9gkab9+m9JDz7AYDF4f+YzECdZwA0ntAnA5Qb/zWnjbcbtOMMF
+lNEm+m9J9GoorSpO9e/4Z61dtEsuX9Vr2tyKSnamNQT2vI+WIUIbGMWFq/VNi390
+o0jQWhEead/xu5BFbLEBD651+NBQGBCQi8slzOAL6jqP8qEGeaYaauECgYEA1m06
+wdHM3U5wwCGsyzTloGZ0LtTFO6oKYPpU/MItEESqVY1grFh5Sqa6tyJEq0jZSdKM
+eXzlqSyJQbaHd85PJnDkVnJVpdeqRj1eMHtv9+s3A8kOzSww+FL7KJHUrnNBZ7MK
+J7wdPtzYBFL124NAhbTaR6+Kv1ZSUpkGFRCV9NMCgYBtMy5BcJog4Vd3xnLO6HX2
+BvJNL53Wg9FeBhN4Y9n2Zl/xAmVa0f3ETWeEo/QXsMxsJpRsCA+0A0UWDnyRlyAX
+qFmMShaLFOc/ijvxcIpVzBX8KCp+nv12dgedsV5yBmXXTd+LK05Zf5gYsPa+YeDD
+OUO3IVv+B897cN9nzZHuAQKBgGIC44ycXUv7AsaPne/0adF7gze0wcKX4s6ZHie6
+ieaZvFIGoV2lwytAMrBq1YCFd+yqdNNDJ6bAWKzUxe8ZOkyT5YsuD8ASaB5bBqaa
+hX+I4Ei2qjFWNbwMEgllPxXOUOMZj1bCQYvuXj77vK1tvRxgojWKI5150381uvX9
+8s1JAoGBAO73ig60VNqPTHg1KDZ7SdAW0KbNiAV+xojvW9pkH5M1vCllz+lpeW1l
+jZFbnL013r5UvzRgwqIuw4t+gZikfIZgPwP8Z2BGtIzNiIHy/RLzOawsPTTXczZA
+hHI2e54G9DgCi8h4LXhFavVykTkNTP62S6za4VTEjxERdWxIBnfL
+-----END RSA PRIVATE KEY-----
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb b/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb
new file mode 100644
index 0000000..d52e492
--- /dev/null
+++ b/software/base/src/test/resources/org/apache/brooklyn/entity/chef/hosted-chef-brooklyn-credentials/knife.rb
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+current_dir = File.dirname(__FILE__)
+log_level                :info
+log_location             STDOUT
+node_name                "brooklyn-tests"
+client_key               "#{current_dir}/brooklyn-tests.pem"
+validation_client_name   "brooklyn-validator"
+validation_key           "#{current_dir}/brooklyn-validator.pem"
+chef_server_url          "https://api.opscode.com/organizations/brooklyn"

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/frogs.txt
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/frogs.txt b/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/frogs.txt
new file mode 100644
index 0000000..a3f617a
--- /dev/null
+++ b/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/frogs.txt
@@ -0,0 +1,27 @@
+# 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.
+#
+# The Frogs
+
+Frogs:
+> Brekekekex koax koax.
+
+Dionysus:
+> Go to hell with your koax
+> koax and nothing but koax!
+
+- Artistophanes, c. 400 BC

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template.yaml
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template.yaml b/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template.yaml
new file mode 100644
index 0000000..4d2b29e
--- /dev/null
+++ b/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template.yaml
@@ -0,0 +1,23 @@
+[#ftl]
+# 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.
+
+entity.hostname:
+    ${driver.hostname}
+entity.address:
+    ${driver.address}
+frogs: 12
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template_with_extra_substitutions.txt
----------------------------------------------------------------------
diff --git a/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template_with_extra_substitutions.txt b/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template_with_extra_substitutions.txt
new file mode 100644
index 0000000..8330e3e
--- /dev/null
+++ b/software/base/src/test/resources/org/apache/brooklyn/entity/software/base/template_with_extra_substitutions.txt
@@ -0,0 +1,18 @@
+# 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.
+
+${myname}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNode.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNode.java
index 9762d5c..94b03bf 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNode.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNode.java
@@ -22,16 +22,12 @@ import org.apache.brooklyn.api.entity.ImplementedBy;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
-
-import brooklyn.entity.basic.SoftwareProcess;
-
 import org.apache.brooklyn.entity.core.Attributes;
 import org.apache.brooklyn.entity.database.DatastoreMixins.DatastoreCommon;
-
-import brooklyn.entity.java.UsesJava;
-import brooklyn.entity.java.UsesJavaMXBeans;
-import brooklyn.entity.java.UsesJmx;
-
+import org.apache.brooklyn.entity.java.UsesJava;
+import org.apache.brooklyn.entity.java.UsesJavaMXBeans;
+import org.apache.brooklyn.entity.java.UsesJmx;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 import org.apache.brooklyn.location.basic.PortRanges;
 import org.apache.brooklyn.sensor.core.AttributeSensorAndConfigKey;
 import org.apache.brooklyn.sensor.core.BasicAttributeSensorAndConfigKey;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeDriver.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeDriver.java
index 708e0e8..de01b6b 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeDriver.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeDriver.java
@@ -18,7 +18,7 @@
  */
 package org.apache.brooklyn.entity.database.crate;
 
-import brooklyn.entity.java.JavaSoftwareProcessDriver;
+import org.apache.brooklyn.entity.java.JavaSoftwareProcessDriver;
 
 public interface CrateNodeDriver extends JavaSoftwareProcessDriver {
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeImpl.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeImpl.java
index 5cf4065..7e4f314 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeImpl.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeImpl.java
@@ -20,16 +20,15 @@ package org.apache.brooklyn.entity.database.crate;
 
 import org.apache.brooklyn.core.config.render.RendererHints;
 import org.apache.brooklyn.entity.core.Attributes;
+import org.apache.brooklyn.entity.java.JavaAppUtils;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
 import org.apache.brooklyn.sensor.enricher.Enrichers;
 import org.apache.brooklyn.sensor.feed.http.HttpFeed;
 import org.apache.brooklyn.sensor.feed.http.HttpPollConfig;
 import org.apache.brooklyn.sensor.feed.http.HttpValueFunctions;
+import org.apache.brooklyn.sensor.feed.jmx.JmxFeed;
 import org.apache.brooklyn.util.guava.Functionals;
 
-import brooklyn.entity.basic.SoftwareProcessImpl;
-import brooklyn.entity.java.JavaAppUtils;
-import brooklyn.event.feed.jmx.JmxFeed;
-
 public class CrateNodeImpl extends SoftwareProcessImpl implements CrateNode{
 
     private JmxFeed jmxFeed;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeSshDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeSshDriver.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeSshDriver.java
index f72c071..fbfe9dd 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeSshDriver.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/crate/CrateNodeSshDriver.java
@@ -24,11 +24,10 @@ import java.util.List;
 
 import org.apache.brooklyn.api.internal.EntityLocal;
 import org.apache.brooklyn.entity.core.Entities;
+import org.apache.brooklyn.entity.java.JavaSoftwareProcessSshDriver;
 
 import com.google.common.collect.ImmutableList;
 
-import brooklyn.entity.java.JavaSoftwareProcessSshDriver;
-
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.net.Urls;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbDriver.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbDriver.java
index 066a73b..05c097a 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbDriver.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbDriver.java
@@ -18,10 +18,9 @@
  */
 package org.apache.brooklyn.entity.database.mariadb;
 
+import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
 import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
 
-import brooklyn.entity.basic.SoftwareProcessDriver;
-
 /**
  * The {@link SoftwareProcessDriver} for MariaDB.
  */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNode.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNode.java
index 5612b5b..a5a91f6 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNode.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNode.java
@@ -25,12 +25,10 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.MapConfigKey;
-
-import brooklyn.entity.basic.SoftwareProcess;
-
 import org.apache.brooklyn.entity.core.Attributes;
 import org.apache.brooklyn.entity.database.DatabaseNode;
 import org.apache.brooklyn.entity.database.DatastoreMixins.DatastoreCommon;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 import org.apache.brooklyn.location.basic.PortRanges;
 import org.apache.brooklyn.sensor.core.BasicAttributeSensorAndConfigKey;
 import org.apache.brooklyn.sensor.core.PortAttributeSensorAndConfigKey;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
index 632e3f2..0b5becf 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbNodeImpl.java
@@ -20,10 +20,8 @@ package org.apache.brooklyn.entity.database.mariadb;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.SoftwareProcessImpl;
-
 import org.apache.brooklyn.effector.core.EffectorBody;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.sensor.feed.ssh.SshFeed;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbSshDriver.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
index 054e5fa..300cc35 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mariadb/MariaDbSshDriver.java
@@ -33,17 +33,13 @@ import java.util.Map;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
-
 import org.apache.brooklyn.entity.core.Attributes;
 import org.apache.brooklyn.entity.core.Entities;
 import org.apache.brooklyn.entity.database.DatastoreMixins;
-
-import brooklyn.entity.software.SshEffectorTasks;
-
+import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
 import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
+import org.apache.brooklyn.sensor.ssh.SshEffectorTasks;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.task.DynamicTasks;
 import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlDriver.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlDriver.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlDriver.java
index b11d22b..461369b 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlDriver.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlDriver.java
@@ -18,10 +18,9 @@
  */
 package org.apache.brooklyn.entity.database.mysql;
 
+import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
 import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
 
-import brooklyn.entity.basic.SoftwareProcessDriver;
-
 /**
  * The {@link SoftwareProcessDriver} for MySQL.
  */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
index b9e9f2c..be837c4 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
@@ -25,14 +25,12 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.MapConfigKey;
-
-import brooklyn.entity.basic.SoftwareProcess;
-
 import org.apache.brooklyn.effector.core.MethodEffector;
 import org.apache.brooklyn.entity.annotation.Effector;
 import org.apache.brooklyn.entity.annotation.EffectorParam;
 import org.apache.brooklyn.entity.core.Attributes;
 import org.apache.brooklyn.entity.database.DatastoreMixins.DatastoreCommon;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 import org.apache.brooklyn.location.basic.PortRanges;
 import org.apache.brooklyn.sensor.core.BasicAttributeSensorAndConfigKey;
 import org.apache.brooklyn.sensor.core.PortAttributeSensorAndConfigKey;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/64c2b2e5/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNodeImpl.java b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNodeImpl.java
index 5d1462f..4f0ae7d 100644
--- a/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNodeImpl.java
+++ b/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNodeImpl.java
@@ -22,11 +22,9 @@ import java.util.Map;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.effector.core.EffectorBody;
+import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.SoftwareProcessImpl;
-
 import org.apache.brooklyn.location.basic.Locations;
 import org.apache.brooklyn.location.basic.SshMachineLocation;
 import org.apache.brooklyn.sensor.feed.ssh.SshFeed;