Skip to content
87 changes: 61 additions & 26 deletions intercom-java/src/main/java/io/intercom/api/Intercom.java
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -19,17 +29,16 @@ enum AuthKeyType {

public static final String USER_AGENT = "intercom-java/" + Intercom.VERSION;

private static volatile String apiKey;
private static ThreadLocal<Context> 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;

Expand All @@ -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() {
Expand All @@ -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() {
Expand All @@ -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<Context> newThreadLocalContext() {
return new ThreadLocal<Context>() {
@Override protected Context initialValue() {
return new Context();
}
};
}
}
209 changes: 209 additions & 0 deletions intercom-java/src/test/java/io/intercom/api/IntercomTest.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
}