1 package com.atlassian.httpclient.apache.httpcomponents;
2
3 import com.atlassian.event.api.EventPublisher;
4 import com.atlassian.httpclient.api.Response;
5 import com.atlassian.httpclient.api.ResponsePromise;
6 import com.atlassian.junit.http.jetty.JettyServer;
7 import com.atlassian.sal.api.ApplicationProperties;
8 import com.atlassian.sal.api.executor.ThreadLocalContextManager;
9 import com.google.common.base.Function;
10 import org.junit.Before;
11 import org.junit.ClassRule;
12 import org.junit.Test;
13 import org.junit.runner.RunWith;
14 import org.mockito.Mock;
15 import org.mockito.runners.MockitoJUnitRunner;
16
17 import java.net.URL;
18 import java.net.URLClassLoader;
19 import java.util.concurrent.atomic.AtomicBoolean;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertSame;
24 import static org.junit.Assert.assertTrue;
25 import static org.mockito.Mockito.when;
26
27 @RunWith(MockitoJUnitRunner.class)
28 public final class DefaultHttpClientIntegrationTest {
29 private static final ThreadLocal<Object> TEST_THREAD_LOCAL = new ThreadLocal<Object>();
30
31 private static final AtomicBoolean NO_OP_THREAD_LOCAL_CONTEXT_MANAGER = new AtomicBoolean(false);
32
33 @ClassRule
34 public static final JettyServer SERVER = new JettyServer();
35
36 private ApacheAsyncHttpClient httpClient;
37
38 @Mock
39 private EventPublisher eventPublisher;
40
41 @Mock
42 private ApplicationProperties applicationProperties;
43
44 @Before
45 public void setUp() {
46 NO_OP_THREAD_LOCAL_CONTEXT_MANAGER.set(false);
47
48 when(applicationProperties.getDisplayName()).thenReturn(DefaultHttpClientIntegrationTest.class.getSimpleName());
49 when(applicationProperties.getVersion()).thenReturn("1");
50 when(applicationProperties.getBuildNumber()).thenReturn("0001");
51
52 httpClient = new ApacheAsyncHttpClient<Object>(eventPublisher, applicationProperties, new ThreadLocalContextManager<Object>() {
53 @Override
54 public Object getThreadLocalContext() {
55 return NO_OP_THREAD_LOCAL_CONTEXT_MANAGER.get() ? null : TEST_THREAD_LOCAL.get();
56 }
57
58 @Override
59 public void setThreadLocalContext(Object context) {
60 if (!NO_OP_THREAD_LOCAL_CONTEXT_MANAGER.get()) {
61 TEST_THREAD_LOCAL.set(context);
62 }
63 }
64
65 @Override
66 public void clearThreadLocalContext() {
67 if (!NO_OP_THREAD_LOCAL_CONTEXT_MANAGER.get()) {
68 TEST_THREAD_LOCAL.set(null);
69 }
70 }
71 });
72 }
73
74 @Test
75 public void threadLocalVariablesAreAvailableToFunctionsWithWorkingThreadLocalContextManager() {
76 final AtomicBoolean okFunctionCalled = new AtomicBoolean(false);
77 final long testThreadId = Thread.currentThread().getId();
78
79 final Object objectInThreadLocal = new Object();
80 TEST_THREAD_LOCAL.set(objectInThreadLocal);
81
82 ResponsePromise responsePromise = httpClient.newRequest(SERVER.newUri("/test")).get();
83 final Object claimedObject = httpClient.transformation().ok(new Function<Response, Object>() {
84 @Override
85 public Object apply(Response response) {
86 okFunctionCalled.set(true);
87 assertTrue("For this test to work the function should be executed in a separate thread!", testThreadId != Thread.currentThread().getId());
88 assertTrue(Thread.currentThread().getName().startsWith("httpclient-callbacks"));
89 return TEST_THREAD_LOCAL.get();
90 }
91 }).build().apply(responsePromise).claim();
92
93 assertTrue(okFunctionCalled.get());
94 assertSame(objectInThreadLocal, claimedObject);
95 }
96
97 @Test
98 public void threadLocalVariablesAreNotAvailableToFunctionsWithNoOpThreadLocalContextManager() {
99 NO_OP_THREAD_LOCAL_CONTEXT_MANAGER.set(true);
100
101 final AtomicBoolean okFunctionCalled = new AtomicBoolean(false);
102 final long testThreadId = Thread.currentThread().getId();
103
104 final Object objectInThreadLocal = new Object();
105 TEST_THREAD_LOCAL.set(objectInThreadLocal);
106
107 ResponsePromise responsePromise = httpClient.newRequest(SERVER.newUri("/test")).get();
108 final Object claimedObject = httpClient.transformation().ok(new Function<Response, Object>() {
109 @Override
110 public Object apply(Response response) {
111 okFunctionCalled.set(true);
112 assertTrue("For this test to work the function should be executed in a separate thread!", testThreadId != Thread.currentThread().getId());
113 assertTrue(Thread.currentThread().getName().startsWith("httpclient-callbacks"));
114 return TEST_THREAD_LOCAL.get();
115 }
116 }).build().apply(responsePromise).claim();
117
118 assertTrue(okFunctionCalled.get());
119 assertNull(claimedObject);
120 }
121
122 @Test
123 public void contextClassLoaderSetForCallback() {
124 ClassLoader tmpClassLoader = new URLClassLoader(new URL[0]);
125 Thread.currentThread().setContextClassLoader(tmpClassLoader);
126
127 ResponsePromise responsePromise = httpClient.newRequest(SERVER.newUri("/test")).get();
128 ClassLoader callbackClassLoader = httpClient.<ClassLoader>transformation().ok(new Function<Response, ClassLoader>() {
129 @Override
130 public ClassLoader apply(Response response) {
131 return Thread.currentThread().getContextClassLoader();
132 }
133 }).build().apply(responsePromise).claim();
134 assertEquals(tmpClassLoader, callbackClassLoader);
135 }
136 }