Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit a6ff4d5

Browse files
Adam Lindenthalpavelbucek
authored andcommitted
LocationHeaderTest Refactoring
Change-Id: I0186464d3ed00c6a02cd21ba25122e975a97d5a9
1 parent 632888b commit a6ff4d5

File tree

3 files changed

+674
-417
lines changed

3 files changed

+674
-417
lines changed
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
package org.glassfish.jersey.tests.api;
41+
42+
import java.net.URI;
43+
import java.util.concurrent.ArrayBlockingQueue;
44+
import java.util.concurrent.BlockingQueue;
45+
import java.util.concurrent.ExecutionException;
46+
import java.util.concurrent.ExecutorService;
47+
import java.util.concurrent.Executors;
48+
import java.util.concurrent.Future;
49+
import java.util.concurrent.TimeUnit;
50+
import java.util.concurrent.atomic.AtomicBoolean;
51+
import java.util.logging.Logger;
52+
53+
import javax.ws.rs.GET;
54+
import javax.ws.rs.Path;
55+
import javax.ws.rs.container.AsyncResponse;
56+
import javax.ws.rs.container.Suspended;
57+
import javax.ws.rs.core.Context;
58+
import javax.ws.rs.core.HttpHeaders;
59+
import javax.ws.rs.core.Response;
60+
import javax.ws.rs.core.UriBuilder;
61+
import javax.ws.rs.core.UriInfo;
62+
63+
import org.glassfish.jersey.server.ManagedAsync;
64+
import org.glassfish.jersey.server.ResourceConfig;
65+
import org.glassfish.jersey.test.JerseyTest;
66+
import org.glassfish.jersey.test.TestProperties;
67+
import org.glassfish.jersey.test.util.runner.ConcurrentRunner;
68+
69+
import org.junit.Test;
70+
import org.junit.runner.RunWith;
71+
72+
import static org.junit.Assert.assertEquals;
73+
import static org.junit.Assert.assertFalse;
74+
import static org.junit.Assert.assertNotEquals;
75+
import static org.junit.Assert.assertTrue;
76+
77+
/**
78+
* Test if the location relativer URI is correctly resolved within asynchronous processing cases.
79+
*
80+
* @author Adam Lindenthal (adam.lindenthal at oracle.com)
81+
*/
82+
@RunWith(ConcurrentRunner.class)
83+
public class LocationHeaderAsyncTest extends JerseyTest {
84+
85+
private static final Logger LOGGER = Logger.getLogger(LocationHeaderAsyncTest.class.getName());
86+
static ExecutorService executor;
87+
88+
private static final AtomicBoolean executorComparisonFailed = new AtomicBoolean(false);
89+
private static final AtomicBoolean interrupted = new AtomicBoolean(false);
90+
91+
@Override
92+
protected ResourceConfig configure() {
93+
enable(TestProperties.LOG_TRAFFIC);
94+
return new ResourceConfig(ResponseTest.class);
95+
}
96+
97+
/**
98+
* Prepare test infrastructure.
99+
*
100+
* In this case it prepares executor thread pool of size one and initializes the thread.
101+
* @throws Exception
102+
*/
103+
@Override
104+
public void setUp() throws Exception {
105+
super.setUp();
106+
/* thread pool for custom executor async test */
107+
LocationHeaderAsyncTest.executor = Executors.newFixedThreadPool(1);
108+
109+
// Force the thread to be eagerly instantiated - this prevents the instantiation later and ensures, that the thread
110+
// will not be a child thread of the request handling thread, so the thread-local baseUri variable will not be inherited.
111+
LocationHeaderAsyncTest.executor.submit(new Runnable() {
112+
@Override
113+
public void run() {
114+
LOGGER.info("Thread pool initialized.");
115+
}
116+
});
117+
}
118+
119+
/**
120+
* Test JAX-RS resource
121+
*/
122+
@SuppressWarnings("VoidMethodAnnotatedWithGET")
123+
@Path(value = "/ResponseTest")
124+
public static class ResponseTest {
125+
126+
/* injected request URI for assertions in the resource methods */
127+
@Context
128+
private UriInfo uriInfo;
129+
130+
/**
131+
* Asynchronous resource method for testing if the URI is absolutized also in case of asynchronous processing;
132+
*
133+
* The response is created in the separate thread. This tests, that the thread still has access to the request baseUri
134+
* thread-local variable in {@link org.glassfish.jersey.message.internal.OutboundJaxrsResponse.Builder}.
135+
*/
136+
@GET
137+
@Path("locationAsync")
138+
public void locationAsync(@Suspended final AsyncResponse asyncResponse) {
139+
new Thread(new Runnable() {
140+
@Override
141+
public void run() {
142+
final URI uri = getUriBuilder().segment("locationAsync").build();
143+
final Response result = Response.created(uri).build();
144+
145+
final URI location = result.getLocation();
146+
if (uriInfo.getAbsolutePath().equals(location)) {
147+
asyncResponse.resume(result);
148+
} else {
149+
asyncResponse.resume(Response.serverError().entity(location.toString()).build());
150+
}
151+
152+
}
153+
}).start();
154+
}
155+
156+
/**
157+
* Resource method for async test with custom executor.
158+
*
159+
* It runs in a thread that was not created within the request scope, so it does not inherit the baseUri thread-local
160+
* variable value.
161+
* In this case, URI will not be absolutized until calling {@link AsyncResponse#resume(Object)}.
162+
*/
163+
@GET
164+
@Path("executorAsync")
165+
@ManagedAsync
166+
public void executorAsync(@Suspended final AsyncResponse asyncResponse) {
167+
LocationHeaderAsyncTest.executor.submit(new Runnable() {
168+
@Override
169+
public void run() {
170+
final URI uri = getUriBuilder().segment("executorAsync").build();
171+
final Response result = Response.created(uri).build();
172+
asyncResponse.resume(result);
173+
if (!uriInfo.getAbsolutePath().equals(result.getLocation())) {
174+
executorComparisonFailed.set(true);
175+
}
176+
}
177+
});
178+
}
179+
180+
/**
181+
* Placeholder for the suspended async responses;
182+
* For the current test a simple static field would be enough, but this is easily extensible;
183+
*
184+
* This is inspired by the {@link AsyncResponse} javadoc example
185+
*/
186+
private static final BlockingQueue<AsyncResponse> suspended = new ArrayBlockingQueue<>(5);
187+
188+
/**
189+
* Start of the async test - stores the asynchronous response object
190+
*/
191+
@GET
192+
@Path("locationAsyncStart")
193+
public void locationAsyncStart(@Suspended final AsyncResponse asyncResponse) {
194+
new Thread(new Runnable() {
195+
@Override
196+
public void run() {
197+
try {
198+
suspended.put(asyncResponse);
199+
} catch (final InterruptedException e) {
200+
asyncResponse.cancel();
201+
Thread.currentThread().interrupt();
202+
interrupted.set(true);
203+
}
204+
}
205+
}).start();
206+
}
207+
208+
/**
209+
* Finish of the async test - creates a response, checks the location header and resumes the asyncResponse
210+
* @return true if the URI was correctly absolutized, false if the URI is relative or differs from the expected URI
211+
*/
212+
@GET
213+
@Path("locationAsyncFinish")
214+
public Boolean locationAsyncFinish() throws InterruptedException {
215+
final AsyncResponse asyncResponse = suspended.poll(2000, TimeUnit.MILLISECONDS);
216+
217+
final URI uri = getUriBuilder().segment("locationAsyncFinish").build();
218+
final Response result = Response.created(uri).build();
219+
final boolean wasEqual = result.getLocation().equals(uriInfo.getAbsolutePath());
220+
221+
asyncResponse.resume(result);
222+
return wasEqual;
223+
}
224+
225+
/** Return UriBuilder with base pre-set {@code /ResponseTest} uri segment for this resource.
226+
*
227+
* @return UriBuilder
228+
*/
229+
private UriBuilder getUriBuilder() {
230+
return UriBuilder.fromResource(ResponseTest.class);
231+
}
232+
}
233+
234+
/**
235+
* Basic asynchronous testcase; checks if the URI is correctly absolutized also within a separate thread during
236+
* async processing
237+
*/
238+
@Test
239+
public void testAsync() {
240+
final String expectedUri = getBaseUri() + "ResponseTest/locationAsync";
241+
final Response response = target().path("ResponseTest/locationAsync").request().get(Response.class);
242+
243+
final String msg = String.format("Comparison failed in the resource method. \nExpected: %1$s\nActual: %2$s",
244+
expectedUri, response.readEntity(String.class));
245+
assertNotEquals(msg, response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
246+
247+
final String location = response.getHeaderString(HttpHeaders.LOCATION);
248+
LOGGER.info("Location resolved from response > " + location);
249+
assertEquals(expectedUri, location);
250+
}
251+
252+
/**
253+
* Test with a thread from thread-pool (created out of request scope)
254+
*/
255+
@Test
256+
public void testExecutorAsync() {
257+
final Response response = target().path("ResponseTest/executorAsync").request().get(Response.class);
258+
final String location = response.getHeaderString(HttpHeaders.LOCATION);
259+
LOGGER.info("Location resolved from response > " + location);
260+
assertFalse("The comparison failed in the resource method.", executorComparisonFailed.get());
261+
assertEquals(getBaseUri() + "ResponseTest/executorAsync", location);
262+
}
263+
264+
/**
265+
* Asynchronous testcase split over two distinct requests
266+
*/
267+
@Test
268+
public void testSeparatedAsync() throws ExecutionException, InterruptedException {
269+
final Future<Response> futureResponse = target().path("ResponseTest/locationAsyncStart").request().async().get();
270+
final Boolean result = target().path("ResponseTest/locationAsyncFinish").request().get(Boolean.class);
271+
assertFalse("Thread was interrupted on inserting into blocking queue.", interrupted.get());
272+
assertTrue(result);
273+
274+
final Response response = futureResponse.get();
275+
final String location = response.getHeaderString(HttpHeaders.LOCATION);
276+
assertEquals(getBaseUri() + "ResponseTest/locationAsyncFinish", location);
277+
}
278+
}
279+
280+
281+

0 commit comments

Comments
 (0)