diff --git a/pom.xml b/pom.xml
index 992f475..5b10f2d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.aphyr
riemann-java-client
- 0.2.12-SNAPSHOT
+ 0.2.13-SNAPSHOT
bundle
Riemann Java Client
Java client for http://aphyr.github.com/riemann/
diff --git a/riemann-java-client.iml b/riemann-java-client.iml
index af0253d..3d9ede7 100644
--- a/riemann-java-client.iml
+++ b/riemann-java-client.iml
@@ -6,23 +6,18 @@
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
-
-
+
\ No newline at end of file
diff --git a/src/main/java/com/aphyr/riemann/client/EventDSL.java b/src/main/java/com/aphyr/riemann/client/EventDSL.java
index 0bbb7a7..5ee6619 100644
--- a/src/main/java/com/aphyr/riemann/client/EventDSL.java
+++ b/src/main/java/com/aphyr/riemann/client/EventDSL.java
@@ -17,12 +17,8 @@ public class EventDSL {
public EventDSL(AbstractRiemannClient client) {
this.client = client;
this.builder = Event.newBuilder();
- try {
- this.builder.setHost(java.net.InetAddress.getLocalHost().getHostName());
- } catch (java.net.UnknownHostException e) {
- // If we can't get the local host, a null host is perfectly
- // acceptable. Caller will know soon enough. :)
- }
+
+ this.builder.setHost(LocalhostResolver.getResolvedHostname());
}
public EventDSL host(String host) {
diff --git a/src/main/java/com/aphyr/riemann/client/LocalhostResolver.java b/src/main/java/com/aphyr/riemann/client/LocalhostResolver.java
new file mode 100644
index 0000000..b416423
--- /dev/null
+++ b/src/main/java/com/aphyr/riemann/client/LocalhostResolver.java
@@ -0,0 +1,91 @@
+package com.aphyr.riemann.client;
+
+import java.net.UnknownHostException;
+
+/**
+ * A "Smarter" localhost resolver
+ * see issue: https://github.com/aphyr/riemann-java-client/issues/44
+ * Trying to avoid a lot of calls to java.net.InetAddress.getLocalHost()
+ * which under AWS trigger DNS resolving and have relatively high latency *per event*
+ * usually, the hostname doesn't change so often to warrant a real query.
+ *
+ * A real call to java.net.InetAddress.getLocalHost().getHostName()
+ * is made only if:
+ * 1) the refresh interval has passed (=result is stale)
+ * AND
+ * 2) no env vars that identify the hostname are found
+ */
+public class LocalhostResolver {
+
+ // default hostname env var names on Win/Nix
+ public static final String COMPUTERNAME = "COMPUTERNAME"; // Windows
+ public static final String HOSTNAME = "HOSTNAME"; // Nix
+
+ // how often should we refresh the cached hostname
+ public static long RefreshIntervalMillis = 60 * 1000;
+ // enables setting a custom env var used for resolving
+ public static String CustomEnvVarName = null;
+
+ // cached hostname result
+ private static String hostname;
+
+ // update (refresh) time management
+ private static long lastUpdate = 0;
+ private static long lastNetUpdate = 0;
+ public static long getLastUpdateTime() { return lastUpdate; }
+ public static long getLastNetUpdateTime() { return lastNetUpdate; }
+ public static void resetUpdateTimes() {
+ lastUpdate = 0;
+ lastNetUpdate = 0;
+ }
+
+ /**
+ * get resolved hostname.
+ * encapsulates all lookup and caching logic.
+ *
+ * @return the hostname
+ */
+ public static String getResolvedHostname() {
+ long now = System.currentTimeMillis();
+ if(now - RefreshIntervalMillis > lastUpdate) {
+ refreshResolve();
+ }
+
+ return hostname;
+ }
+
+ /**
+ * forces a new resolve even if refresh interval has not passed yet
+ */
+ public static void refreshResolve() {
+ try {
+ hostname = resolveByEnv();
+ if(hostname == null || hostname.isEmpty()) {
+ hostname = java.net.InetAddress.getLocalHost().getHostName();
+ lastNetUpdate = System.currentTimeMillis();
+ }
+ } catch (UnknownHostException e) {
+ //e.printStackTrace();
+ }
+ finally {
+ lastUpdate = System.currentTimeMillis();
+ }
+ }
+
+ /**
+ * try to resolve the hostname by env vars
+ *
+ * @return
+ */
+ private static String resolveByEnv() {
+ if(CustomEnvVarName != null) {
+ return System.getenv(CustomEnvVarName);
+ }
+
+ if(System.getProperty("os.name").startsWith("Windows")) {
+ return System.getenv(COMPUTERNAME);
+ }
+
+ return System.getenv(HOSTNAME);
+ }
+}
diff --git a/src/test/java/riemann/java/client/tests/LocalhostResolveTest.java b/src/test/java/riemann/java/client/tests/LocalhostResolveTest.java
new file mode 100644
index 0000000..5207564
--- /dev/null
+++ b/src/test/java/riemann/java/client/tests/LocalhostResolveTest.java
@@ -0,0 +1,146 @@
+package riemann.java.client.tests;
+
+import com.aphyr.riemann.client.LocalhostResolver;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class LocalhostResolveTest {
+
+ protected Map env = new HashMap();
+
+ protected static final String OS_NAME_PROP = "os.name";
+ protected static final String ExpectedWinHostname = "LocalHostResolverTestWin";
+ protected static final String ExpectedNixHostname = "LocalHostResolverTestNix";
+
+ @BeforeClass
+ public static void oneTimeSetUp() {
+ LocalhostResolver.RefreshIntervalMillis = 1000;
+ }
+
+ @Before
+ public void setup() {
+ env = new HashMap(System.getenv());
+ env.put(LocalhostResolver.HOSTNAME, ExpectedNixHostname);
+ env.put(LocalhostResolver.COMPUTERNAME, ExpectedWinHostname);
+ setEnv(env);
+
+ LocalhostResolver.CustomEnvVarName = null;
+ LocalhostResolver.resetUpdateTimes();
+ Assert.assertEquals(0, LocalhostResolver.getLastUpdateTime());
+ Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime());
+ }
+
+ @Test
+ public void testUpdateInterval() {
+ Assert.assertEquals(0, LocalhostResolver.getLastUpdateTime());
+ String hostname = LocalhostResolver.getResolvedHostname();
+ long lastUpdateTime = LocalhostResolver.getLastUpdateTime();
+ Assert.assertNotNull(hostname);
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ Assert.assertNotSame(lastUpdateTime, LocalhostResolver.getLastUpdateTime());
+ }
+
+ @Test
+ public void testNoEnvVars() throws UnknownHostException {
+ env.remove(LocalhostResolver.HOSTNAME);
+ env.remove(LocalhostResolver.COMPUTERNAME);
+ setEnv(env);
+
+ String hostname = LocalhostResolver.getResolvedHostname();
+ Assert.assertNotNull(hostname);
+
+ try {
+ Thread.sleep(1500);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ // ensure queried hostname without env vars
+ Assert.assertEquals(LocalhostResolver.getLastUpdateTime(), LocalhostResolver.getLastNetUpdateTime());
+ Assert.assertEquals(java.net.InetAddress.getLocalHost().getHostName(), hostname);
+ }
+
+ @Test
+ public void testEnvVarWindows() {
+ System.getProperties().put(OS_NAME_PROP, "Windows7");
+
+ String hostname = LocalhostResolver.getResolvedHostname();
+ Assert.assertEquals(ExpectedWinHostname, hostname);
+ Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime());
+ }
+
+ @Test
+ public void testEnvVarNix() {
+ System.getProperties().put(OS_NAME_PROP, "Linux");
+ String hostname = LocalhostResolver.getResolvedHostname();
+ Assert.assertEquals(ExpectedNixHostname, hostname);
+ Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime());
+ }
+
+ @Test
+ public void testCustomEnvVar() {
+ final String customHostnameEnvVar = "AWS_HOST";
+ final String customHostname = "EC2-LocalHostResolverTest";
+ env.put(customHostnameEnvVar, customHostname);
+ setEnv(env);
+
+ LocalhostResolver.CustomEnvVarName = customHostnameEnvVar;
+ String hostname = LocalhostResolver.getResolvedHostname();
+ Assert.assertEquals(customHostname, hostname);
+ Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime());
+ }
+
+
+ /**
+ * evil hack for testing (only!) with env var in-memory modification
+ * see: http://stackoverflow.com/a/7201825/1469004
+ *
+ * @param newEnv - to set in memory
+ */
+ protected static void setEnv(Map newEnv) {
+ try {
+ Class> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
+ Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
+ theEnvironmentField.setAccessible(true);
+ Map env = (Map) theEnvironmentField.get(null);
+ env.putAll(newEnv);
+ Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
+ theCaseInsensitiveEnvironmentField.setAccessible(true);
+ Map cienv = (Map) theCaseInsensitiveEnvironmentField.get(null);
+ cienv.putAll(newEnv);
+ }
+ catch (NoSuchFieldException e) {
+ try {
+ Class[] classes = Collections.class.getDeclaredClasses();
+ Map env = System.getenv();
+ for(Class cl : classes) {
+ if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
+ Field field = cl.getDeclaredField("m");
+ field.setAccessible(true);
+ Object obj = field.get(env);
+ Map map = (Map) obj;
+ map.clear();
+ map.putAll(newEnv);
+ }
+ }
+ } catch (Exception e2) {
+ e2.printStackTrace();
+ }
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ }
+}