http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
----------------------------------------------------------------------
diff --cc 
brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
index 0000000,5ce2c1b..a9ff180
mode 000000,100644..100644
--- 
a/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
+++ 
b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
@@@ -1,0 -1,322 +1,345 @@@
+ /*
+  * 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.dns;
+ 
++import static 
org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsContinually;
++import static 
org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEventually;
++import static org.testng.Assert.assertFalse;
+ import static org.testng.Assert.assertTrue;
+ 
+ import java.util.Collection;
+ import java.util.LinkedHashMap;
+ import java.util.Map;
+ 
+ import org.apache.brooklyn.api.entity.Entity;
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.api.entity.ImplementedBy;
+ import org.apache.brooklyn.api.location.Location;
+ import org.apache.brooklyn.api.location.LocationRegistry;
+ import org.apache.brooklyn.api.location.LocationResolver;
+ import org.apache.brooklyn.api.location.LocationSpec;
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ import org.apache.brooklyn.config.ConfigKey;
+ import org.apache.brooklyn.core.config.ConfigKeys;
+ import org.apache.brooklyn.core.entity.Attributes;
+ import org.apache.brooklyn.core.entity.Entities;
 -import org.apache.brooklyn.core.entity.EntityInternal;
 -import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
++import org.apache.brooklyn.core.entity.EntityAsserts;
++import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+ import org.apache.brooklyn.core.location.BasicLocationRegistry;
+ import org.apache.brooklyn.core.location.LocationConfigKeys;
+ import org.apache.brooklyn.core.location.Locations;
+ import org.apache.brooklyn.core.location.Machines;
+ import org.apache.brooklyn.core.location.SimulatedLocation;
+ import org.apache.brooklyn.core.location.geo.HostGeoInfo;
 -import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 -import org.apache.brooklyn.core.test.entity.TestApplication;
++import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+ import org.apache.brooklyn.core.test.entity.TestEntity;
 -import org.apache.brooklyn.entity.dns.AbstractGeoDnsService;
 -import org.apache.brooklyn.entity.dns.AbstractGeoDnsServiceImpl;
 -import org.apache.brooklyn.entity.dns.AbstractGeoDnsServiceTest;
+ import org.apache.brooklyn.entity.group.DynamicFabric;
+ import org.apache.brooklyn.entity.group.DynamicGroup;
+ import org.apache.brooklyn.entity.group.DynamicRegionsFabric;
 -import org.apache.brooklyn.test.EntityTestUtils;
+ import org.apache.brooklyn.util.collections.CollectionFunctionals;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ 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.ssh.SshMachineLocation;
+ 
+ import com.google.common.base.Predicates;
+ import com.google.common.collect.ImmutableList;
+ import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.Iterables;
+ 
 -public class AbstractGeoDnsServiceTest {
++public class AbstractGeoDnsServiceTest extends BrooklynAppUnitTestSupport {
++
+     public static final Logger log = 
LoggerFactory.getLogger(AbstractGeoDnsServiceTest.class);
+ 
+     private static final String WEST_IP = "100.0.0.1";
+     private static final String EAST_IP = "100.0.0.2";
+     private static final double WEST_LATITUDE = 0, WEST_LONGITUDE = -60;
+     private static final double EAST_LATITUDE = 0, EAST_LONGITUDE = 60;
+     
+     private static final String NORTH_IP = "10.0.0.1";
+     private static final double NORTH_LATITUDE = 60, NORTH_LONGITUDE = 0;
+     
 -    private ManagementContext managementContext;
 -    
+     private Location westParent;
+     private Location westChild;
+     private Location westChildWithLocation; 
+     private Location eastParent;
+     private Location eastChild; 
+     private Location eastChildWithLocationAndWithPrivateHostname; 
+ 
+     private Location northParent;
+     private Location northChildWithLocation; 
+ 
 -    private TestApplication app;
+     private DynamicRegionsFabric fabric;
+     private DynamicGroup testEntities;
+     private GeoDnsTestService geoDns;
+     
 -
++    @Override
+     @BeforeMethod(alwaysRun=true)
 -    public void setup() {
 -        managementContext = new LocalManagementContext();
 -        
++    public void setUp() throws Exception {
++        super.setUp();
++
+         westParent = newSimulatedLocation("West parent", WEST_LATITUDE, 
WEST_LONGITUDE);
+         
+         // west uses public IP for name, so is always picked up
+         westChild = newSshMachineLocation("West child", WEST_IP, westParent);
+         westChildWithLocation = newSshMachineLocation("West child with 
location", WEST_IP, WEST_IP, westParent, WEST_LATITUDE, WEST_LONGITUDE); 
+         
+         // east has public IP but private IP hostname, so should also be 
picked up but by a different path
+         eastParent = newSimulatedLocation("East parent", EAST_LATITUDE, 
EAST_LONGITUDE);
+         eastChild = newSshMachineLocation("East child", EAST_IP, eastParent); 
+         eastChildWithLocationAndWithPrivateHostname = 
newSshMachineLocation("East child with location", "localhost", EAST_IP, 
eastParent, EAST_LATITUDE, EAST_LONGITUDE); 
+ 
+         // north has a private IP and private hostname so should not be 
picked up when we turn off ADD_ANYTHING
+         northParent = newSimulatedLocation("North parent", NORTH_LATITUDE, 
NORTH_LONGITUDE);
+         northChildWithLocation = newSshMachineLocation("North child", 
"localhost", NORTH_IP, northParent, NORTH_LATITUDE, NORTH_LONGITUDE);
 -        
((BasicLocationRegistry)managementContext.getLocationRegistry()).registerResolver(new
 LocationResolver() {
++        ((BasicLocationRegistry) 
mgmt.getLocationRegistry()).registerResolver(new LocationResolver() {
+             @Override
+             public Location newLocationFromString(Map locationFlags, String 
spec, LocationRegistry registry) {
+                 if (!spec.equals("test:north")) throw new 
IllegalStateException("unsupported");
+                 return northChildWithLocation;
+             }
+             @Override
+             public void init(ManagementContext managementContext) {
+             }
+             @Override
+             public String getPrefix() {
+                 return "test";
+             }
+             @Override
+             public boolean accepts(String spec, LocationRegistry registry) {
+                 return spec.startsWith(getPrefix());
+             }
+         });
+ 
 -        Locations.manage(westParent, managementContext);
 -        Locations.manage(eastParent, managementContext);
 -        Locations.manage(northParent, managementContext);
++        Locations.manage(westParent, mgmt);
++        Locations.manage(eastParent, mgmt);
++        Locations.manage(northParent, mgmt);
+         
 -        app = ApplicationBuilder.newManagedApp(TestApplication.class, 
managementContext);
+         fabric = 
app.createAndManageChild(EntitySpec.create(DynamicRegionsFabric.class)
+             .configure(DynamicFabric.MEMBER_SPEC, 
EntitySpec.create(TestEntity.class)));
 -        
++
+         testEntities = 
app.createAndManageChild(EntitySpec.create(DynamicGroup.class)
+             .configure(DynamicGroup.ENTITY_FILTER, 
Predicates.instanceOf(TestEntity.class)));
 -        geoDns = 
app.createAndManageChild(EntitySpec.create(GeoDnsTestService.class));
 -        geoDns.setTargetEntityProvider(testEntities);
 -    }
+ 
 -    @AfterMethod(alwaysRun=true)
 -    public void tearDown() throws Exception {
 -        if (app != null) Entities.destroyAll(app.getManagementContext());
++        geoDns = 
app.createAndManageChild(EntitySpec.create(GeoDnsTestService.class)
++            .configure(AbstractGeoDnsService.ENTITY_PROVIDER, testEntities));
+     }
+ 
+     private SimulatedLocation newSimulatedLocation(String name, double lat, 
double lon) {
 -        return 
managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)
++        return 
mgmt.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)
+                 .displayName(name)
+                 .configure("latitude", lat)
+                 .configure("longitude", lon));
+     }
+     
+     private Location newSshMachineLocation(String name, String address, 
Location parent) {
 -        return 
managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
++        return 
mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                 .parent(parent)
+                 .displayName(name)
+                 .configure("address", address));
+     }
+     
+     private Location newSshMachineLocation(String name, String hostname, 
String address, Location parent, double lat, double lon) {
 -        return 
managementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
++        return 
mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                 .parent(parent)
+                 .displayName(name)
+                 .configure("hostname", hostname)
+                 .configure("address", address)
+                 .configure("latitude", lat)
+                 .configure("longitude", lon));
+     }
+     
+     @Test
+     public void testGeoInfoOnLocation() {
+         app.start( ImmutableList.of(westChildWithLocation, 
eastChildWithLocationAndWithPrivateHostname) );
+         publishSensors(2, true, true, true);
+         
 -        EntityTestUtils.assertAttributeEventually(geoDns, 
AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2));
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("West child with 
location"), "targets="+geoDns.getTargetHostsByName());
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("East child with 
location"), "targets="+geoDns.getTargetHostsByName());
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
++        assertIsTarget("West child with location");
++        assertIsTarget("East child with location");
+     }
 -    
++
+     @Test
+     public void testGeoInfoOnParentLocation() {
+         app.start( ImmutableList.of(westChild, eastChild) );
+         publishSensors(2, true, false, false);
 -        
 -        EntityTestUtils.assertAttributeEventually(geoDns, 
AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2));
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("West child"), 
"targets="+geoDns.getTargetHostsByName());
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("East child"), 
"targets="+geoDns.getTargetHostsByName());
++
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
++        assertIsTarget("West child");
++        assertIsTarget("East child");
+     }
+ 
+     @Test
+     public void testSubscribesToHostname() {
 -        
((EntityInternal)geoDns).config().set(GeoDnsTestServiceImpl.ADD_ANYTHING, 
false);
++        geoDns.config().set(GeoDnsTestServiceImpl.ADD_ANYTHING, false);
+         app.start( ImmutableList.of(westChild, 
eastChildWithLocationAndWithPrivateHostname) );
+         Assert.assertEquals(geoDns.getTargetHostsByName().size(), 0);
+         publishSensors(2, true, true, true);
 -        
 -        EntityTestUtils.assertAttributeEventually(geoDns, 
AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2));
++
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
+         Assert.assertEquals(geoDns.getTargetHostsByName().size(), 2);
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("West child"), 
"targets="+geoDns.getTargetHostsByName());
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("East child with 
location"), "targets="+geoDns.getTargetHostsByName());
++        assertIsTarget("West child");
++        assertIsTarget("East child with location");
+     }
+ 
+     protected void publishSensors(int expectedSize, boolean includeServiceUp, 
boolean includeHostname, boolean includeAddress) {
+         // First wait for the right size of group; the dynamic group gets 
notified asynchronously
+         // of nodes added/removed, so if we don't wait then might not set 
value for all members.
 -        EntityTestUtils.assertGroupSizeEqualsEventually(testEntities, 
expectedSize);
 -        
++        EntityAsserts.assertGroupSizeEqualsEventually(testEntities, 
expectedSize);
++
+         for (Entity e: testEntities.getMembers()) {
+             if (includeServiceUp)
 -                ((EntityInternal)e).sensors().set(Attributes.SERVICE_UP, 
true);
 -            
++                e.sensors().set(Attributes.SERVICE_UP, true);
++
+             SshMachineLocation l = 
Machines.findUniqueMachineLocation(e.getLocations(), 
SshMachineLocation.class).get();
+             if (includeAddress)
 -                ((EntityInternal)e).sensors().set(Attributes.ADDRESS, 
l.getAddress().getHostAddress());
++                e.sensors().set(Attributes.ADDRESS, 
l.getAddress().getHostAddress());
+             String h = (String) l.config().getBag().getStringKey("hostname");
+             if (h==null) h = l.getAddress().getHostName();
+             if (includeHostname)
 -                ((EntityInternal)e).sensors().set(Attributes.HOSTNAME, h);
++                e.sensors().set(Attributes.HOSTNAME, h);
+         }
+     }
 -    
++
+     @Test
+     public void testChildAddedLate() {
+         app.start( ImmutableList.of(westChild, 
eastChildWithLocationAndWithPrivateHostname) );
+         publishSensors(2, true, false, false);
 -        EntityTestUtils.assertAttributeEventually(geoDns, 
AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2));
 -        
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
++
+         String id3 = fabric.addRegion("test:north");
+         publishSensors(3, true, false, false);
+         try {
 -            EntityTestUtils.assertAttributeEventually(geoDns, 
AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(3));
++            assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(3));
+         } catch (Throwable e) {
+             log.warn("Did not pick up third entity, targets are 
"+geoDns.getAttribute(AbstractGeoDnsService.TARGETS)+" (rethrowing): "+e);
+             Exceptions.propagate(e);
+         }
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("North child"), 
"targets="+geoDns.getTargetHostsByName());
 -        
 -        log.info("targets: "+geoDns.getTargetHostsByName());
 -    }    
++        assertIsTarget("North child");
+ 
++        log.info("targets: "+geoDns.getTargetHostsByName());
++    }
+ 
+     @Test
+     public void testFiltersEntirelyPrivate() {
 -        
((EntityInternal)geoDns).config().set(GeoDnsTestServiceImpl.ADD_ANYTHING, 
false);
++        geoDns.config().set(GeoDnsTestServiceImpl.ADD_ANYTHING, false);
+         app.start( ImmutableList.of(westChild, 
eastChildWithLocationAndWithPrivateHostname, northChildWithLocation) );
+         Assert.assertEquals(geoDns.getTargetHostsByName().size(), 0);
+         publishSensors(3, true, true, true);
 -        
 -        EntityTestUtils.assertAttributeEventually(geoDns, 
AbstractGeoDnsService.TARGETS, CollectionFunctionals.<String>mapSizeEquals(2));
++
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
+         Assert.assertEquals(geoDns.getTargetHostsByName().size(), 2);
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("West child"), 
"targets="+geoDns.getTargetHostsByName());
 -        assertTrue(geoDns.getTargetHostsByName().containsKey("East child with 
location"), "targets="+geoDns.getTargetHostsByName());
 -        assertTrue(!geoDns.getTargetHostsByName().containsKey("North child"), 
"targets="+geoDns.getTargetHostsByName());
++        assertIsTarget("West child");
++        assertIsTarget("East child with location");
++        assertIsNotTarget("North child");
++    }
++
++    @Test
++    public void testFiltersForRunningEntities() {
++        app.start(ImmutableList.of(westChildWithLocation, 
eastChildWithLocationAndWithPrivateHostname));
++        publishSensors(2, true, true, true);
++
++        TestEntity problemChild = Entities.descendants(app, 
TestEntity.class).iterator().next();
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
++        problemChild.sensors().set(Attributes.SERVICE_STATE_ACTUAL, 
Lifecycle.ON_FIRE);
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(1));
++        problemChild.sensors().set(Attributes.SERVICE_STATE_ACTUAL, 
Lifecycle.RUNNING);
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
++    }
++
++    @Test
++    public void testCanDisableFilterForRunningEntities() throws Exception {
++        geoDns.config().set(AbstractGeoDnsService.FILTER_FOR_RUNNING, false);
++        app.start(ImmutableList.of(westChildWithLocation, 
eastChildWithLocationAndWithPrivateHostname));
++        publishSensors(2, true, true, true);
++
++        assertAttributeEventually(geoDns, AbstractGeoDnsService.TARGETS, 
CollectionFunctionals.<String>mapSizeEquals(2));
++        final Map<String, String> targets = 
ImmutableMap.copyOf(geoDns.sensors().get(AbstractGeoDnsService.TARGETS));
++        TestEntity problemChild = Entities.descendants(app, 
TestEntity.class).iterator().next();
++        problemChild.sensors().set(Attributes.SERVICE_STATE_ACTUAL, 
Lifecycle.ON_FIRE);
++        assertAttributeEqualsContinually(geoDns, 
AbstractGeoDnsService.TARGETS, targets);
++    }
++
++    private void assertIsTarget(String target) {
++        assertTrue(geoDns.getTargetHostsByName().containsKey(target), 
"targets=" + geoDns.getTargetHostsByName());
++    }
++
++    private void assertIsNotTarget(String target) {
++        assertFalse(geoDns.getTargetHostsByName().containsKey(target), 
"targets=" + geoDns.getTargetHostsByName());
+     }
+ 
+     @ImplementedBy(GeoDnsTestServiceImpl.class)
+     public static interface GeoDnsTestService extends AbstractGeoDnsService {
+         public Map<String, HostGeoInfo> getTargetHostsByName();
+     }
+     
+     public static class GeoDnsTestServiceImpl extends 
AbstractGeoDnsServiceImpl implements GeoDnsTestService {
+         public Map<String, HostGeoInfo> targetHostsByName = new 
LinkedHashMap<String, HostGeoInfo>();
+ 
+         public static final ConfigKey<Boolean> ADD_ANYTHING = 
ConfigKeys.newBooleanConfigKey("test.add.always", "", true);
+         
+         public GeoDnsTestServiceImpl() {
+         }
+ 
+         @Override
+         public Map<String, HostGeoInfo> getTargetHostsByName() {
+             synchronized (targetHostsByName) {
+                 return ImmutableMap.copyOf(targetHostsByName);
+             }
+         }
+         
+         @Override
+         protected boolean addTargetHost(Entity e) {
+             if (!getConfig(ADD_ANYTHING)) {
+                 return super.addTargetHost(e);
+             } else {
+                 //ignore geo lookup, override parent menu
+                 if (e.getLocations().isEmpty()) {
+                     log.info("GeoDns TestService ignoring target host {} (no 
location)", e);
+                     return false;
+                 }
+                 Location l = Iterables.getOnlyElement(e.getLocations());
+                 HostGeoInfo geoInfo = new HostGeoInfo("<address-ignored>", 
l.getDisplayName(), 
+                     l.getConfig(LocationConfigKeys.LATITUDE), 
l.getConfig(LocationConfigKeys.LONGITUDE));
+                 log.info("GeoDns TestService adding target host {} {}", e, 
geoInfo);
+                 targetHosts.put(e, geoInfo);
+                 return true;
+             }
+         }
+         
+         @Override
+         protected void reconfigureService(Collection<HostGeoInfo> 
targetHosts) {
+             synchronized (targetHostsByName) {
+                 targetHostsByName.clear();
+                 for (HostGeoInfo host : targetHosts) {
+                     if (host != null) targetHostsByName.put(host.displayName, 
host);
+                 }
+             }
+         }
+ 
+         @Override
+         public String getHostname() {
+             return "localhost";
+         }
+     }
+     
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java
----------------------------------------------------------------------
diff --cc 
brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java
index 0000000,f030987..75c6c2f
mode 000000,100644..100644
--- 
a/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java
+++ 
b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java
@@@ -1,0 -1,212 +1,222 @@@
+ /*
+  * 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.dns.geoscaling;
+ 
+ import static org.testng.Assert.assertEquals;
+ 
+ import java.net.InetAddress;
+ 
 -import org.apache.brooklyn.api.entity.EntityLocal;
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.api.location.LocationSpec;
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ import org.apache.brooklyn.core.entity.Attributes;
 -import org.apache.brooklyn.core.entity.Entities;
++import org.apache.brooklyn.core.internal.BrooklynProperties;
+ import org.apache.brooklyn.core.location.geo.HostGeoInfo;
+ import org.apache.brooklyn.core.location.geo.HostGeoLookup;
+ import org.apache.brooklyn.core.location.geo.MaxMind2HostGeoLookup;
+ import org.apache.brooklyn.core.location.geo.UtraceHostGeoLookup;
 -import org.apache.brooklyn.core.test.entity.TestApplication;
++import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
++import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
++import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+ import org.apache.brooklyn.core.test.entity.TestEntity;
 -import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingDnsService;
 -import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingScriptGenerator;
+ import org.apache.brooklyn.entity.group.DynamicGroup;
+ import org.apache.brooklyn.test.Asserts;
+ import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ import org.apache.brooklyn.util.internal.BrooklynSystemProperties;
+ import org.apache.brooklyn.util.net.Networking;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
++import org.testng.SkipException;
+ import org.testng.annotations.AfterMethod;
+ import org.testng.annotations.BeforeMethod;
+ import org.testng.annotations.Test;
+ import org.apache.brooklyn.location.ssh.SshMachineLocation;
+ 
+ import com.google.common.base.Predicates;
+ import com.google.common.collect.ImmutableList;
+ 
+ /**
+  * {@link GeoscalingScriptGenerator} unit tests.
+  */
 -public class GeoscalingIntegrationTest {
++public class GeoscalingIntegrationTest extends BrooklynAppUnitTestSupport {
+ 
+     private static final Logger LOG = 
LoggerFactory.getLogger(GeoscalingIntegrationTest.class);
+ 
+     private final String primaryDomain = 
"geopaas.org";//"domain"+((int)(Math.random()*10000))+".test.org";
+     private final String subDomain = "subdomain"+((int)(Math.random()*10000));
+     private final InetAddress addrWithGeo = Networking.getLocalHost();
+     private final InetAddress addrWithoutGeo = 
Networking.getInetAddressWithFixedName(StubHostGeoLookup.HOMELESS_IP);
+     
 -    private ManagementContext mgmt;
 -    private TestApplication app;
+     private TestEntity target;
+     private DynamicGroup group;
+     private GeoscalingDnsService geoDns;
+     private String origGeoLookupImpl;
+ 
+     private SshMachineLocation locWithGeo;
+     private SshMachineLocation locWithoutGeo;
 -    
++
++    @Override
+     @BeforeMethod(alwaysRun=true)
+     public void setUp() throws Exception {
++        // Want to load username and password from user's properties.
++        mgmt = 
LocalManagementContextForTests.newInstance(BrooklynProperties.Factory.newDefault());
++        super.setUp();
++
+         origGeoLookupImpl = 
BrooklynSystemProperties.HOST_GEO_LOOKUP_IMPL.getValue();
+         HostGeoInfo.clearCachedLookup();
+ 
 -        app = TestApplication.Factory.newManagedInstanceForTests();
 -        mgmt = app.getManagementContext();
 -        
+         target = 
app.createAndManageChild(EntitySpec.create(TestEntity.class));
 -        
+         group = app.createAndManageChild(EntitySpec.create(DynamicGroup.class)
+                 .configure(DynamicGroup.ENTITY_FILTER, 
Predicates.instanceOf(TestEntity.class)));
 -        
++
++        String username = getBrooklynProperty(mgmt, 
"brooklyn.geoscaling.username");
++        String password = getBrooklynProperty(mgmt, 
"brooklyn.geoscaling.password");
++        if (username == null || password == null) {
++            throw new SkipException("Set brooklyn.geoscaling.username and 
brooklyn.geoscaling.password in brooklyn.properties to enable test");
++        }
++
+         geoDns = 
app.createAndManageChild(EntitySpec.create(GeoscalingDnsService.class)
+                 .displayName("Geo-DNS")
 -                .configure("username", "cloudsoft")
 -                .configure("password", "cl0uds0ft")
 -                .configure("primaryDomainName", primaryDomain)
 -                .configure("smartSubdomainName", subDomain)
 -                .configure("targetEntityProvider", group));
 -        
++                .configure(GeoscalingDnsService.GEOSCALING_USERNAME, username)
++                .configure(GeoscalingDnsService.GEOSCALING_PASSWORD, password)
++                
.configure(GeoscalingDnsService.GEOSCALING_PRIMARY_DOMAIN_NAME, primaryDomain)
++                
.configure(GeoscalingDnsService.GEOSCALING_SMART_SUBDOMAIN_NAME, subDomain)
++                .configure(GeoscalingDnsService.ENTITY_PROVIDER, group));
++
+         locWithGeo = 
mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                 .configure("address", addrWithGeo)
+                 .configure("name", "Edinburgh")
+                 .configure("latitude", 55.94944)
+                 .configure("longitude", -3.16028)
+                 .configure("iso3166", ImmutableList.of("GB-EDH")));
+ 
+         locWithoutGeo = 
mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                 .configure("address", addrWithoutGeo)
+                 .configure("name", "Nowhere"));
+     }
 -    
