Skip to content

Commit 7c2f24d

Browse files
pjfanningmmartinic
authored andcommitted
use thread local (optionally) for context setting (#204)
* use thread local (optionally) for context setting
1 parent d85acdd commit 7c2f24d

File tree

2 files changed

+270
-26
lines changed

2 files changed

+270
-26
lines changed

intercom-java/src/main/java/io/intercom/api/Intercom.java

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@
44

55
public class Intercom {
66

7+
static class Context {
8+
private volatile AuthKeyType authKeyType = AuthKeyType.API_KEY;
9+
private volatile String apiKey;
10+
private volatile String token;
11+
private volatile String appID;
12+
private volatile int connectionTimeout = 3 * 1000;
13+
private volatile int requestTimeout = 60 * 1000;
14+
private volatile boolean requestUsingCaches = false;
15+
}
16+
717
private static final URI API_BASE_URI = URI.create("https://api.intercom.io/");
818

9-
private static volatile URI apiBaseURI = API_BASE_URI;
19+
private static volatile boolean useThreadLocal = false;
1020

11-
private static volatile AuthKeyType authKeyType = AuthKeyType.API_KEY;
21+
private static volatile URI apiBaseURI = API_BASE_URI;
1222

1323
enum AuthKeyType {
1424
API_KEY,
@@ -19,17 +29,16 @@ enum AuthKeyType {
1929

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

22-
private static volatile String apiKey;
32+
private static ThreadLocal<Context> threadContext = newThreadLocalContext();
2333

24-
private static volatile String token;
34+
private static final Context staticContext = new Context();
2535

26-
private static volatile String appID;
27-
28-
private static volatile int connectionTimeout = 3 * 1000;
29-
30-
private static volatile int requestTimeout = 60 * 1000;
31-
32-
private static volatile boolean requestUsingCaches = false;
36+
private static Context getContext() {
37+
if (useThreadLocal) {
38+
return threadContext.get();
39+
}
40+
return staticContext;
41+
}
3342

3443
private static volatile HttpConnectorSupplier httpConnectorSupplier = HttpConnectorSupplier.defaultSupplier;
3544

@@ -38,29 +47,29 @@ public static long currentTimestamp() {
3847
}
3948

4049
public static int getConnectionTimeout() {
41-
return connectionTimeout;
50+
return getContext().connectionTimeout;
4251
}
4352

4453
@SuppressWarnings("UnusedDeclaration")
4554
public static void setConnectionTimeout(int connectionTimeout) {
46-
Intercom.connectionTimeout = connectionTimeout;
55+
getContext().connectionTimeout = connectionTimeout;
4756
}
4857

4958
public static int getRequestTimeout() {
50-
return requestTimeout;
59+
return getContext().requestTimeout;
5160
}
5261

5362
@SuppressWarnings("UnusedDeclaration")
5463
public static void setRequestTimeout(int requestTimeout) {
55-
Intercom.requestTimeout = requestTimeout;
64+
getContext().requestTimeout = requestTimeout;
5665
}
5766

5867
public static boolean isRequestUsingCaches() {
59-
return requestUsingCaches;
68+
return getContext().requestUsingCaches;
6069
}
6170

6271
public static void setRequestUsingCaches(boolean requestUsingCaches) {
63-
Intercom.requestUsingCaches = requestUsingCaches;
72+
getContext().requestUsingCaches = requestUsingCaches;
6473
}
6574

6675
public static HttpConnectorSupplier getHttpConnectorSupplier() {
@@ -72,25 +81,29 @@ public static void setHttpConnectorSupplier(HttpConnectorSupplier supplier) {
7281
}
7382

7483
public static String getAppID() {
75-
return appID;
84+
return getContext().appID;
7685
}
7786

7887
public static void setAppID(String appID) {
79-
Intercom.appID = appID;
88+
getContext().appID = appID;
8089
}
8190

8291
public static void setToken(String token) {
83-
authKeyType = AuthKeyType.TOKEN;
84-
Intercom.token = token;
92+
Context context = getContext();
93+
context.authKeyType = AuthKeyType.TOKEN;
94+
context.token = token;
95+
context.apiKey = null;
8596
}
8697

8798
public static String getApiKey() {
88-
return Intercom.apiKey;
99+
return getContext().apiKey;
89100
}
90101

91102
public static void setApiKey(String apiKey) {
92-
authKeyType = AuthKeyType.API_KEY;
93-
Intercom.apiKey = apiKey;
103+
Context context = getContext();
104+
context.authKeyType = AuthKeyType.API_KEY;
105+
context.apiKey = apiKey;
106+
context.token = null;
94107
}
95108

96109
public static URI getApiBaseURI() {
@@ -102,12 +115,34 @@ public static void setApiBaseURI(URI apiBaseURI) {
102115
}
103116

104117
static AuthKeyType getAuthKeyType() {
105-
return authKeyType;
118+
return getContext().authKeyType;
106119
}
107120

108121
public static String getToken() {
109-
return token;
122+
return getContext().token;
110123
}
111124

125+
public static boolean usesThreadLocal() {
126+
return Intercom.useThreadLocal;
127+
}
128+
129+
public static void setUseThreadLocal(boolean useThreadLocal) {
130+
Intercom.useThreadLocal = useThreadLocal;
131+
}
132+
133+
public static void clearThreadLocalContext() {
134+
threadContext.remove();
135+
}
136+
137+
public static void clearThreadLocalContexts() {
138+
threadContext = newThreadLocalContext();
139+
}
112140

141+
private static ThreadLocal<Context> newThreadLocalContext() {
142+
return new ThreadLocal<Context>() {
143+
@Override protected Context initialValue() {
144+
return new Context();
145+
}
146+
};
147+
}
113148
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package io.intercom.api;
2+
3+
import org.junit.After;
4+
import org.junit.Test;
5+
6+
import java.util.Random;
7+
8+
import static org.junit.Assert.*;
9+
10+
public class IntercomTest {
11+
12+
@After
13+
public void tearDown() {
14+
Intercom.setUseThreadLocal(false);
15+
}
16+
17+
@Test
18+
public void testUseThreadLocal() {
19+
Intercom.setUseThreadLocal(false);
20+
Intercom.setToken("tx");
21+
assertFalse(Intercom.usesThreadLocal());
22+
assertEquals("tx", Intercom.getToken());
23+
Intercom.setUseThreadLocal(true);
24+
assertTrue(Intercom.usesThreadLocal());
25+
assertNotEquals("tx", Intercom.getToken());
26+
Intercom.setUseThreadLocal(false);
27+
assertFalse(Intercom.usesThreadLocal());
28+
assertEquals("tx", Intercom.getToken());
29+
}
30+
31+
@Test
32+
public void testApiKey() {
33+
Intercom.setApiKey("k1");
34+
assertEquals("k1", Intercom.getApiKey());
35+
assertEquals(Intercom.AuthKeyType.API_KEY, Intercom.getAuthKeyType());
36+
assertNull(Intercom.getToken());
37+
}
38+
39+
@Test
40+
public void testToken() {
41+
Intercom.setToken("t1");
42+
assertEquals("t1", Intercom.getToken());
43+
assertEquals(Intercom.AuthKeyType.TOKEN, Intercom.getAuthKeyType());
44+
assertNull(Intercom.getApiKey());
45+
}
46+
47+
@Test
48+
public void testStaticContext() throws Exception {
49+
Intercom.setApiKey("k1");
50+
assertEquals("k1", Intercom.getApiKey());
51+
assertNull(Intercom.getToken());
52+
assertEquals(Intercom.AuthKeyType.API_KEY, Intercom.getAuthKeyType());
53+
Intercom.setAppID("app1");
54+
assertEquals("app1", Intercom.getAppID());
55+
Intercom.setConnectionTimeout(98765);
56+
assertEquals(98765, Intercom.getConnectionTimeout());
57+
Intercom.setRequestTimeout(12345);
58+
assertEquals(12345, Intercom.getRequestTimeout());
59+
Intercom.setRequestUsingCaches(true);
60+
assertTrue(Intercom.isRequestUsingCaches());
61+
62+
ThreadTester tt1 = new ThreadTester();
63+
ThreadTester tt2 = new ThreadTester();
64+
new Thread(tt1).run();
65+
new Thread(tt2).run();
66+
tt1.waitUntilComplete();
67+
tt2.waitUntilComplete();
68+
69+
assertEquals(Intercom.getApiKey(), tt1.apiKey);
70+
assertEquals(Intercom.getAuthKeyType(), tt1.authKeyType);
71+
assertEquals(Intercom.getToken(), tt1.token);
72+
assertEquals(Intercom.getConnectionTimeout(), tt1.connectionTimeout);
73+
assertEquals(Intercom.getRequestTimeout(), tt1.requestTimeout);
74+
assertEquals(Intercom.isRequestUsingCaches(), tt1.requestUsingCaches);
75+
76+
assertEquals(Intercom.getApiKey(), tt2.apiKey);
77+
assertEquals(Intercom.getAuthKeyType(), tt2.authKeyType);
78+
assertEquals(Intercom.getToken(), tt2.token);
79+
assertEquals(Intercom.getConnectionTimeout(), tt2.connectionTimeout);
80+
assertEquals(Intercom.getRequestTimeout(), tt2.requestTimeout);
81+
assertEquals(Intercom.isRequestUsingCaches(), tt2.requestUsingCaches);
82+
}
83+
84+
@Test
85+
public void testThreadLocalContext() throws Exception {
86+
Intercom.setUseThreadLocal(true);
87+
88+
ThreadLocalTester1 tt1 = new ThreadLocalTester1();
89+
ThreadLocalTester2 tt2 = new ThreadLocalTester2();
90+
new Thread(tt1).run();
91+
new Thread(tt2).run();
92+
tt1.waitUntilComplete();
93+
tt2.waitUntilComplete();
94+
95+
assertEquals(tt1.localToken, tt1.token);
96+
assertNull(tt1.apiKey);
97+
assertEquals(Intercom.AuthKeyType.TOKEN, tt1.authKeyType);
98+
assertEquals(tt1.localConnectionTimeout, tt1.connectionTimeout);
99+
assertEquals(tt1.localRequestTimeout, tt1.requestTimeout);
100+
assertEquals(tt1.localRequestUsingCaches, tt1.requestUsingCaches);
101+
102+
assertEquals(tt2.localApiKey, tt2.apiKey);
103+
assertNull(tt2.token);
104+
assertEquals(Intercom.AuthKeyType.API_KEY, tt2.authKeyType);
105+
assertEquals(tt2.localConnectionTimeout, tt2.connectionTimeout);
106+
assertEquals(tt2.localRequestTimeout, tt2.requestTimeout);
107+
assertEquals(tt2.localRequestUsingCaches, tt2.requestUsingCaches);
108+
}
109+
110+
@Test
111+
public void testClearThreadLocalContexts() throws Exception {
112+
Intercom.setUseThreadLocal(true);
113+
114+
Intercom.setApiKey("testKey");
115+
assertEquals("testKey", Intercom.getApiKey());
116+
117+
Intercom.clearThreadLocalContexts();
118+
assertNull(Intercom.getApiKey());
119+
120+
Intercom.setApiKey("testKey2");
121+
assertEquals("testKey2", Intercom.getApiKey());
122+
}
123+
124+
@Test
125+
public void testClearThreadLocalContext() throws Exception {
126+
Intercom.setUseThreadLocal(true);
127+
128+
Intercom.setApiKey("testKey");
129+
assertEquals("testKey", Intercom.getApiKey());
130+
131+
Intercom.clearThreadLocalContext();
132+
assertNull(Intercom.getApiKey());
133+
134+
Intercom.setApiKey("testKey2");
135+
assertEquals("testKey2", Intercom.getApiKey());
136+
}
137+
138+
class ThreadTester implements Runnable {
139+
String apiKey, appId, token;
140+
Intercom.AuthKeyType authKeyType;
141+
int connectionTimeout = -1;
142+
int requestTimeout = -1;
143+
Boolean requestUsingCaches;
144+
boolean completed = false;
145+
146+
@Override
147+
public void run() {
148+
apiKey = Intercom.getApiKey();
149+
authKeyType = Intercom.getAuthKeyType();
150+
token = Intercom.getToken();
151+
appId = Intercom.getAppID();
152+
connectionTimeout = Intercom.getConnectionTimeout();
153+
requestTimeout = Intercom.getRequestTimeout();
154+
requestUsingCaches = Intercom.isRequestUsingCaches();
155+
completed = true;
156+
synchronized (this) {
157+
notify();
158+
}
159+
}
160+
161+
void waitUntilComplete() throws InterruptedException {
162+
synchronized (this) {
163+
while(!completed) {
164+
wait(5000);
165+
}
166+
}
167+
}
168+
}
169+
170+
class ThreadLocalTester1 extends ThreadTester {
171+
final Random rnd = new Random();
172+
final String localToken = "tx";
173+
final String localAppId = "appx";
174+
final int localConnectionTimeout = rnd.nextInt();
175+
final int localRequestTimeout = rnd.nextInt();
176+
final boolean localRequestUsingCaches = rnd.nextBoolean();
177+
178+
@Override
179+
public void run() {
180+
Intercom.clearThreadLocalContext();
181+
Intercom.setToken(localToken);
182+
Intercom.setAppID(localAppId);
183+
Intercom.setConnectionTimeout(localConnectionTimeout);
184+
Intercom.setRequestTimeout(localRequestTimeout);
185+
Intercom.setRequestUsingCaches(localRequestUsingCaches);
186+
super.run();
187+
}
188+
}
189+
190+
class ThreadLocalTester2 extends ThreadTester {
191+
final Random rnd = new Random();
192+
final String localApiKey = "api";
193+
final String localAppId = "appId";
194+
final int localConnectionTimeout = rnd.nextInt();
195+
final int localRequestTimeout = rnd.nextInt();
196+
final boolean localRequestUsingCaches = rnd.nextBoolean();
197+
198+
@Override
199+
public void run() {
200+
Intercom.clearThreadLocalContext();
201+
Intercom.setApiKey(localApiKey);
202+
Intercom.setAppID(localAppId);
203+
Intercom.setConnectionTimeout(localConnectionTimeout);
204+
Intercom.setRequestTimeout(localRequestTimeout);
205+
Intercom.setRequestUsingCaches(localRequestUsingCaches);
206+
super.run();
207+
}
208+
}
209+
}

0 commit comments

Comments
 (0)