You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/08/12 17:55:25 UTC

[07/35] incubator-brooklyn git commit: [BROOKLYN-162] package rename to org.apache.brooklyn: software/webapp

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerEc2LiveTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerEc2LiveTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerEc2LiveTest.java
deleted file mode 100644
index b8ffa32..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerEc2LiveTest.java
+++ /dev/null
@@ -1,68 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import static org.testng.Assert.assertNotNull;
-
-import org.testng.annotations.Test;
-
-import brooklyn.entity.AbstractEc2LiveTest;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.tomcat.Tomcat8Server;
-import brooklyn.location.Location;
-import brooklyn.test.Asserts;
-
-import org.apache.brooklyn.test.HttpTestUtils;
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * A simple test of installing+running on AWS-EC2, using various OS distros and versions. 
- */
-public class Tomcat8ServerEc2LiveTest extends AbstractEc2LiveTest {
-
-    public String getTestWar() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
-        return "classpath://hello-world.war";
-    }
-
-    @Override
-    protected void doTest(Location loc) throws Exception {
-        final Tomcat8Server server = app.createAndManageChild(EntitySpec.create(Tomcat8Server.class)
-                .configure("war", getTestWar()));
-        
-        app.start(ImmutableList.of(loc));
-        
-        String url = server.getAttribute(Tomcat8Server.ROOT_URL);
-        
-        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(url, 200);
-        HttpTestUtils.assertContentContainsText(url, "Hello");
-        
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertNotNull(server.getAttribute(Tomcat8Server.REQUEST_COUNT));
-                assertNotNull(server.getAttribute(Tomcat8Server.ERROR_COUNT));
-                assertNotNull(server.getAttribute(Tomcat8Server.TOTAL_PROCESSING_TIME));
-            }});
-    }
-    
-    @Test(enabled=false)
-    public void testDummy() {} // Convince testng IDE integration that this really does have test methods  
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerRestartIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerRestartIntegrationTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerRestartIntegrationTest.java
deleted file mode 100644
index d84c8a0..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerRestartIntegrationTest.java
+++ /dev/null
@@ -1,45 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.AbstractSoftwareProcessRestartIntegrationTest;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.tomcat.Tomcat8Server;
-
-/**
- * Tests restart of the software *process* (as opposed to the VM).
- */
-@Test(groups="Integration")
-public class Tomcat8ServerRestartIntegrationTest extends AbstractSoftwareProcessRestartIntegrationTest {
-    
-    // TODO Remove duplication from MySqlRestartIntegrationTest
-    
-    @SuppressWarnings("unused")
-    private static final Logger LOG = LoggerFactory.getLogger(Tomcat8ServerRestartIntegrationTest.class);
-
-    @Override
-    protected EntitySpec<? extends SoftwareProcess> newEntitySpec() {
-        return EntitySpec.create(Tomcat8Server.class);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerSoftlayerLiveTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerSoftlayerLiveTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerSoftlayerLiveTest.java
deleted file mode 100644
index d88191b..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerSoftlayerLiveTest.java
+++ /dev/null
@@ -1,77 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import static org.testng.Assert.assertNotNull;
-
-import org.testng.annotations.Test;
-
-import brooklyn.entity.AbstractSoftlayerLiveTest;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.tomcat.Tomcat8Server;
-import brooklyn.location.Location;
-import brooklyn.test.Asserts;
-
-import org.apache.brooklyn.test.HttpTestUtils;
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * A simple test of installing+running on Softlayer, using various OS distros and versions. 
- */
-public class Tomcat8ServerSoftlayerLiveTest extends AbstractSoftlayerLiveTest {
-
-    public String getTestWar() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
-        return "classpath://hello-world.war";
-    }
-
-    @Override
-    protected void doTest(Location loc) throws Exception {
-        final Tomcat8Server server = app.createAndManageChild(EntitySpec.create(Tomcat8Server.class)
-                .configure("war", getTestWar()));
-        
-        app.start(ImmutableList.of(loc));
-        
-        String url = server.getAttribute(Tomcat8Server.ROOT_URL);
-        
-        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(url, 200);
-        HttpTestUtils.assertContentContainsText(url, "Hello");
-        
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertNotNull(server.getAttribute(Tomcat8Server.REQUEST_COUNT));
-                assertNotNull(server.getAttribute(Tomcat8Server.ERROR_COUNT));
-                assertNotNull(server.getAttribute(Tomcat8Server.TOTAL_PROCESSING_TIME));
-                
-                // TODO These appear not to be set in TomcatServerImpl.connectSensors
-                //      See TomcatServerEc2LiveTest, where these are also not included.
-//                assertNotNull(server.getAttribute(TomcatServer.MAX_PROCESSING_TIME));
-//                assertNotNull(server.getAttribute(TomcatServer.BYTES_RECEIVED));
-//                assertNotNull(server.getAttribute(TomcatServer.BYTES_SENT));
-            }});
-    }
-
-    @Test(groups = {"Live", "Live-sanity"})
-    @Override
-    public void test_Ubuntu_12_0_4() throws Exception {
-        super.test_Ubuntu_12_0_4();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerWebAppFixtureIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerWebAppFixtureIntegrationTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerWebAppFixtureIntegrationTest.java
deleted file mode 100644
index f0d7212..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/Tomcat8ServerWebAppFixtureIntegrationTest.java
+++ /dev/null
@@ -1,176 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import java.io.File;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketException;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.AbstractWebAppFixtureIntegrationTest;
-import brooklyn.entity.webapp.HttpsSslConfig;
-import brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
-import brooklyn.entity.webapp.tomcat.Tomcat8Server;
-import brooklyn.location.basic.PortRanges;
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-import brooklyn.test.entity.TestApplication;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.repeat.Repeater;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-
-public class Tomcat8ServerWebAppFixtureIntegrationTest extends AbstractWebAppFixtureIntegrationTest {
-
-    @SuppressWarnings("unused")
-    private static final Logger log = LoggerFactory.getLogger(Tomcat8ServerWebAppFixtureIntegrationTest.class);
-    
-    @DataProvider(name = "basicEntities")
-    public Object[][] basicEntities() {
-        TestApplication tomcatApp = newTestApplication();
-        Tomcat8Server tomcat = tomcatApp.createAndManageChild(EntitySpec.create(Tomcat8Server.class)
-                .configure(Tomcat8Server.HTTP_PORT, PortRanges.fromString(DEFAULT_HTTP_PORT)));
-
-
-        File keystoreFile;
-        try {
-            keystoreFile = createTemporaryKeyStore("myname", "mypass");
-            keystoreFile.deleteOnExit();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-
-        TestApplication tomcatHttpsApp = newTestApplication();
-        Tomcat8Server httpsTomcat = tomcatHttpsApp.createAndManageChild(EntitySpec.create(Tomcat8Server.class)
-                .configure(Tomcat8Server.ENABLED_PROTOCOLS, ImmutableSet.of("https"))
-                .configure(Tomcat8Server.HTTPS_SSL_CONFIG,
-                        new HttpsSslConfig().keyAlias("myname").keystorePassword("mypass").keystoreUrl(keystoreFile.getAbsolutePath())));
-
-        return new JavaWebAppSoftwareProcess[][] {
-                new JavaWebAppSoftwareProcess[] { tomcat },
-                new JavaWebAppSoftwareProcess[] { httpsTomcat }
-        };
-    }
-
-    // exists to be able to test on this class from GUI in Eclipse IDE
-    @Test(groups = "Integration", dataProvider = "basicEntities")
-    public void canStartAndStop(final SoftwareProcess entity) {
-        super.canStartAndStop(entity);
-    }
-    @Test(groups = "Integration", dataProvider = "basicEntities")
-    public void testReportsServiceDownWhenKilled(final SoftwareProcess entity) throws Exception {
-        super.testReportsServiceDownWhenKilled(entity);
-    }
-
-    @Override
-    // as parent, but with spring travel
-    @DataProvider(name = "entitiesWithWarAndURL")
-    public Object[][] entitiesWithWar() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
-        List<Object[]> result = Lists.newArrayList();
-        
-        for (Object[] entity : basicEntities()) {
-            result.add(new Object[] {
-                    entity[0],
-                    "hello-world.war",
-                    "hello-world/",
-                    "" // no sub-page path
-                    });
-        }
-
-        // TODO would be nice to test against spring web framework stock booking example
-        // but we'd need an external URL for that (we removed the binary from here for apache compliance reasons)
-//        TestApplication tomcatApp = newTestApplication();
-//        TomcatServer tomcat = tomcatApp.createAndManageChild(EntitySpec.create(TomcatServer.class)
-//                .configure(TomcatServer.HTTP_PORT, PortRanges.fromString(DEFAULT_HTTP_PORT)));
-//        result.add(new Object[] {
-//                tomcat,
-//                "swf-booking-mvc.war",
-//                "swf-booking-mvc/",
-//                "spring/intro",
-//               });
-        
-        return result.toArray(new Object[][] {});
-    }
-
-    @AfterMethod(alwaysRun=true, dependsOnMethods="shutdownApp")
-    public void ensureIsShutDown() throws Exception {
-        final AtomicReference<Socket> shutdownSocket = new AtomicReference<Socket>();
-        final AtomicReference<SocketException> gotException = new AtomicReference<SocketException>();
-        final Integer shutdownPort = (entity != null) ? entity.getAttribute(Tomcat8Server.SHUTDOWN_PORT) : null;
-        
-        if (shutdownPort != null) {
-            boolean socketClosed = Repeater.create("Checking WebApp has shut down")
-                    .repeat(new Callable<Void>() {
-                            public Void call() throws Exception {
-                                if (shutdownSocket.get() != null) shutdownSocket.get().close();
-                                try {
-                                    shutdownSocket.set(new Socket(InetAddress.getLocalHost(), shutdownPort));
-                                    gotException.set(null);
-                                } catch (SocketException e) {
-                                    gotException.set(e);
-                                }
-                                return null;
-                            }})
-                    .every(100, TimeUnit.MILLISECONDS)
-                    .until(new Callable<Boolean>() {
-                            public Boolean call() {
-                                return (gotException.get() != null);
-                            }})
-                    .limitIterationsTo(25)
-                    .run();
-            
-            if (socketClosed == false) {
-//                log.error("WebApp did not shut down - this is a failure of the last test run");
-//                log.warn("I'm sending a message to the shutdown port {}", shutdownPort);
-//                OutputStreamWriter writer = new OutputStreamWriter(shutdownSocket.getOutputStream());
-//                writer.write("SHUTDOWN\r\n");
-//                writer.flush();
-//                writer.close();
-//                shutdownSocket.close();
-                throw new Exception("Last test run did not shut down WebApp entity "+entity+" (port "+shutdownPort+")");
-            }
-        } else {
-            Assert.fail("Cannot shutdown, because shutdown-port not set for "+entity);
-        }
-    }
-
-    public static void main(String ...args) throws Exception {
-        Tomcat8ServerWebAppFixtureIntegrationTest t = new Tomcat8ServerWebAppFixtureIntegrationTest();
-        t.setUp();
-        t.testReportsServiceDownWhenKilled((SoftwareProcess) t.basicEntities()[0][0]);
-        t.shutdownApp();
-        t.ensureIsShutDown();
-        t.shutdownMgmt();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerDisableRetrieveUsageMetricsIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerDisableRetrieveUsageMetricsIntegrationTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerDisableRetrieveUsageMetricsIntegrationTest.java
deleted file mode 100644
index ed300c4..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerDisableRetrieveUsageMetricsIntegrationTest.java
+++ /dev/null
@@ -1,65 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-
-import org.apache.brooklyn.test.EntityTestUtils;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.BrooklynAppLiveTestSupport;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
-import brooklyn.test.Asserts;
-
-import com.google.common.collect.ImmutableList;
-
-public class TomcatServerDisableRetrieveUsageMetricsIntegrationTest extends BrooklynAppLiveTestSupport {
-    
-    // Note we test the default and the disabled with two entities, in the same method.
-    // This piggie-backs off the necessary length of time required for the default entity
-    // to have its metrics set; we then assert that the other entity does not have its set.
-    @Test(groups="Integration")
-    public void testDisableRetrievalOfUsageMetrics() throws Exception {
-        LocalhostMachineProvisioningLocation loc = app.newLocalhostProvisioningLocation();
-        final TomcatServer tc1 = app.createAndManageChild(EntitySpec.create(TomcatServer.class)
-                .configure(SoftwareProcess.RETRIEVE_USAGE_METRICS, false));
-        final TomcatServer tc2 = app.createAndManageChild(EntitySpec.create(TomcatServer.class));
-        
-        tc1.start(ImmutableList.of(loc));
-        tc2.start(ImmutableList.of(loc));
-
-        // tc2 uses defaults, so will include usage metrics
-        Asserts.succeedsEventually(new Runnable() {
-            public void run() {
-                assertNotNull(tc2.getAttribute(TomcatServer.CONNECTOR_STATUS));
-                assertNotNull(tc2.getAttribute(TomcatServer.ERROR_COUNT));
-                assertNotNull(tc2.getAttribute(TomcatServer.REQUEST_COUNT));
-                assertNotNull(tc2.getAttribute(TomcatServer.TOTAL_PROCESSING_TIME));
-            }});
-
-        // tc1 should have status info, but not usage metrics
-        EntityTestUtils.assertAttributeEventuallyNonNull(tc1, TomcatServer.CONNECTOR_STATUS);
-        EntityTestUtils.assertAttributeEqualsContinually(tc1, TomcatServer.ERROR_COUNT, null);
-        assertNull(tc1.getAttribute(TomcatServer.REQUEST_COUNT));
-        assertNull(tc1.getAttribute(TomcatServer.TOTAL_PROCESSING_TIME));
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java
deleted file mode 100644
index 467e3c4..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java
+++ /dev/null
@@ -1,70 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.testng.Assert.assertNotNull;
-
-import java.net.URL;
-
-import org.apache.brooklyn.test.HttpTestUtils;
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.AbstractEc2LiveTest;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.location.Location;
-import brooklyn.test.Asserts;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * A simple test of installing+running on AWS-EC2, using various OS distros and versions. 
- */
-public class TomcatServerEc2LiveTest extends AbstractEc2LiveTest {
-
-    public String getTestWar() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
-        return "classpath://hello-world.war";
-    }
-
-    @Override
-    protected void doTest(Location loc) throws Exception {
-        final TomcatServer server = app.createAndManageChild(EntitySpec.create(TomcatServer.class)
-                .configure(TomcatServer.OPEN_IPTABLES, true)
-                .configure("war", getTestWar()));
-        
-        app.start(ImmutableList.of(loc));
-        
-        String url = server.getAttribute(TomcatServer.ROOT_URL);
-        
-        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(url, 200);
-        HttpTestUtils.assertContentContainsText(url, "Hello");
-        
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertNotNull(server.getAttribute(TomcatServer.REQUEST_COUNT));
-                assertNotNull(server.getAttribute(TomcatServer.ERROR_COUNT));
-                assertNotNull(server.getAttribute(TomcatServer.TOTAL_PROCESSING_TIME));
-            }});
-    }
-    
-    @Test(enabled=false)
-    public void testDummy() {} // Convince testng IDE integration that this really does have test methods  
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerRestartIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerRestartIntegrationTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerRestartIntegrationTest.java
deleted file mode 100644
index 665cc69..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerRestartIntegrationTest.java
+++ /dev/null
@@ -1,44 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.AbstractSoftwareProcessRestartIntegrationTest;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.proxying.EntitySpec;
-
-/**
- * Tests restart of the software *process* (as opposed to the VM).
- */
-@Test(groups="Integration")
-public class TomcatServerRestartIntegrationTest extends AbstractSoftwareProcessRestartIntegrationTest {
-    
-    // TODO Remove duplication from MySqlRestartIntegrationTest
-    
-    @SuppressWarnings("unused")
-    private static final Logger LOG = LoggerFactory.getLogger(TomcatServerRestartIntegrationTest.class);
-
-    @Override
-    protected EntitySpec<? extends SoftwareProcess> newEntitySpec() {
-        return EntitySpec.create(TomcatServer.class);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSoftlayerLiveTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSoftlayerLiveTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSoftlayerLiveTest.java
deleted file mode 100644
index ab23e7f..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerSoftlayerLiveTest.java
+++ /dev/null
@@ -1,78 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.testng.Assert.assertNotNull;
-
-import java.net.URL;
-
-import org.apache.brooklyn.test.HttpTestUtils;
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.AbstractSoftlayerLiveTest;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.location.Location;
-import brooklyn.test.Asserts;
-
-import com.google.common.collect.ImmutableList;
-
-/**
- * A simple test of installing+running on Softlayer, using various OS distros and versions. 
- */
-public class TomcatServerSoftlayerLiveTest extends AbstractSoftlayerLiveTest {
-
-    public String getTestWar() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
-        return "classpath://hello-world.war";
-    }
-
-    @Override
-    protected void doTest(Location loc) throws Exception {
-        final TomcatServer server = app.createAndManageChild(EntitySpec.create(TomcatServer.class)
-                .configure("war", getTestWar()));
-        
-        app.start(ImmutableList.of(loc));
-        
-        String url = server.getAttribute(TomcatServer.ROOT_URL);
-        
-        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(url, 200);
-        HttpTestUtils.assertContentContainsText(url, "Hello");
-        
-        Asserts.succeedsEventually(new Runnable() {
-            @Override public void run() {
-                assertNotNull(server.getAttribute(TomcatServer.REQUEST_COUNT));
-                assertNotNull(server.getAttribute(TomcatServer.ERROR_COUNT));
-                assertNotNull(server.getAttribute(TomcatServer.TOTAL_PROCESSING_TIME));
-                
-                // TODO These appear not to be set in TomcatServerImpl.connectSensors
-                //      See TomcatServerEc2LiveTest, where these are also not included.
-//                assertNotNull(server.getAttribute(TomcatServer.MAX_PROCESSING_TIME));
-//                assertNotNull(server.getAttribute(TomcatServer.BYTES_RECEIVED));
-//                assertNotNull(server.getAttribute(TomcatServer.BYTES_SENT));
-            }});
-    }
-
-    @Test(groups = {"Live", "Live-sanity"})
-    @Override
-    public void test_Ubuntu_12_0_4() throws Exception {
-        super.test_Ubuntu_12_0_4();
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java
deleted file mode 100644
index b25bc58..0000000
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerWebAppFixtureIntegrationTest.java
+++ /dev/null
@@ -1,175 +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.
- */
-package brooklyn.entity.webapp.tomcat;
-
-import java.io.File;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.SocketException;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.brooklyn.test.TestResourceUnavailableException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.entity.webapp.AbstractWebAppFixtureIntegrationTest;
-import brooklyn.entity.webapp.HttpsSslConfig;
-import brooklyn.entity.webapp.JavaWebAppSoftwareProcess;
-import brooklyn.location.basic.PortRanges;
-import brooklyn.test.entity.TestApplication;
-import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.repeat.Repeater;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-
-public class TomcatServerWebAppFixtureIntegrationTest extends AbstractWebAppFixtureIntegrationTest {
-
-    @SuppressWarnings("unused")
-    private static final Logger log = LoggerFactory.getLogger(TomcatServerWebAppFixtureIntegrationTest.class);
-    
-    @DataProvider(name = "basicEntities")
-    public Object[][] basicEntities() {
-        TestApplication tomcatApp = newTestApplication();
-        TomcatServer tomcat = tomcatApp.createAndManageChild(EntitySpec.create(TomcatServer.class)
-                .configure(TomcatServer.HTTP_PORT, PortRanges.fromString(DEFAULT_HTTP_PORT)));
-
-
-        File keystoreFile;
-        try {
-            keystoreFile = createTemporaryKeyStore("myname", "mypass");
-            keystoreFile.deleteOnExit();
-        } catch (Exception e) {
-            throw Exceptions.propagate(e);
-        }
-
-        TestApplication tomcatHttpsApp = newTestApplication();
-        TomcatServer httpsTomcat = tomcatHttpsApp.createAndManageChild(EntitySpec.create(TomcatServer.class)
-                .configure(TomcatServer.ENABLED_PROTOCOLS, ImmutableSet.of("https"))
-                .configure(TomcatServer.HTTPS_SSL_CONFIG,
-                        new HttpsSslConfig().keyAlias("myname").keystorePassword("mypass").keystoreUrl(keystoreFile.getAbsolutePath())));
-
-        return new JavaWebAppSoftwareProcess[][] {
-                new JavaWebAppSoftwareProcess[] { tomcat },
-                new JavaWebAppSoftwareProcess[] { httpsTomcat }
-        };
-    }
-
-    // exists to be able to test on this class from GUI in Eclipse IDE
-    @Test(groups = "Integration", dataProvider = "basicEntities")
-    public void canStartAndStop(final SoftwareProcess entity) {
-        super.canStartAndStop(entity);
-    }
-    @Test(groups = "Integration", dataProvider = "basicEntities")
-    public void testReportsServiceDownWhenKilled(final SoftwareProcess entity) throws Exception {
-        super.testReportsServiceDownWhenKilled(entity);
-    }
-
-    @Override
-    // as parent, but with spring travel
-    @DataProvider(name = "entitiesWithWarAndURL")
-    public Object[][] entitiesWithWar() {
-        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), "/hello-world.war");
-        List<Object[]> result = Lists.newArrayList();
-        
-        for (Object[] entity : basicEntities()) {
-            result.add(new Object[] {
-                    entity[0],
-                    "hello-world.war",
-                    "hello-world/",
-                    "" // no sub-page path
-                    });
-        }
-
-        // TODO would be nice to test against spring web framework stock booking example
-        // but we'd need an external URL for that (we removed the binary from here for apache compliance reasons)
-//        TestApplication tomcatApp = newTestApplication();
-//        TomcatServer tomcat = tomcatApp.createAndManageChild(EntitySpec.create(TomcatServer.class)
-//                .configure(TomcatServer.HTTP_PORT, PortRanges.fromString(DEFAULT_HTTP_PORT)));
-//        result.add(new Object[] {
-//                tomcat,
-//                "swf-booking-mvc.war",
-//                "swf-booking-mvc/",
-//                "spring/intro",
-//               });
-        
-        return result.toArray(new Object[][] {});
-    }
-
-    @AfterMethod(alwaysRun=true, dependsOnMethods="shutdownApp")
-    public void ensureIsShutDown() throws Exception {
-        final AtomicReference<Socket> shutdownSocket = new AtomicReference<Socket>();
-        final AtomicReference<SocketException> gotException = new AtomicReference<SocketException>();
-        final Integer shutdownPort = (entity != null) ? entity.getAttribute(TomcatServer.SHUTDOWN_PORT) : null;
-        
-        if (shutdownPort != null) {
-            boolean socketClosed = Repeater.create("Checking WebApp has shut down")
-                    .repeat(new Callable<Void>() {
-                            public Void call() throws Exception {
-                                if (shutdownSocket.get() != null) shutdownSocket.get().close();
-                                try {
-                                    shutdownSocket.set(new Socket(InetAddress.getLocalHost(), shutdownPort));
-                                    gotException.set(null);
-                                } catch (SocketException e) {
-                                    gotException.set(e);
-                                }
-                                return null;
-                            }})
-                    .every(100, TimeUnit.MILLISECONDS)
-                    .until(new Callable<Boolean>() {
-                            public Boolean call() {
-                                return (gotException.get() != null);
-                            }})
-                    .limitIterationsTo(25)
-                    .run();
-            
-            if (socketClosed == false) {
-//                log.error("WebApp did not shut down - this is a failure of the last test run");
-//                log.warn("I'm sending a message to the shutdown port {}", shutdownPort);
-//                OutputStreamWriter writer = new OutputStreamWriter(shutdownSocket.getOutputStream());
-//                writer.write("SHUTDOWN\r\n");
-//                writer.flush();
-//                writer.close();
-//                shutdownSocket.close();
-                throw new Exception("Last test run did not shut down WebApp entity "+entity+" (port "+shutdownPort+")");
-            }
-        } else {
-            Assert.fail("Cannot shutdown, because shutdown-port not set for "+entity);
-        }
-    }
-
-    public static void main(String ...args) throws Exception {
-        TomcatServerWebAppFixtureIntegrationTest t = new TomcatServerWebAppFixtureIntegrationTest();
-        t.setUp();
-        t.testReportsServiceDownWhenKilled((SoftwareProcess) t.basicEntities()[0][0]);
-        t.shutdownApp();
-        t.ensureIsShutDown();
-        t.shutdownMgmt();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntity.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntity.java b/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntity.java
deleted file mode 100644
index a13ac9d..0000000
--- a/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntity.java
+++ /dev/null
@@ -1,78 +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.
- */
-package brooklyn.test.entity;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.entity.basic.ServiceStateLogic;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.basic.SoftwareProcessDriverLifecycleEffectorTasks;
-import brooklyn.entity.basic.EntityLocal;
-import brooklyn.entity.java.VanillaJavaApp;
-import brooklyn.entity.proxying.ImplementedBy;
-import brooklyn.entity.webapp.WebAppService;
-import brooklyn.location.Location;
-import brooklyn.util.config.ConfigBag;
-
-/**
- * Mock web application server entity for testing.
- */
-@ImplementedBy(TestJavaWebAppEntityImpl.class)
-public interface TestJavaWebAppEntity extends VanillaJavaApp, WebAppService, EntityLocal {
-
-    /**
-     * Injects the test entity's customised lifecycle tasks.
-     */
-    ConfigKey<SoftwareProcessDriverLifecycleEffectorTasks> LIFECYCLE_EFFECTOR_TASKS = ConfigKeys.newConfigKeyWithDefault(
-            SoftwareProcess.LIFECYCLE_EFFECTOR_TASKS,
-            new TestJavaWebAppEntityLifecycleTasks());
-
-    void spoofRequest();
-    int getA();
-    int getB();
-    int getC();
-
-    static class TestJavaWebAppEntityLifecycleTasks extends SoftwareProcessDriverLifecycleEffectorTasks {
-        private static final Logger LOG = LoggerFactory.getLogger(TestJavaWebAppEntityLifecycleTasks.class);
-
-        @Override
-        public void start(java.util.Collection<? extends Location> locations) {
-            ServiceStateLogic.setExpectedState(entity(), Lifecycle.STARTING);
-            LOG.trace("Starting {}", this);
-            entity().setAttribute(SERVICE_PROCESS_IS_RUNNING, true);
-            entity().setAttribute(Attributes.SERVICE_UP, true);
-            ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING);
-        }
-
-        @Override
-        public void stop(ConfigBag parameters) {
-            ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING);
-            LOG.trace("Stopping {}", this);
-            entity().setAttribute(Attributes.SERVICE_UP, false);
-            entity().setAttribute(SERVICE_PROCESS_IS_RUNNING, false);
-            ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPED);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntityImpl.java b/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntityImpl.java
deleted file mode 100644
index be7fb8a..0000000
--- a/software/webapp/src/test/java/brooklyn/test/entity/TestJavaWebAppEntityImpl.java
+++ /dev/null
@@ -1,61 +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.
- */
-package brooklyn.test.entity;
-
-import java.util.Map;
-
-import brooklyn.entity.Entity;
-import brooklyn.entity.java.VanillaJavaAppImpl;
-import brooklyn.entity.webapp.WebAppServiceConstants;
-import brooklyn.util.flags.SetFromFlag;
-
-public class TestJavaWebAppEntityImpl extends VanillaJavaAppImpl implements TestJavaWebAppEntity {
-
-    @SetFromFlag public int a;
-    @SetFromFlag public int b;
-    @SetFromFlag public int c;
-
-    public TestJavaWebAppEntityImpl() {}
-    
-    // constructor required for use in DynamicCluster.factory
-    public TestJavaWebAppEntityImpl(@SuppressWarnings("rawtypes") Map flags, Entity parent) { super(flags, parent); }
-
-    @Override
-    public synchronized void spoofRequest() {
-        Integer rc = getAttribute(WebAppServiceConstants.REQUEST_COUNT);
-        if (rc==null) rc = 0;
-        setAttribute(WebAppServiceConstants.REQUEST_COUNT, rc+1);
-    }
-
-    @Override
-    public int getA() {
-        return a;
-    }
-    
-    @Override
-    public int getB() {
-        return b;
-    }
-    
-    @Override
-    public int getC() {
-        return c;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
new file mode 100644
index 0000000..12ffce9
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/AbstractGeoDnsServiceTest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.testng.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+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.management.ManagementContext;
+import org.apache.brooklyn.test.EntityTestUtils;
+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 brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.DynamicGroup;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.group.DynamicFabric;
+import brooklyn.entity.group.DynamicRegionsFabric;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.proxying.ImplementedBy;
+import brooklyn.location.Location;
+import brooklyn.location.LocationRegistry;
+import brooklyn.location.LocationResolver;
+import brooklyn.location.LocationSpec;
+import brooklyn.location.basic.BasicLocationRegistry;
+import brooklyn.location.basic.LocationConfigKeys;
+import brooklyn.location.basic.Locations;
+import brooklyn.location.basic.Machines;
+import brooklyn.location.basic.SimulatedLocation;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.location.geo.HostGeoInfo;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.collections.CollectionFunctionals;
+import brooklyn.util.exceptions.Exceptions;
+
+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 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;
+    
+
+    @BeforeMethod(alwaysRun=true)
+    public void setup() {
+        managementContext = new LocalManagementContext();
+        
+        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() {
+            @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);
+        
+        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());
+    }
+
+    private SimulatedLocation newSimulatedLocation(String name, double lat, double lon) {
+        return managementContext.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)
+                .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)
+                .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());
+    }
+    
+    @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());
+    }
+
+    @Test
+    public void testSubscribesToHostname() {
+        ((EntityInternal)geoDns).setConfig(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));
+        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());
+    }
+
+    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);
+        
+        for (Entity e: testEntities.getMembers()) {
+            if (includeServiceUp)
+                ((EntityInternal)e).setAttribute(Attributes.SERVICE_UP, true);
+            
+            SshMachineLocation l = Machines.findUniqueSshMachineLocation(e.getLocations()).get();
+            if (includeAddress)
+                ((EntityInternal)e).setAttribute(Attributes.ADDRESS, l.getAddress().getHostAddress());
+            String h = (String) l.config().getBag().getStringKey("hostname");
+            if (h==null) h = l.getAddress().getHostName();
+            if (includeHostname)
+                ((EntityInternal)e).setAttribute(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));
+        
+        String id3 = fabric.addRegion("test:north");
+        publishSensors(3, true, false, false);
+        try {
+            EntityTestUtils.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());
+    }    
+
+
+    @Test
+    public void testFiltersEntirelyPrivate() {
+        ((EntityInternal)geoDns).setConfig(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));
+        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());
+    }
+
+    @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/77dff880/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java
new file mode 100644
index 0000000..b49d9eb
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingIntegrationTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.entity.dns.geoscaling.GeoscalingDnsService;
+import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingScriptGenerator;
+import org.apache.brooklyn.management.ManagementContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.DynamicGroup;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.LocationSpec;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.location.geo.HostGeoInfo;
+import brooklyn.location.geo.HostGeoLookup;
+import brooklyn.location.geo.MaxMind2HostGeoLookup;
+import brooklyn.location.geo.UtraceHostGeoLookup;
+import brooklyn.test.Asserts;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.internal.BrooklynSystemProperties;
+import brooklyn.util.net.Networking;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * {@link GeoscalingScriptGenerator} unit tests.
+ */
+public class GeoscalingIntegrationTest {
+
+    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;
+    
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        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)));
+        
+        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));
+        
+        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"));
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        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();
+    }
+    
+    @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).setConfig(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES, true);
+        
+        target.setAttribute(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).setConfig(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES, false); // false is default
+        
+        app.start(ImmutableList.of(locWithoutGeo));
+        target.setAttribute(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).setConfig(GeoscalingDnsService.INCLUDE_HOMELESS_ENTITIES, true);
+        
+        app.start(ImmutableList.of(locWithoutGeo));
+        target.setAttribute(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/77dff880/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java
new file mode 100644
index 0000000..fe45121
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingScriptGeneratorTest.java
@@ -0,0 +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.dns.geoscaling;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingScriptGenerator;
+import org.testng.annotations.Test;
+
+import brooklyn.location.geo.HostGeoInfo;
+import brooklyn.util.ResourceUtils;
+
+
+/**
+ * {@link GeoscalingScriptGenerator} unit tests.
+ */
+public class GeoscalingScriptGeneratorTest {
+    
+    private final static Set<HostGeoInfo> HOSTS = new LinkedHashSet<HostGeoInfo>();
+    static {
+        HOSTS.add(new HostGeoInfo("1.2.3.100", "Server 1", 40.0, -80.0));
+        HOSTS.add(new HostGeoInfo("1.2.3.101", "Server 2", 30.0, 20.0));
+    }
+    
+    
+    @Test
+    public void testScriptGeneration() {
+        Date generationTime = new Date(0);
+        String generatedScript = GeoscalingScriptGenerator.generateScriptString(generationTime, HOSTS);
+        assertTrue(generatedScript.contains("1.2.3"));
+        String expectedScript = ResourceUtils.create(this).getResourceAsString("org/apache/brooklyn/entity/dns/geoscaling/expectedScript.php");
+        assertEquals(generatedScript, expectedScript);
+        //also make sure leading slash is allowed
+        String expectedScript2 = ResourceUtils.create(this).getResourceAsString("org/apache/brooklyn/entity/dns/geoscaling/expectedScript.php");
+        assertEquals(generatedScript, expectedScript);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/77dff880/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
new file mode 100644
index 0000000..4602840
--- /dev/null
+++ b/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoscalingWebClientTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.entity.dns.geoscaling.GeoscalingWebClient;
+import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.Domain;
+import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingWebClient.SmartSubdomain;
+import org.apache.http.client.HttpClient;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.util.http.HttpTool;
+import brooklyn.util.text.Strings;
+
+/**
+ * {@link GeoscalingWebClient} unit tests.
+ */
+public class GeoscalingWebClientTest {
+    
+    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() {
+        // 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);
+    }
+    
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        if (smartSubdomain != null)
+            smartSubdomain.delete();
+        
+        if (domain != null)
+            domain.delete();
+        
+        if (geoscaling != null)
+            geoscaling.logout();
+    }
+    
+    @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();
+    }
+    
+}