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(); - } + + }