++
++    @Override
+     @AfterMethod(alwaysRun=true)
+     public void tearDown() throws Exception {
++        super.tearDown();
+         if (origGeoLookupImpl != null) {
+             
System.setProperty(BrooklynSystemProperties.HOST_GEO_LOOKUP_IMPL.getPropertyName(),
 origGeoLookupImpl);
+         } else {
+             
System.clearProperty(BrooklynSystemProperties.HOST_GEO_LOOKUP_IMPL.getPropertyName());
+         }
 -        if (mgmt != null) Entities.destroyAll(mgmt);
+         HostGeoInfo.clearCachedLookup();
+     }
 -    
++
++    private String getBrooklynProperty(ManagementContext mgmt, String 
property) {
++        return ((ManagementContextInternal) 
mgmt).getBrooklynProperties().getFirst(property);
++    }
++
+     @Test(groups={"Integration"})
+     public void testRoutesToExpectedLocation() {
+         // Without this config, running on a home network (i.e. no public IP) 
the entity will have a private IP and will be ignored
 -        
((EntityLocal)geoDns).config().set(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES,
 true);
++        geoDns.config().set(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES, 
true);
+         
+         target.sensors().set(Attributes.HOSTNAME,addrWithGeo.getHostName());
+         
+         app.start(ImmutableList.of(locWithGeo));
+         
+         LOG.info("geo-scaling test, using {}.{}; expect to be wired to {}", 
new Object[] {subDomain, primaryDomain, addrWithGeo});
+         
+         assertTargetHostsEventually(geoDns, 1);
+     }
+     
+     @Test(groups={"Integration"})
+     public void testIgnoresAddressWithoutGeography() throws Exception {
+         
System.setProperty(BrooklynSystemProperties.HOST_GEO_LOOKUP_IMPL.getPropertyName(),
 StubHostGeoLookup.class.getName());
 -        
((EntityLocal)geoDns).config().set(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES,
 false); // false is default
++        geoDns.config().set(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES, 
false); // false is default
+         
+         app.start(ImmutableList.of(locWithoutGeo));
+         target.sensors().set(Attributes.HOSTNAME, 
StubHostGeoLookup.HOMELESS_IP);
+         
+         LOG.info("geo-scaling test, using {}.{}; expect not to be wired to 
{}", new Object[] {subDomain, primaryDomain, addrWithoutGeo});
+         
+         Asserts.succeedsContinually(MutableMap.of("timeout", 10*1000), new 
Runnable() {
+             @Override public void run() {
+                 assertEquals(geoDns.getTargetHosts().size(), 0, 
"targets="+geoDns.getTargetHosts());
+             }
+         });
+     }
+ 
+     @Test(groups={"Integration"})
+     public void testIncludesAddressWithoutGeography() {
+         
System.setProperty(BrooklynSystemProperties.HOST_GEO_LOOKUP_IMPL.getPropertyName(),
 StubHostGeoLookup.class.getName());
 -        
((EntityLocal)geoDns).config().set(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES,
 true);
++        geoDns.config().set(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES, 
true);
+         
+         app.start(ImmutableList.of(locWithoutGeo));
+         target.sensors().set(Attributes.HOSTNAME, 
StubHostGeoLookup.HOMELESS_IP);
+         
+         LOG.info("geo-scaling test, using {}.{}; expect to be wired to {}", 
new Object[] {subDomain, primaryDomain, addrWithoutGeo});
+         
+         assertTargetHostsEventually(geoDns, 1);
+     }
+ 
+     private void assertTargetHostsEventually(final GeoscalingDnsService 
geoDns, final int numExpected) {
+         Asserts.succeedsEventually(new Runnable() {
+             @Override public void run() {
+                 assertEquals(geoDns.getTargetHosts().size(), 1, 
"targets="+geoDns.getTargetHosts());
+             }
+         });
+     }
+     
+     public static class StubHostGeoLookup implements HostGeoLookup {
+         public static final String HOMELESS_IP = "1.2.3.4";
+         private final HostGeoLookup delegate;
+ 
+         public StubHostGeoLookup() throws Exception {
+             this(null);
+         }
+         
+         public StubHostGeoLookup(String delegateImpl) throws Exception {
+             if (delegateImpl == null) {
+                 // don't just call HostGeoInfo.getDefaultLookup; this is the 
default lookup!
+                 if (MaxMind2HostGeoLookup.getDatabaseReader()!=null) {
+                     delegate = new MaxMind2HostGeoLookup();
+                 } else {
+                     delegate = new UtraceHostGeoLookup();
+                 }
+             } else {
+                 delegate = (HostGeoLookup) 
Class.forName(delegateImpl).newInstance();
+             }
+         }
+ 
+         @Override
+         public HostGeoInfo getHostGeoInfo(InetAddress address) throws 
Exception {
+             // Saw strange test failure on jenkins: hence paranoid logging, 
just in case exception is swallowed somehow.
+             try {
+                 HostGeoInfo result;
+                 if (HOMELESS_IP.equals(address.getHostAddress())) {
+                     result = null;
+                 } else {
+                     result = delegate.getHostGeoInfo(address);
+                 }
+                 LOG.info("StubHostGeoLookup.getHostGeoInfo queried: 
address="+address+"; hostAddress="+address.getHostAddress()+"; result="+result);
+                 return result;
+             } catch (Throwable t) {
+                 LOG.error("StubHostGeoLookup.getHostGeoInfo encountered 
problem (rethrowing): address="+address+"; 
hostAddress="+address.getHostAddress(), t);
+                 throw Exceptions.propagate(t);
+             }
+         }
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
----------------------------------------------------------------------
diff --cc 
brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
index 0000000,d29ae5b..325cd81
mode 000000,100644..100644
--- 
a/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
+++ 
b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
@@@ -1,0 -1,178 +1,199 @@@
+ /*
+  * 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.dns.geoscaling;
+ 
+ import static 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.PROVIDE_CITY_INFO;
+ import static 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.PROVIDE_COUNTRY_INFO;
+ import static 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.PROVIDE_EXTRA_INFO;
+ import static 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.PROVIDE_NETWORK_INFO;
+ import static 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.PROVIDE_UPTIME_INFO;
+ import static org.testng.Assert.assertNotNull;
+ import static org.testng.Assert.assertNull;
+ 
++import org.apache.brooklyn.api.mgmt.ManagementContext;
++import org.apache.brooklyn.core.internal.BrooklynProperties;
++import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
++import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport;
++import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+ import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.Domain;
+ import 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.SmartSubdomain;
+ import org.apache.brooklyn.util.http.HttpTool;
+ import org.apache.brooklyn.util.text.Strings;
+ import org.apache.http.client.HttpClient;
++import org.testng.SkipException;
+ import org.testng.annotations.AfterMethod;
+ import org.testng.annotations.BeforeMethod;
+ import org.testng.annotations.Test;
+ 
+ /**
+  * {@link GeoscalingWebClient} unit tests.
+  */
+ /*
+ Exception java.lang.RuntimeException
+ 
+ Message: Failed to log-in to GeoScaling service: 
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
+ Stacktrace:
+ 
+ 
+ at 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.login(GeoscalingWebClient.java:208)
+ at 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClientTest.setUp(GeoscalingWebClientTest.java:64)
+ at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
+ at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
+ at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
+ at java.lang.reflect.Method.invoke(Method.java:606)
+ at 
org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
+ at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:564)
+ at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:213)
+ at org.testng.internal.Invoker.invokeMethod(Invoker.java:653)
+ at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
+ at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
+ at 
org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
+ at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
+ at org.testng.TestRunner.privateRun(TestRunner.java:767)
+ at org.testng.TestRunner.run(TestRunner.java:617)
+ at org.testng.SuiteRunner.runTest(SuiteRunner.java:348)
+ at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343)
+ at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305)
+ at org.testng.SuiteRunner.run(SuiteRunner.java:254)
+ at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
+ at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
+ at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
+ at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
+ at org.testng.TestNG.run(TestNG.java:1057)
+ at 
org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:115)
+ at 
org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:205)
+ at 
org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:108)
+ at 
org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:111)
+ at 
org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203)
+ at 
org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155)
+ at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103)
+ Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: 
handshake_failure
+ at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
+ at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
+ at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1991)
+ at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1098)
+ at 
sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1344)
+ at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1371)
+ at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1355)
+ at 
org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:543)
+ at 
org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:409)
+ at 
org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:177)
+ at 
org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:304)
+ at 
org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:611)
+ at 
org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:446)
+ at 
org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:882)
+ at 
org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
+ at 
org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
+ at 
org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
+ at 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.sendRequest(GeoscalingWebClient.java:438)
+ at 
org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.login(GeoscalingWebClient.java:205)
+ ... 31 more
+  */
+ @Test(groups="Broken", enabled=false)
 -public class GeoscalingWebClientTest {
++public class GeoscalingWebClientTest extends BrooklynMgmtUnitTestSupport {
+     
+     private final static String GEOSCALING_URL = "https://www.geoscaling.com";;
 -    private final static String USERNAME = "cloudsoft";
 -    private final static String PASSWORD = "cl0uds0ft";
 -    
+     private final static String PRIMARY_DOMAIN = "domain-" + 
Strings.makeRandomId(5) + ".test.org";
+     private final static String SUBDOMAIN = "subdomain-" + 
Strings.makeRandomId(5);
+     
+     private final static String DEFAULT_SCRIPT = "output[] = 
array(\"fail\");";
+     
+     private GeoscalingWebClient geoscaling;
+     
+     private Domain domain;
+     private SmartSubdomain smartSubdomain;
 -    
 -    @BeforeMethod(alwaysRun=true)
 -    public void setUp() {
++
++    @Override
++    @BeforeMethod(alwaysRun = true)
++    public void setUp() throws Exception {
++        // Want to load username and password from user's properties.
++        mgmt = 
LocalManagementContextForTests.newInstance(BrooklynProperties.Factory.newDefault());
++
++        String username = getBrooklynProperty(mgmt, 
"brooklyn.geoscaling.username");
++        String password = getBrooklynProperty(mgmt, 
"brooklyn.geoscaling.password");
++        if (username == null || password == null) {
++            throw new SkipException("Set brooklyn.geoscaling.username and 
brooklyn.geoscaling.password in brooklyn.properties to enable test");
++        }
++
+         // Insecurely use "trustAll" so that don't need to import signature 
into trust store
+         // before test will work on jenkins machine.
+         HttpClient httpClient = 
HttpTool.httpClientBuilder().uri(GEOSCALING_URL).trustAll().build();
+         geoscaling = new GeoscalingWebClient(httpClient);
 -        geoscaling.login(USERNAME, PASSWORD);
++        geoscaling.login(username, password);
++        super.setUp();
+     }
+     
+     @AfterMethod(alwaysRun=true)
 -    public void tearDown() {
++    public void tearDown() throws Exception {
+         if (smartSubdomain != null)
+             smartSubdomain.delete();
+         
+         if (domain != null)
+             domain.delete();
+         
+         if (geoscaling != null)
+             geoscaling.logout();
++
++        super.tearDown();
++
+     }
 -    
++
++    private String getBrooklynProperty(ManagementContext mgmt, String 
property) {
++        return ((ManagementContextInternal) 
mgmt).getBrooklynProperties().getFirst(property);
++    }
++
+     @Test(groups = "Integration")
+     public void testSimpleNames() {
+         testWebClient(PRIMARY_DOMAIN, SUBDOMAIN);
+     }
+     
+     @Test(groups = "Integration")
+     public void testMixedCaseNames() {
+         testWebClient("MixedCase-"+PRIMARY_DOMAIN, "MixedCase-"+SUBDOMAIN);
+     }
+     
+     public void testWebClient(String primaryDomainName, String 
smartSubdomainName) {
+         assertNull(geoscaling.getPrimaryDomain(primaryDomainName));
+         geoscaling.createPrimaryDomain(primaryDomainName);
+         domain = geoscaling.getPrimaryDomain(primaryDomainName);
+         assertNotNull(domain);
+         
+         assertNull(domain.getSmartSubdomain(smartSubdomainName));
+         domain.createSmartSubdomain(smartSubdomainName);
+         smartSubdomain = domain.getSmartSubdomain(smartSubdomainName);
+         assertNotNull(smartSubdomain);
+         
+         smartSubdomain.configure(
+             PROVIDE_NETWORK_INFO | PROVIDE_CITY_INFO | PROVIDE_COUNTRY_INFO | 
PROVIDE_EXTRA_INFO | PROVIDE_UPTIME_INFO,
+             DEFAULT_SCRIPT);
+         
+         // TODO: read-back config and verify is as expected?
+         // TODO: send actual config, test ping/dig from multiple locations?
+         // TODO: rename subdomain
+         
+         smartSubdomain.delete();
+         assertNull(domain.getSmartSubdomain(smartSubdomainName));
+         
+         domain.delete();
+         assertNull(geoscaling.getPrimaryDomain(primaryDomainName));
+         
+         geoscaling.logout();
+     }
+     
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
----------------------------------------------------------------------
diff --cc 
brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
index 0000000,728acae..ef752dd
mode 000000,100644..100644
--- 
a/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
+++ 
b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/AbstractWebAppFixtureIntegrationTest.java
@@@ -1,0 -1,501 +1,539 @@@
+ /*
+  * 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.webapp;
+ 
+ import static org.apache.brooklyn.test.HttpTestUtils.connectToUrl;
+ import static org.testng.Assert.assertEquals;
+ import static org.testng.Assert.assertFalse;
+ import static org.testng.Assert.assertNotNull;
+ import static org.testng.Assert.assertTrue;
+ 
+ import java.io.File;
+ import java.io.FileOutputStream;
+ import java.net.HttpURLConnection;
+ import java.net.URL;
+ import java.net.URLConnection;
+ import java.security.KeyStore;
+ import java.security.cert.Certificate;
+ import java.util.List;
+ import java.util.concurrent.Callable;
+ import java.util.concurrent.CopyOnWriteArrayList;
+ import java.util.concurrent.TimeUnit;
+ import java.util.concurrent.atomic.AtomicInteger;
+ 
 -import org.slf4j.Logger;
 -import org.slf4j.LoggerFactory;
 -import org.testng.annotations.AfterClass;
 -import org.testng.annotations.AfterMethod;
 -import org.testng.annotations.BeforeMethod;
 -import org.testng.annotations.DataProvider;
 -import org.testng.annotations.Test;
 -import 
org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+ import org.apache.brooklyn.api.entity.Application;
+ import org.apache.brooklyn.api.entity.Entity;
+ import org.apache.brooklyn.api.entity.EntityLocal;
 -import org.apache.brooklyn.api.entity.drivers.DriverDependentEntity;
+ import org.apache.brooklyn.api.location.LocationSpec;
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
 -import org.apache.brooklyn.api.mgmt.SubscriptionContext;
+ import org.apache.brooklyn.api.mgmt.SubscriptionHandle;
++import org.apache.brooklyn.api.sensor.AttributeSensor;
++import org.apache.brooklyn.api.sensor.Sensor;
+ import org.apache.brooklyn.api.sensor.SensorEvent;
+ import org.apache.brooklyn.api.sensor.SensorEventListener;
+ import org.apache.brooklyn.core.entity.Entities;
 -import org.apache.brooklyn.core.entity.EntityInternal;
++import org.apache.brooklyn.core.entity.EntityAsserts;
+ import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
+ import org.apache.brooklyn.core.entity.trait.Startable;
+ import org.apache.brooklyn.core.internal.BrooklynProperties;
+ import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+ import org.apache.brooklyn.core.test.entity.TestApplication;
+ import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 -import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
 -import org.apache.brooklyn.entity.webapp.JavaWebAppService;
 -import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
 -import org.apache.brooklyn.entity.webapp.WebAppService;
 -import org.apache.brooklyn.entity.webapp.WebAppServiceMethods;
++import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
++import 
org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
+ import org.apache.brooklyn.test.Asserts;
+ import org.apache.brooklyn.test.EntityTestUtils;
+ import org.apache.brooklyn.test.HttpTestUtils;
+ import org.apache.brooklyn.test.support.TestResourceUnavailableException;
+ import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.core.crypto.FluentKeySigner;
+ import org.apache.brooklyn.util.core.crypto.SecureKeys;
+ import org.apache.brooklyn.util.net.Urls;
+ import org.apache.brooklyn.util.stream.Streams;
+ import org.apache.brooklyn.util.time.Time;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++import org.testng.annotations.AfterClass;
++import org.testng.annotations.AfterMethod;
++import org.testng.annotations.BeforeMethod;
++import org.testng.annotations.DataProvider;
++import org.testng.annotations.Test;
+ 
++import com.google.common.base.Predicate;
+ import com.google.common.base.Stopwatch;
+ import com.google.common.collect.ImmutableList;
+ import com.google.common.collect.ImmutableSet;
+ import com.google.common.collect.Lists;
+ 
+ /**
+  * Test fixture for implementations of JavaWebApp, checking start up and 
shutdown, 
+  * post request and error count metrics and deploy wars, etc.
+  */
+ public abstract class AbstractWebAppFixtureIntegrationTest {
+     
+     private static final Logger log = 
LoggerFactory.getLogger(AbstractWebAppFixtureIntegrationTest.class);
+     
+     // Don't use 8080 since that is commonly used by testing software
+     public static final String DEFAULT_HTTP_PORT = "7880+";
+     
+     // Port increment for JBoss 6.
+     public static final int PORT_INCREMENT = 400;
+ 
+     // The parent application entity for these tests
+     protected ManagementContext mgmt;
+     protected List<TestApplication> applications = Lists.newArrayList();
+     protected SoftwareProcess entity;
+     protected LocalhostMachineProvisioningLocation loc;
+ 
+     protected synchronized ManagementContext getMgmt() {
+         if (mgmt==null)
+             mgmt = new 
LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
+         return mgmt;
+     }
+     
+     @BeforeMethod(alwaysRun=true)
+     public void setUp() throws Exception {
+         loc = 
getMgmt().getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class));
+     }
+     
+     /*
+      * Use of @DataProvider with test methods gives surprising behaviour with 
@AfterMethod.
+      * Unless careful, this causes problems when trying to ensure everything 
is shutdown cleanly.
+      *
+      * Empirically, the rules seem to be...
+      *  - @DataProvider method is called first; it creates a bunch of cases 
to run 
+      *    (all sharing the same instance of WebAppIntegrationTest).
+      *  - It runs the test method for the first time with the first 
@DataProvider value
+      *  - It runs @AfterMethod
+      *  - It runs the test method for the second @DataProvider value
+      *  - It runs @AfterMethod
+      *  - etc...
+      *
+      * Previously shutdownApp was calling stop on each app in applications, 
and clearing the applications set;
+      * but then the second invocation of the method was starting an entity 
that was never stopped. Until recently,
+      * every test method was also terminating the entity (belt-and-braces, 
but also brittle for if the method threw
+      * an exception earlier). When that "extra" termination was removed, it 
meant the second and subsequent 
+      * entities were never being stopped.
+      *
+      * Now we rely on having the test method set the entity field, so we can 
find out which application instance 
+      * it is and calling stop on just that app + entity.
+      */
+     @AfterMethod(alwaysRun=true)
+     public void shutdownApp() {
+         if (entity != null) {
+             Application app = entity.getApplication();
+             if (app != null) Entities.destroy(app);
+         }
+     }
+ 
+     @AfterClass(alwaysRun=true)
+     public synchronized void shutdownMgmt() {
+         try {
+             if (mgmt != null) Entities.destroyAll(mgmt);
+         } finally {
+             mgmt = null;
+         }
+     }
+ 
+     public static File createTemporaryKeyStore(String alias, String password) 
throws Exception {
+         FluentKeySigner signer = new 
FluentKeySigner("brooklyn-test").selfsign();
+ 
+         KeyStore ks = SecureKeys.newKeyStore();
+         ks.setKeyEntry(
+                 alias,
+                 signer.getKey().getPrivate(),
+                 password.toCharArray(),
+                 new Certificate[]{signer.getAuthorityCertificate()});
+ 
+         File file = File.createTempFile("test", "keystore");
+         FileOutputStream fos = new FileOutputStream(file);
+         try {
+             ks.store(fos, password.toCharArray());
+             return file;
+         } finally {
+             Streams.closeQuietly(fos);
+         }
+     }
+ 
+     /** 
+      * Create a new instance of TestApplication and append it to applications 
list
+      * so it can be terminated suitable after each test has run.
+      * @return
+      */
+     protected TestApplication newTestApplication() {
+         TestApplication ta = 
ApplicationBuilder.newManagedApp(TestApplication.class, getMgmt());
+         applications.add(ta);
+         return ta;
+     }
+ 
+     /**
+      * Provides instances of the WebAppServer to test
+      * (arrays of 1-element array arguments to some of the other methods) 
+      *
+      * NB annotation must be placed on concrete impl method
+      * 
+      * TODO combine the data provider here with live integration test
+      * @see WebAppLiveIntegrationTest#basicEntities()
+      */
+     @DataProvider(name = "basicEntities")
+     public abstract Object[][] basicEntities();
+ 
+     /**
+      * Checks an entity can start, set SERVICE_UP to true and shutdown again.
+      */
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     public void canStartAndStop(final SoftwareProcess entity) {
+         this.entity = entity;
+         log.info("test=canStartAndStop; entity="+entity+"; 
app="+entity.getApplication());
+         
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
 -        Asserts.succeedsEventually(MutableMap.of("timeout", 120*1000), new 
Runnable() {
 -            public void run() {
 -                assertTrue(entity.getAttribute(Startable.SERVICE_UP));
 -            }});
 -        
++        EntityAsserts.assertAttributeEqualsEventually(
++                MutableMap.of("timeout", 120*1000), entity, 
Startable.SERVICE_UP, Boolean.TRUE);
+         entity.stop();
+         assertFalse(entity.getAttribute(Startable.SERVICE_UP));
+     }
+     
+     /**
+      * Checks an entity can start, set SERVICE_UP to true and shutdown again.
+      */
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     public void testReportsServiceDownWhenKilled(final SoftwareProcess 
entity) throws Exception {
+         this.entity = entity;
+         log.info("test=testReportsServiceDownWithKilled; entity="+entity+"; 
app="+entity.getApplication());
+         
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
 -        
EntityTestUtils.assertAttributeEqualsEventually(MutableMap.of("timeout", 
120*1000), entity, Startable.SERVICE_UP, true);
++        
EntityAsserts.assertAttributeEqualsEventually(MutableMap.of("timeout", 
120*1000), entity, Startable.SERVICE_UP, true);
+ 
+         // Stop the underlying entity, but without our entity instance being 
told!
+         killEntityBehindBack(entity);
+         log.info("Killed {} behind mgmt's back, waiting for service up false 
in mgmt context", entity);
+         
 -        EntityTestUtils.assertAttributeEqualsEventually(entity, 
Startable.SERVICE_UP, false);
++        EntityAsserts.assertAttributeEqualsEventually(entity, 
Startable.SERVICE_UP, false);
+         
+         log.info("success getting service up false in primary mgmt universe");
+     }
+     
+     /**
+      * Stop the given underlying entity, but without our entity instance 
being told!
+      */
+     protected void killEntityBehindBack(Entity tokill) throws Exception {
 -        ((SoftwareProcessDriver)((DriverDependentEntity<?>) 
Entities.deproxy(entity)).getDriver()).stop();
++        ((SoftwareProcessImpl) Entities.deproxy(tokill)).getDriver().stop();
+         // old method of doing this did some dodgy legacy rebind and failed 
due to too many dangling refs; above is better in any case
+         // but TODO we should have some rebind tests for these!
+     }
 -    
+     /**
+      * Checks that an entity correctly sets request and error count metrics by
+      * connecting to a non-existent URL several times.
+      */
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     public void publishesRequestAndErrorCountMetrics(final SoftwareProcess 
entity) throws Exception {
+         this.entity = entity;
+         log.info("test=publishesRequestAndErrorCountMetrics; 
entity="+entity+"; app="+entity.getApplication());
+         
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
 -        
 -        Asserts.succeedsEventually(MutableMap.of("timeout", 10*1000), new 
Runnable() {
 -            public void run() {
 -                assertTrue(entity.getAttribute(SoftwareProcess.SERVICE_UP));
 -            }});
 -        
++        EntityAsserts.assertAttributeEqualsEventually(
++                MutableMap.of("timeout", 120 * 1000), entity, 
Startable.SERVICE_UP, Boolean.TRUE);
++
+         String url = entity.getAttribute(WebAppService.ROOT_URL) + 
"does_not_exist";
 -        
++
+         final int n = 10;
+         for (int i = 0; i < n; i++) {
+             URLConnection connection = HttpTestUtils.connectToUrl(url);
+             int status = ((HttpURLConnection) connection).getResponseCode();
+             log.info("connection to {} gives {}", url, status);
+         }
+         
+         Asserts.succeedsEventually(MutableMap.of("timeout", 20*1000), new 
Runnable() {
+             public void run() {
+                 Integer requestCount = 
entity.getAttribute(WebAppService.REQUEST_COUNT);
+                 Integer errorCount = 
entity.getAttribute(WebAppService.ERROR_COUNT);
+                 log.info("req={}, err={}", requestCount, errorCount);
+                 
+                 assertNotNull(errorCount, "errorCount not set yet 
("+errorCount+")");
+     
+                 // AS 7 seems to take a very long time to report error counts,
+                 // hence not using ==.  >= in case error pages include a 
favicon, etc.
+                 assertEquals(errorCount, (Integer)n);
+                 assertTrue(requestCount >= errorCount);
+             }});
+     }
+     
+     /**
+      * Checks an entity publishes correct requests/second figures and that 
these figures
+      * fall to zero after a period of no activity.
+      */
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     public void publishesRequestsPerSecondMetric(final SoftwareProcess 
entity) throws Exception {
+         this.entity = entity;
+         log.info("test=publishesRequestsPerSecondMetric; entity="+entity+"; 
app="+entity.getApplication());
+         
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
+ 
+         log.info("Entity "+entity+" started");
+         
+         try {
+             // reqs/sec initially zero
+             log.info("Waiting for initial avg-requests to be zero...");
+             Asserts.succeedsEventually(MutableMap.of("timeout", 20*1000), new 
Runnable() {
+                 public void run() {
+                     Double activityValue = 
entity.getAttribute(WebAppService.REQUESTS_PER_SECOND_IN_WINDOW);
+                     assertNotNull(activityValue, "activity not set yet 
"+activityValue+")");
+                     assertEquals(activityValue.doubleValue(), 0.0d, 
0.000001d);
+                 }});
+             
+             // apply workload on 1 per sec; reqs/sec should update
+             Asserts.succeedsEventually(MutableMap.of("timeout", 30*1000), new 
Callable<Void>() {
+                 public Void call() throws Exception {
+                     String url = entity.getAttribute(WebAppService.ROOT_URL) 
+ "does_not_exist";
+                     final int desiredMsgsPerSec = 10;
+                     
+                     Stopwatch stopwatch = Stopwatch.createStarted();
+                     final AtomicInteger reqsSent = new AtomicInteger();
+                     final Integer preRequestCount = 
entity.getAttribute(WebAppService.REQUEST_COUNT);
+                     
+                     // need to maintain n requests per second for the 
duration of the window size
+                     log.info("Applying load for 
"+WebAppServiceMethods.DEFAULT_WINDOW_DURATION);
+                     while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < 
WebAppServiceMethods.DEFAULT_WINDOW_DURATION.toMilliseconds()) {
+                         long preReqsTime = 
stopwatch.elapsed(TimeUnit.MILLISECONDS);
+                         for (int i = 0; i < desiredMsgsPerSec; i++) { 
connectToUrl(url); }
+                         Time.sleep(1000 - 
(stopwatch.elapsed(TimeUnit.MILLISECONDS)-preReqsTime));
+                         reqsSent.addAndGet(desiredMsgsPerSec);
+                     }
+     
+                     Asserts.succeedsEventually(MutableMap.of("timeout", 
4000), new Runnable() {
+                         public void run() {
+                             Double avgReqs = 
entity.getAttribute(WebAppService.REQUESTS_PER_SECOND_IN_WINDOW);
+                             Integer requestCount = 
entity.getAttribute(WebAppService.REQUEST_COUNT);
+                             
+                             log.info("avg-requests="+avgReqs+"; 
total-requests="+requestCount);
+                             assertEquals(avgReqs.doubleValue(), 
(double)desiredMsgsPerSec, 3.0d);
+                             assertEquals(requestCount.intValue(), 
preRequestCount+reqsSent.get());
+                         }});
+                     
+                     return null;
+                 }});
+             
+             // After suitable delay, expect to again get zero msgs/sec
+             log.info("Waiting for avg-requests to drop to zero, for 
"+WebAppServiceMethods.DEFAULT_WINDOW_DURATION);
+             
Thread.sleep(WebAppServiceMethods.DEFAULT_WINDOW_DURATION.toMilliseconds());
+             
+             Asserts.succeedsEventually(MutableMap.of("timeout", 10*1000), new 
Runnable() {
+                 public void run() {
+                     Double avgReqs = 
entity.getAttribute(WebAppService.REQUESTS_PER_SECOND_IN_WINDOW);
+                     assertNotNull(avgReqs);
+                     assertEquals(avgReqs.doubleValue(), 0.0d, 0.00001d);
+                 }});
+         } finally {
+             entity.stop();
+         }
+     }
+ 
+     /**
+      * Tests that we get consecutive events with zero workrate, and with 
suitably small timestamps between them.
+      */
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     @SuppressWarnings("rawtypes")
+     public void publishesZeroRequestsPerSecondMetricRepeatedly(final 
SoftwareProcess entity) {
+         this.entity = entity;
+         log.info("test=publishesZeroRequestsPerSecondMetricRepeatedly; 
entity="+entity+"; app="+entity.getApplication());
+         
 -        final int MAX_INTERVAL_BETWEEN_EVENTS = 4000; // TomcatServerImpl 
publishes events every 3000ms so this should be enough overhead
 -        final int NUM_CONSECUTIVE_EVENTS = 3;
++        final int maxIntervalBetweenEvents = 4000; // TomcatServerImpl 
publishes events every 3000ms so this should be enough overhead
++        final int consecutiveEvents = 3;
+ 
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
 -        
+         SubscriptionHandle subscriptionHandle = null;
 -
++        final CopyOnWriteArrayList<SensorEvent<Double>> events = new 
CopyOnWriteArrayList<>();
+         try {
 -            final List<SensorEvent> events = new 
CopyOnWriteArrayList<SensorEvent>();
 -            subscriptionHandle = entity.subscriptions().subscribe(entity, 
WebAppService.REQUESTS_PER_SECOND_IN_WINDOW, new SensorEventListener<Double>() {
 -                public void onEvent(SensorEvent<Double> event) {
 -                    
log.info("publishesRequestsPerSecondMetricRepeatedly.onEvent: {}", event);
 -                    events.add(event);
 -                }});
 -            
 -            
 -            Asserts.succeedsEventually(new Runnable() {
 -                public void run() {
 -                    assertTrue(events.size() > NUM_CONSECUTIVE_EVENTS, 
"events "+events.size()+" > "+NUM_CONSECUTIVE_EVENTS);
 -                    long eventTime = 0;
 -                    
 -                    for (SensorEvent event : 
events.subList(events.size()-NUM_CONSECUTIVE_EVENTS, events.size())) {
 -                        assertEquals(event.getSource(), entity);
 -                        assertEquals(event.getSensor(), 
WebAppService.REQUESTS_PER_SECOND_IN_WINDOW);
 -                        assertEquals(event.getValue(), 0.0d);
 -                        if (eventTime > 0) 
assertTrue(event.getTimestamp()-eventTime < MAX_INTERVAL_BETWEEN_EVENTS,
 -                            "events at "+eventTime+" and 
"+event.getTimestamp()+" exceeded maximum allowable interval 
"+MAX_INTERVAL_BETWEEN_EVENTS);
 -                        eventTime = event.getTimestamp();
 -                    }
++            subscriptionHandle = recordEvents(entity, 
WebAppService.REQUESTS_PER_SECOND_IN_WINDOW, events);
++            Asserts.succeedsEventually(assertConsecutiveSensorEventsEqual(
++                    events, WebAppService.REQUESTS_PER_SECOND_IN_WINDOW, 
0.0d, consecutiveEvents, maxIntervalBetweenEvents));
++        } finally {
++            if (subscriptionHandle != null) 
entity.subscriptions().unsubscribe(subscriptionHandle);
++            entity.stop();
++        }
++    }
++
++    /**
++     * Tests that requests/sec last and windowed decay when the entity can't 
be contacted for
++     * up to date values.
++     */
++    @Test(groups = "Integration", dataProvider = "basicEntities")
++    public void testRequestCountContinuallyPublishedWhenEntityKilled(final 
SoftwareProcess entity) throws Exception {
++        this.entity = entity;
++        log.info("test=testRequestCountContinuallyPublishedWhenEntityKilled; 
entity="+entity+"; app="+entity.getApplication());
++
++        Entities.start(entity.getApplication(), ImmutableList.of(loc));
++        EntityAsserts.assertAttributeEqualsEventually(entity, 
SoftwareProcess.SERVICE_UP, Boolean.TRUE);
++        String url = entity.getAttribute(WebAppService.ROOT_URL) + 
"does_not_exist";
++
++        // Apply load to entity. Assert enriched sensor values.
++        HttpTestUtils.connectToUrl(url);
++        EntityAsserts.assertAttributeEventually(entity, 
WebAppServiceMetrics.REQUEST_COUNT, new Predicate<Integer>() {
++                @Override public boolean apply(Integer input) {
++                    return input > 0;
+                 }});
++        killEntityBehindBack(entity);
++
++        final int requestCountAfterKilled = 
entity.sensors().get(WebAppServiceMetrics.REQUEST_COUNT);
++        final int maxIntervalBetweenEvents = 4000; // TomcatServerImpl 
publishes events every 3000ms so this should be enough overhead
++        final int consecutiveEvents = 3;
++
++        // The entity should be configured to keep publishing request count, 
so
++        SubscriptionHandle subscriptionHandle = null;
++        final CopyOnWriteArrayList<SensorEvent<Integer>> events = new 
CopyOnWriteArrayList<>();
++        try {
++            subscriptionHandle = recordEvents(entity, 
WebAppServiceMetrics.REQUEST_COUNT, events);
++            Asserts.succeedsEventually(assertConsecutiveSensorEventsEqual(
++                    events, WebAppServiceMetrics.REQUEST_COUNT, 
requestCountAfterKilled, consecutiveEvents, maxIntervalBetweenEvents));
+         } finally {
+             if (subscriptionHandle != null) 
entity.subscriptions().unsubscribe(subscriptionHandle);
+             entity.stop();
+         }
+     }
+ 
++    protected <T> SubscriptionHandle recordEvents(Entity entity, 
AttributeSensor<T> sensor, final List<SensorEvent<T>> events) {
++        SensorEventListener<T> listener = new SensorEventListener<T>() {
++            @Override public void onEvent(SensorEvent<T> event) {
++                log.info("onEvent: {}", event);
++                events.add(event);
++            }
++        };
++        return entity.subscriptions().subscribe(entity, sensor, listener);
++    }
++
++    protected <T> Runnable assertConsecutiveSensorEventsEqual(final 
List<SensorEvent<T>> events,
++                final Sensor<T> sensor, final T expectedValue,
++                final int numConsecutiveEvents, final int 
maxIntervalBetweenEvents) {
++        return new Runnable() {
++            @Override public void run() {
++                assertTrue(events.size() > numConsecutiveEvents, "events " + 
events.size() + " > " + numConsecutiveEvents);
++                long eventTime = 0;
++
++                for (SensorEvent event : events.subList(events.size() - 
numConsecutiveEvents, events.size())) {
++                    assertEquals(event.getSource(), entity);
++                    assertEquals(event.getSensor(), sensor);
++                    assertEquals(event.getValue(), expectedValue);
++                    if (eventTime > 0) assertTrue(event.getTimestamp() - 
eventTime < maxIntervalBetweenEvents,
++                            "events at " + eventTime + " and " + 
event.getTimestamp() + " exceeded maximum allowable interval " + 
maxIntervalBetweenEvents);
++                    eventTime = event.getTimestamp();
++                }
++            }
++        };
++    }
++
+     /**
+      * Twins the entities given by basicEntities() with links to WAR files
+      * they should be able to deploy.  Correct deployment can be checked by
+      * pinging the given URL.
+      *
+      * Everything can deploy hello world. Some subclasses deploy add'l apps.
+      * We're using the simplest hello-world (with no URL mapping) because 
JBoss 6 does not
+      * support URL mappings.
+      */
+     @DataProvider(name = "entitiesWithWarAndURL")
+     public Object[][] entitiesWithWar() {
+         
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
"/hello-world-no-mapping.war");
+         List<Object[]> result = Lists.newArrayList();
+         
+         for (Object[] entity : basicEntities()) {
+             result.add(new Object[] {
+                     entity[0],
+                     "hello-world-no-mapping.war",
+                     "hello-world-no-mapping/",
+                     "" // no sub-page path
+                     });
+         }
+         
+         return result.toArray(new Object[][] {});
+     }
+ 
+     /**
+      * Tests given entity can deploy the given war.  Checks given httpURL to 
confirm success.
+      */
+     @Test(groups = "Integration", dataProvider = "entitiesWithWarAndURL")
+     public void initialRootWarDeployments(final SoftwareProcess entity, final 
String war, 
+             final String urlSubPathToWebApp, final String 
urlSubPathToPageToQuery) {
+         this.entity = entity;
+         log.info("test=initialRootWarDeployments; entity="+entity+"; 
app="+entity.getApplication());
+         
+         URL resource = getClass().getClassLoader().getResource(war);
+         assertNotNull(resource);
+         
+         ((EntityLocal)entity).config().set(JavaWebAppService.ROOT_WAR, 
resource.toString());
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
+         
+         //tomcat may need a while to unpack everything
+         Asserts.succeedsEventually(MutableMap.of("timeout", 60*1000), new 
Runnable() {
+             public void run() {
+                 // TODO get this URL from a WAR file entity
+                 
HttpTestUtils.assertHttpStatusCodeEquals(Urls.mergePaths(entity.getAttribute(WebAppService.ROOT_URL),
 urlSubPathToPageToQuery), 200);
+                 
+                 
assertEquals(entity.getAttribute(JavaWebAppSoftwareProcess.DEPLOYED_WARS), 
ImmutableSet.of("/"));
+             }});
+     }
+     
+     @Test(groups = "Integration", dataProvider = "entitiesWithWarAndURL")
+     public void initialNamedWarDeployments(final SoftwareProcess entity, 
final String war, 
+             final String urlSubPathToWebApp, final String 
urlSubPathToPageToQuery) {
+         this.entity = entity;
+         log.info("test=initialNamedWarDeployments; entity="+entity+"; 
app="+entity.getApplication());
+         
+         URL resource = getClass().getClassLoader().getResource(war);
+         assertNotNull(resource);
+         
+         ((EntityLocal)entity).config().set(JavaWebAppService.NAMED_WARS, 
ImmutableList.of(resource.toString()));
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
+ 
+         Asserts.succeedsEventually(MutableMap.of("timeout", 60*1000), new 
Runnable() {
+             public void run() {
+                 // TODO get this URL from a WAR file entity
+                 
HttpTestUtils.assertHttpStatusCodeEquals(Urls.mergePaths(entity.getAttribute(WebAppService.ROOT_URL),
 urlSubPathToWebApp, urlSubPathToPageToQuery), 200);
+             }});
+     }
+     
+     @Test(groups = "Integration", dataProvider = "entitiesWithWarAndURL")
+     public void testWarDeployAndUndeploy(final JavaWebAppSoftwareProcess 
entity, final String war, 
+             final String urlSubPathToWebApp, final String 
urlSubPathToPageToQuery) {
+         this.entity = entity;
+         log.info("test=testWarDeployAndUndeploy; entity="+entity+"; 
app="+entity.getApplication());
+         
+         URL resource = getClass().getClassLoader().getResource(war);;
+         assertNotNull(resource);
+         
+         Entities.start(entity.getApplication(), ImmutableList.of(loc));
+         
+         // Test deploying
+         entity.deploy(resource.toString(), "myartifactname.war");
+         Asserts.succeedsEventually(MutableMap.of("timeout", 60*1000), new 
Runnable() {
+             public void run() {
+                 // TODO get this URL from a WAR file entity
+                 
HttpTestUtils.assertHttpStatusCodeEquals(Urls.mergePaths(entity.getAttribute(WebAppService.ROOT_URL),
 "myartifactname/", urlSubPathToPageToQuery), 200);
+                 
assertEquals(entity.getAttribute(JavaWebAppSoftwareProcess.DEPLOYED_WARS), 
ImmutableSet.of("/myartifactname"));
+             }});
+         
+         // And undeploying
+         entity.undeploy("/myartifactname");
+         Asserts.succeedsEventually(MutableMap.of("timeout", 60*1000), new 
Runnable() {
+             public void run() {
+                 // TODO get this URL from a WAR file entity
+                 
HttpTestUtils.assertHttpStatusCodeEquals(Urls.mergePaths(entity.getAttribute(WebAppService.ROOT_URL),
 "myartifactname", urlSubPathToPageToQuery), 404);
+                 
assertEquals(entity.getAttribute(JavaWebAppSoftwareProcess.DEPLOYED_WARS), 
ImmutableSet.of());
+             }});
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jetty/JettyWebAppFixtureIntegrationTest.java
----------------------------------------------------------------------
diff --cc 
brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jetty/JettyWebAppFixtureIntegrationTest.java
index 0000000,f8d83c5..38b242b
mode 000000,100644..100644
--- 
a/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jetty/JettyWebAppFixtureIntegrationTest.java
+++ 
b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/webapp/jetty/JettyWebAppFixtureIntegrationTest.java
@@@ -1,0 -1,67 +1,59 @@@
+ /*
+  * 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.webapp.jetty;
+ 
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.core.location.PortRanges;
+ import org.apache.brooklyn.core.test.entity.TestApplication;
+ import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+ import org.apache.brooklyn.entity.webapp.AbstractWebAppFixtureIntegrationTest;
+ import org.apache.brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
+ import org.testng.annotations.DataProvider;
+ import org.testng.annotations.Test;
+ 
+ public class JettyWebAppFixtureIntegrationTest extends 
AbstractWebAppFixtureIntegrationTest {
+ 
+     // FIXME Fails with this is in the jetty log:
+     //     Caused by: java.lang.ClassNotFoundException: 
mx4j.tools.adaptor.http.HttpAdaptor
+ 
+     @Test(groups = "Integration", dataProvider = "basicEntities")
+     public void canStartAndStop(final SoftwareProcess entity) {
+         super.canStartAndStop(entity);
+     }
+     
+     @DataProvider(name = "basicEntities")
+     public Object[][] basicEntities() {
+         TestApplication jettyApp = newTestApplication();
+         Jetty6Server jetty = 
jettyApp.createAndManageChild(EntitySpec.create(Jetty6Server.class)
+                 .configure(Jetty6Server.HTTP_PORT, 
PortRanges.fromString(DEFAULT_HTTP_PORT)));
+         
+         return new JavaWebAppSoftwareProcess[][] {
+                 new JavaWebAppSoftwareProcess[] {jetty}
+         };
+     }
+ 
+     // to be able to test on this class in Eclipse IDE
+     @Override
+     @Test(groups = "Integration", dataProvider = "entitiesWithWarAndURL")
+     public void testWarDeployAndUndeploy(JavaWebAppSoftwareProcess entity, 
String war, String urlSubPathToWebApp,
+             String urlSubPathToPageToQuery) {
+         super.testWarDeployAndUndeploy(entity, war, urlSubPathToWebApp, 
urlSubPathToPageToQuery);
+     }
 -    
 -    public static void main(String ...args) throws Exception {
 -        JettyWebAppFixtureIntegrationTest t = new 
JettyWebAppFixtureIntegrationTest();
 -        t.setUp();
 -        t.canStartAndStop((SoftwareProcess) t.basicEntities()[0][0]);
 -        t.shutdownApp();
 -        t.shutdownMgmt();
 -    }
+ 
+ }

Reply via email to