return quickly on attempts to look up external ip address, so we don't block excessively when network is dodgy; and misc tidies elsewhere for better http test routings and logging on duplicated sensor definitions
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/2da0cf6b Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/2da0cf6b Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/2da0cf6b Branch: refs/heads/0.4.0 Commit: 2da0cf6bf0a2e99b54b7564365e159b90abef664 Parents: a0e7c13 Author: Alex Heneveld <[email protected]> Authored: Wed Oct 10 00:50:46 2012 +0100 Committer: Alex Heneveld <[email protected]> Committed: Wed Oct 10 00:50:46 2012 +0100 ---------------------------------------------------------------------- .../entity/basic/EntityDynamicType.java | 16 ++++-- .../location/geo/UtraceHostGeoLookup.java | 51 ++++++++++++++++++-- core/src/main/java/brooklyn/util/Time.java | 2 + .../brooklyn/util/internal/TimeExtras.groovy | 19 ++++++-- .../main/java/brooklyn/test/HttpTestUtils.java | 34 +++++++++++++ .../main/java/brooklyn/test/TestUtils.groovy | 15 ++++-- 6 files changed, 121 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2da0cf6b/core/src/main/java/brooklyn/entity/basic/EntityDynamicType.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/basic/EntityDynamicType.java b/core/src/main/java/brooklyn/entity/basic/EntityDynamicType.java index c1f5701..0024e3b 100644 --- a/core/src/main/java/brooklyn/entity/basic/EntityDynamicType.java +++ b/core/src/main/java/brooklyn/entity/basic/EntityDynamicType.java @@ -176,12 +176,14 @@ public class EntityDynamicType { try { Class<? extends Entity> clazz = entity.getClass(); Map<String,Effector<?>> result = Maps.newLinkedHashMap(); + Map<String,Field> sources = Maps.newLinkedHashMap(); for (Field f : clazz.getFields()) { if (Effector.class.isAssignableFrom(f.getType())) { Effector<?> eff = (Effector<?>) f.get(entity); Effector<?> overwritten = result.put(eff.getName(), eff); + Field source = sources.put(eff.getName(), f); if (overwritten!=null && overwritten != eff) - LOG.warn("multiple definitions for effector {} on {}; preferring {} to {}", new Object[] {eff.getName(), entity, eff, overwritten}); + LOG.warn("multiple definitions for effector {} on {}; preferring {} from {} to {} from {}", new Object[] {eff.getName(), entity, eff, f, overwritten, source}); } } @@ -198,12 +200,20 @@ public class EntityDynamicType { try { Class<? extends Entity> clazz = entity.getClass(); Map<String,Sensor<?>> result = Maps.newLinkedHashMap(); + Map<String,Field> sources = Maps.newLinkedHashMap(); for (Field f : clazz.getFields()) { if (Sensor.class.isAssignableFrom(f.getType())) { Sensor<?> sens = (Sensor<?>) f.get(entity); Sensor<?> overwritten = result.put(sens.getName(), sens); - if (overwritten!=null && overwritten != sens) - LOG.warn("multiple definitions for sensor {} on {}; preferring {} to {}", new Object[] {sens.getName(), entity, sens, overwritten}); + Field source = sources.put(sens.getName(), f); + if (overwritten!=null && overwritten != sens) { + if (sens instanceof HasConfigKey) { + // probably overriding defaults, just log as debug (there will be add'l logging in config key section) + LOG.debug("multiple definitions for config sensor {} on {}; preferring {} from {} to {} from {}", new Object[] {sens.getName(), entity, sens, f, overwritten, source}); + } else { + LOG.warn("multiple definitions for sensor {} on {}; preferring {} from {} to {} from {}", new Object[] {sens.getName(), entity, sens, f, overwritten, source}); + } + } } } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2da0cf6b/core/src/main/java/brooklyn/location/geo/UtraceHostGeoLookup.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/location/geo/UtraceHostGeoLookup.java b/core/src/main/java/brooklyn/location/geo/UtraceHostGeoLookup.java index 9a41344..221534c 100644 --- a/core/src/main/java/brooklyn/location/geo/UtraceHostGeoLookup.java +++ b/core/src/main/java/brooklyn/location/geo/UtraceHostGeoLookup.java @@ -7,15 +7,18 @@ import groovy.util.XmlParser; import java.io.IOException; import java.net.InetAddress; import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Throwables; - import brooklyn.util.NetworkUtils; import brooklyn.util.ResourceUtils; +import com.google.common.base.Throwables; + public class UtraceHostGeoLookup implements HostGeoLookup { /* @@ -51,11 +54,49 @@ Beyond this you get blacklisted and requests may time out, or return none. return "http://xml.utrace.de/?query="+ip.trim(); } - static String localExternalIp; + static AtomicBoolean retrievingLocalExternalIp = new AtomicBoolean(false); + volatile static String localExternalIp; /** returns public IP of localhost */ - public synchronized static String getLocalhostExternalIp() { + public static synchronized String getLocalhostExternalIp() { if (localExternalIp!=null) return localExternalIp; - localExternalIp = new ResourceUtils(HostGeoLookup.class).getResourceAsString("http://api.externalip.net/ip/").trim(); + + // do in private thread, otherwise blocks for 30s+ on dodgy network! + // (we can skip it if someone else is doing it, we have synch lock so we'll get notified) + if (!retrievingLocalExternalIp.get()) + new Thread(new Runnable() { + public void run() { + if (retrievingLocalExternalIp.getAndSet(true)) + // someone else already trying to retrieve; caller can safely just wait, + // as they will get notified by the someone else + return; + try { + if (localExternalIp!=null) + // someone else succeeded + return; + log.debug("Looking up external IP of this host in private thread "+Thread.currentThread()); + localExternalIp = new ResourceUtils(HostGeoLookup.class).getResourceAsString("http://api.externalip.net/ip/").trim(); + log.debug("Finished looking up external IP of this host in private thread, result "+localExternalIp); + } catch (Throwable t) { + log.debug("Not able to look up external IP of this host in private thread, probably offline ("+t+")"); + } finally { + synchronized (UtraceHostGeoLookup.class) { + UtraceHostGeoLookup.class.notifyAll(); + retrievingLocalExternalIp.set(false); + } + } + } + }).start(); + + try { + // only wait 2s, so startup is fast + UtraceHostGeoLookup.class.wait(2000); + } catch (InterruptedException e) { + throw Throwables.propagate(e); + } + if (localExternalIp==null) throw + Throwables.propagate(new IOException("Unable to discover external IP of local machine; response to server timed out (thread may be ongoing)")); + + log.debug("Looked up external IP of this host, result is: "+localExternalIp); return localExternalIp; } public String getLookupUrlForLocalhost() { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2da0cf6b/core/src/main/java/brooklyn/util/Time.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/util/Time.java b/core/src/main/java/brooklyn/util/Time.java index d59240a..0aa32b3 100644 --- a/core/src/main/java/brooklyn/util/Time.java +++ b/core/src/main/java/brooklyn/util/Time.java @@ -147,6 +147,7 @@ public class Time { return result; } + /** sleep which propagates Interrupted as unchecked */ public static void sleep(long millis) { try { Thread.sleep(millis); @@ -275,4 +276,5 @@ public class Time { return d*multiplier + dd; } } + } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2da0cf6b/core/src/main/java/brooklyn/util/internal/TimeExtras.groovy ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/util/internal/TimeExtras.groovy b/core/src/main/java/brooklyn/util/internal/TimeExtras.groovy index 7a694a9..61c5169 100644 --- a/core/src/main/java/brooklyn/util/internal/TimeExtras.groovy +++ b/core/src/main/java/brooklyn/util/internal/TimeExtras.groovy @@ -3,12 +3,12 @@ package brooklyn.util.internal import groovy.time.TimeDuration import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicBoolean -import org.codehaus.groovy.reflection.ClassInfo; import org.slf4j.Logger import org.slf4j.LoggerFactory +import brooklyn.util.Time + /** * Classloading this class will cause multiply/add to be made available on TimeDuration. @@ -44,6 +44,19 @@ class TimeExtras { * where an int may get constructed too early and not have the multiply syntax available * (because grail is invoked?; if e.g. 5*SECONDS throws an error, try duration(5, SECONDS) */ public static TimeDuration duration(int value, TimeUnit unit) { - return new TimeDuration(unit.toMillis(value)); + return new TimeDuration(0, 0, 0, (int)unit.toMillis(value)); } + + public static final TimeDuration ONE_SECOND = duration(1, TimeUnit.SECONDS); + public static final TimeDuration FIVE_SECONDS = duration(5, TimeUnit.SECONDS); + public static final TimeDuration TEN_SECONDS = duration(10, TimeUnit.SECONDS); + public static final TimeDuration THIRTY_SECONDS = duration(30, TimeUnit.SECONDS); + public static final TimeDuration ONE_MINUTE = duration(1, TimeUnit.MINUTES); + public static final TimeDuration TWO_MINUTES = duration(2, TimeUnit.MINUTES); + public static final TimeDuration FIVE_MINUTES = duration(5, TimeUnit.MINUTES); + + public static void sleep(TimeDuration duration) { + Time.sleep(duration.toMilliseconds()); + } + } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2da0cf6b/usage/test-support/src/main/java/brooklyn/test/HttpTestUtils.java ---------------------------------------------------------------------- diff --git a/usage/test-support/src/main/java/brooklyn/test/HttpTestUtils.java b/usage/test-support/src/main/java/brooklyn/test/HttpTestUtils.java index a64adfa..a9167fe 100644 --- a/usage/test-support/src/main/java/brooklyn/test/HttpTestUtils.java +++ b/usage/test-support/src/main/java/brooklyn/test/HttpTestUtils.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.util.Collections; import java.util.Map; import java.util.NoSuchElementException; @@ -17,10 +18,13 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; +import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testng.Assert; import com.google.common.base.Throwables; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; @@ -108,6 +112,36 @@ public class HttpTestUtils { }); } + public static void assertContentContainsText(final String url, final String phrase, final String ...additionalPhrases) { + try { + String contents = DefaultGroovyMethods.getText(new URL(url).openStream()); + Assert.assertTrue(contents!=null && contents.length()>0); + for (String text: Lists.asList(phrase, additionalPhrases)) { + if (!contents.contains(text)) { + LOG.warn("CONTENTS OF URL "+url+" MISSING TEXT: "+text+"\n"+contents); + Assert.fail("URL "+url+" does not contain text: "+text); + } + } + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + public static void assertContentEventuallyContainsText(Map flags, final String url, final String phrase, final String ...additionalPhrases) { + TestUtils.executeUntilSucceeds(new Runnable() { + public void run() { + assertContentContainsText(url, phrase, additionalPhrases); + } + }); + } + public static void assertContentEventuallyContainsText(final String url, final String phrase, final String ...additionalPhrases) { + assertContentEventuallyContainsText(Collections.emptyMap(), url, phrase, additionalPhrases); + } + + /** @deprecated since 0.4.0 use assertContentEventuallyContainsText */ + // it's not necessarily http (and http is implied by the class name anyway) + // more importantly, we want to use new routines above which don't wrap execute-until-succeeds twice! + @Deprecated public static void assertHttpContentEventuallyContainsText(final String url, final String containedText) { TestUtils.executeUntilSucceeds(new Runnable() { public void run() { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2da0cf6b/usage/test-support/src/main/java/brooklyn/test/TestUtils.groovy ---------------------------------------------------------------------- diff --git a/usage/test-support/src/main/java/brooklyn/test/TestUtils.groovy b/usage/test-support/src/main/java/brooklyn/test/TestUtils.groovy index d2d340f..1c7a828 100644 --- a/usage/test-support/src/main/java/brooklyn/test/TestUtils.groovy +++ b/usage/test-support/src/main/java/brooklyn/test/TestUtils.groovy @@ -188,14 +188,14 @@ public class TestUtils { TimeDuration fixedPeriod = toTimeDuration(flags.period) ?: null TimeDuration minPeriod = fixedPeriod ?: toTimeDuration(flags.minPeriod) ?: new TimeDuration(0,0,0,1) TimeDuration maxPeriod = fixedPeriod ?: toTimeDuration(flags.maxPeriod) ?: new TimeDuration(0,0,0,500) - int maxAttempts = flags.maxAttempts ?: Integer.MAX_VALUE + int maxAttempts = flags.maxAttempts ?: Integer.MAX_VALUE; + int attempt = 0; + long startTime = System.currentTimeMillis(); try { Throwable lastException = null; Object result; long lastAttemptTime = 0; - long startTime = System.currentTimeMillis() - long expireTime = startTime+duration.toMilliseconds() - int attempt = 0; + long expireTime = startTime+duration.toMilliseconds(); long sleepTimeBetweenAttempts = minPeriod.toMilliseconds(); while (attempt<maxAttempts && lastAttemptTime<expireTime) { @@ -229,7 +229,9 @@ public class TestUtils { throw lastException fail "invalid result: $result" } catch (Throwable t) { - if (logException) log.info("failed execute-until-succeeds (rethrowing): "+t) + if (logException) log.info("failed execute-until-succeeds, "+attempt+" attempts, "+ + (System.currentTimeMillis()-startTime)+"ms elapsed "+ + "(rethrowing): "+t); throw t } finally { finallyBlock.call() @@ -319,6 +321,9 @@ public class TestUtils { } } + /** @deprecated since 0.4.0 use HttpTestUtils.assertUrlEventuallyHasText or HttpTestUtils.assertUrlHasText + * (NB: this method has "eventually" logic, with default timeout of 30s, despite the absence of that in the name) */ + @Deprecated public static void assertUrlHasText(Map flags=[:], String url, String ...phrases) { String contents; TimeDuration timeout = flags.timeout in Number ? flags.timeout*TimeUnit.MILLISECONDS : flags.timeout ?: 30*TimeUnit.SECONDS
