diff --git a/intercom-java/src/main/java/io/intercom/api/Intercom.java b/intercom-java/src/main/java/io/intercom/api/Intercom.java index 0ac58502..9bc5e71d 100644 --- a/intercom-java/src/main/java/io/intercom/api/Intercom.java +++ b/intercom-java/src/main/java/io/intercom/api/Intercom.java @@ -4,11 +4,21 @@ public class Intercom { + static class Context { + private volatile AuthKeyType authKeyType = AuthKeyType.API_KEY; + private volatile String apiKey; + private volatile String token; + private volatile String appID; + private volatile int connectionTimeout = 3 * 1000; + private volatile int requestTimeout = 60 * 1000; + private volatile boolean requestUsingCaches = false; + } + private static final URI API_BASE_URI = URI.create("https://api.intercom.io/"); - private static volatile URI apiBaseURI = API_BASE_URI; + private static volatile boolean useThreadLocal = false; - private static volatile AuthKeyType authKeyType = AuthKeyType.API_KEY; + private static volatile URI apiBaseURI = API_BASE_URI; enum AuthKeyType { API_KEY, @@ -19,17 +29,16 @@ enum AuthKeyType { public static final String USER_AGENT = "intercom-java/" + Intercom.VERSION; - private static volatile String apiKey; + private static ThreadLocal threadContext = newThreadLocalContext(); - private static volatile String token; + private static final Context staticContext = new Context(); - private static volatile String appID; - - private static volatile int connectionTimeout = 3 * 1000; - - private static volatile int requestTimeout = 60 * 1000; - - private static volatile boolean requestUsingCaches = false; + private static Context getContext() { + if (useThreadLocal) { + return threadContext.get(); + } + return staticContext; + } private static volatile HttpConnectorSupplier httpConnectorSupplier = HttpConnectorSupplier.defaultSupplier; @@ -38,29 +47,29 @@ public static long currentTimestamp() { } public static int getConnectionTimeout() { - return connectionTimeout; + return getContext().connectionTimeout; } @SuppressWarnings("UnusedDeclaration") public static void setConnectionTimeout(int connectionTimeout) { - Intercom.connectionTimeout = connectionTimeout; + getContext().connectionTimeout = connectionTimeout; } public static int getRequestTimeout() { - return requestTimeout; + return getContext().requestTimeout; } @SuppressWarnings("UnusedDeclaration") public static void setRequestTimeout(int requestTimeout) { - Intercom.requestTimeout = requestTimeout; + getContext().requestTimeout = requestTimeout; } public static boolean isRequestUsingCaches() { - return requestUsingCaches; + return getContext().requestUsingCaches; } public static void setRequestUsingCaches(boolean requestUsingCaches) { - Intercom.requestUsingCaches = requestUsingCaches; + getContext().requestUsingCaches = requestUsingCaches; } public static HttpConnectorSupplier getHttpConnectorSupplier() { @@ -72,25 +81,29 @@ public static void setHttpConnectorSupplier(HttpConnectorSupplier supplier) { } public static String getAppID() { - return appID; + return getContext().appID; } public static void setAppID(String appID) { - Intercom.appID = appID; + getContext().appID = appID; } public static void setToken(String token) { - authKeyType = AuthKeyType.TOKEN; - Intercom.token = token; + Context context = getContext(); + context.authKeyType = AuthKeyType.TOKEN; + context.token = token; + context.apiKey = null; } public static String getApiKey() { - return Intercom.apiKey; + return getContext().apiKey; } public static void setApiKey(String apiKey) { - authKeyType = AuthKeyType.API_KEY; - Intercom.apiKey = apiKey; + Context context = getContext(); + context.authKeyType = AuthKeyType.API_KEY; + context.apiKey = apiKey; + context.token = null; } public static URI getApiBaseURI() { @@ -102,12 +115,34 @@ public static void setApiBaseURI(URI apiBaseURI) { } static AuthKeyType getAuthKeyType() { - return authKeyType; + return getContext().authKeyType; } public static String getToken() { - return token; + return getContext().token; } + public static boolean usesThreadLocal() { + return Intercom.useThreadLocal; + } + + public static void setUseThreadLocal(boolean useThreadLocal) { + Intercom.useThreadLocal = useThreadLocal; + } + + public static void clearThreadLocalContext() { + threadContext.remove(); + } + + public static void clearThreadLocalContexts() { + threadContext = newThreadLocalContext(); + } + private static ThreadLocal newThreadLocalContext() { + return new ThreadLocal() { + @Override protected Context initialValue() { + return new Context(); + } + }; + } } diff --git a/intercom-java/src/test/java/io/intercom/api/IntercomTest.java b/intercom-java/src/test/java/io/intercom/api/IntercomTest.java new file mode 100644 index 00000000..6c73f256 --- /dev/null +++ b/intercom-java/src/test/java/io/intercom/api/IntercomTest.java @@ -0,0 +1,209 @@ +package io.intercom.api; + +import org.junit.After; +import org.junit.Test; + +import java.util.Random; + +import static org.junit.Assert.*; + +public class IntercomTest { + + @After + public void tearDown() { + Intercom.setUseThreadLocal(false); + } + + @Test + public void testUseThreadLocal() { + Intercom.setUseThreadLocal(false); + Intercom.setToken("tx"); + assertFalse(Intercom.usesThreadLocal()); + assertEquals("tx", Intercom.getToken()); + Intercom.setUseThreadLocal(true); + assertTrue(Intercom.usesThreadLocal()); + assertNotEquals("tx", Intercom.getToken()); + Intercom.setUseThreadLocal(false); + assertFalse(Intercom.usesThreadLocal()); + assertEquals("tx", Intercom.getToken()); + } + + @Test + public void testApiKey() { + Intercom.setApiKey("k1"); + assertEquals("k1", Intercom.getApiKey()); + assertEquals(Intercom.AuthKeyType.API_KEY, Intercom.getAuthKeyType()); + assertNull(Intercom.getToken()); + } + + @Test + public void testToken() { + Intercom.setToken("t1"); + assertEquals("t1", Intercom.getToken()); + assertEquals(Intercom.AuthKeyType.TOKEN, Intercom.getAuthKeyType()); + assertNull(Intercom.getApiKey()); + } + + @Test + public void testStaticContext() throws Exception { + Intercom.setApiKey("k1"); + assertEquals("k1", Intercom.getApiKey()); + assertNull(Intercom.getToken()); + assertEquals(Intercom.AuthKeyType.API_KEY, Intercom.getAuthKeyType()); + Intercom.setAppID("app1"); + assertEquals("app1", Intercom.getAppID()); + Intercom.setConnectionTimeout(98765); + assertEquals(98765, Intercom.getConnectionTimeout()); + Intercom.setRequestTimeout(12345); + assertEquals(12345, Intercom.getRequestTimeout()); + Intercom.setRequestUsingCaches(true); + assertTrue(Intercom.isRequestUsingCaches()); + + ThreadTester tt1 = new ThreadTester(); + ThreadTester tt2 = new ThreadTester(); + new Thread(tt1).run(); + new Thread(tt2).run(); + tt1.waitUntilComplete(); + tt2.waitUntilComplete(); + + assertEquals(Intercom.getApiKey(), tt1.apiKey); + assertEquals(Intercom.getAuthKeyType(), tt1.authKeyType); + assertEquals(Intercom.getToken(), tt1.token); + assertEquals(Intercom.getConnectionTimeout(), tt1.connectionTimeout); + assertEquals(Intercom.getRequestTimeout(), tt1.requestTimeout); + assertEquals(Intercom.isRequestUsingCaches(), tt1.requestUsingCaches); + + assertEquals(Intercom.getApiKey(), tt2.apiKey); + assertEquals(Intercom.getAuthKeyType(), tt2.authKeyType); + assertEquals(Intercom.getToken(), tt2.token); + assertEquals(Intercom.getConnectionTimeout(), tt2.connectionTimeout); + assertEquals(Intercom.getRequestTimeout(), tt2.requestTimeout); + assertEquals(Intercom.isRequestUsingCaches(), tt2.requestUsingCaches); + } + + @Test + public void testThreadLocalContext() throws Exception { + Intercom.setUseThreadLocal(true); + + ThreadLocalTester1 tt1 = new ThreadLocalTester1(); + ThreadLocalTester2 tt2 = new ThreadLocalTester2(); + new Thread(tt1).run(); + new Thread(tt2).run(); + tt1.waitUntilComplete(); + tt2.waitUntilComplete(); + + assertEquals(tt1.localToken, tt1.token); + assertNull(tt1.apiKey); + assertEquals(Intercom.AuthKeyType.TOKEN, tt1.authKeyType); + assertEquals(tt1.localConnectionTimeout, tt1.connectionTimeout); + assertEquals(tt1.localRequestTimeout, tt1.requestTimeout); + assertEquals(tt1.localRequestUsingCaches, tt1.requestUsingCaches); + + assertEquals(tt2.localApiKey, tt2.apiKey); + assertNull(tt2.token); + assertEquals(Intercom.AuthKeyType.API_KEY, tt2.authKeyType); + assertEquals(tt2.localConnectionTimeout, tt2.connectionTimeout); + assertEquals(tt2.localRequestTimeout, tt2.requestTimeout); + assertEquals(tt2.localRequestUsingCaches, tt2.requestUsingCaches); + } + + @Test + public void testClearThreadLocalContexts() throws Exception { + Intercom.setUseThreadLocal(true); + + Intercom.setApiKey("testKey"); + assertEquals("testKey", Intercom.getApiKey()); + + Intercom.clearThreadLocalContexts(); + assertNull(Intercom.getApiKey()); + + Intercom.setApiKey("testKey2"); + assertEquals("testKey2", Intercom.getApiKey()); + } + + @Test + public void testClearThreadLocalContext() throws Exception { + Intercom.setUseThreadLocal(true); + + Intercom.setApiKey("testKey"); + assertEquals("testKey", Intercom.getApiKey()); + + Intercom.clearThreadLocalContext(); + assertNull(Intercom.getApiKey()); + + Intercom.setApiKey("testKey2"); + assertEquals("testKey2", Intercom.getApiKey()); + } + + class ThreadTester implements Runnable { + String apiKey, appId, token; + Intercom.AuthKeyType authKeyType; + int connectionTimeout = -1; + int requestTimeout = -1; + Boolean requestUsingCaches; + boolean completed = false; + + @Override + public void run() { + apiKey = Intercom.getApiKey(); + authKeyType = Intercom.getAuthKeyType(); + token = Intercom.getToken(); + appId = Intercom.getAppID(); + connectionTimeout = Intercom.getConnectionTimeout(); + requestTimeout = Intercom.getRequestTimeout(); + requestUsingCaches = Intercom.isRequestUsingCaches(); + completed = true; + synchronized (this) { + notify(); + } + } + + void waitUntilComplete() throws InterruptedException { + synchronized (this) { + while(!completed) { + wait(5000); + } + } + } + } + + class ThreadLocalTester1 extends ThreadTester { + final Random rnd = new Random(); + final String localToken = "tx"; + final String localAppId = "appx"; + final int localConnectionTimeout = rnd.nextInt(); + final int localRequestTimeout = rnd.nextInt(); + final boolean localRequestUsingCaches = rnd.nextBoolean(); + + @Override + public void run() { + Intercom.clearThreadLocalContext(); + Intercom.setToken(localToken); + Intercom.setAppID(localAppId); + Intercom.setConnectionTimeout(localConnectionTimeout); + Intercom.setRequestTimeout(localRequestTimeout); + Intercom.setRequestUsingCaches(localRequestUsingCaches); + super.run(); + } + } + + class ThreadLocalTester2 extends ThreadTester { + final Random rnd = new Random(); + final String localApiKey = "api"; + final String localAppId = "appId"; + final int localConnectionTimeout = rnd.nextInt(); + final int localRequestTimeout = rnd.nextInt(); + final boolean localRequestUsingCaches = rnd.nextBoolean(); + + @Override + public void run() { + Intercom.clearThreadLocalContext(); + Intercom.setApiKey(localApiKey); + Intercom.setAppID(localAppId); + Intercom.setConnectionTimeout(localConnectionTimeout); + Intercom.setRequestTimeout(localRequestTimeout); + Intercom.setRequestUsingCaches(localRequestUsingCaches); + super.run(); + } + } +} \ No newline at end of file