Skip to content

Commit 6b16add

Browse files
author
Venkat
committed
Unit tests for Jersey Netty Http Container
Current unit tests only validate Netty's `JerseyClientHandler` against a Grizzly http server. This commit adds test cases to validate the same using Netty's `JerseyServerHandler`. Also adding extra test cases to effectively validate the bug fixes for eclipse-ee4j#3500 and eclipse-ee4j#3568 (that is, these new test cases will fail if ran against the code base older than eclipse-ee4j#4312) Fixes eclipse-ee4j#3500 and eclipse-ee4j#3568 Signed-off-by: Venkat Ganesh <010gvr@gmail.com>
1 parent 8dcfed4 commit 6b16add

File tree

6 files changed

+703
-0
lines changed

6 files changed

+703
-0
lines changed

containers/netty-http/pom.xml

+27
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,33 @@
4242
<artifactId>jersey-netty-connector</artifactId>
4343
<version>${project.version}</version>
4444
</dependency>
45+
<dependency>
46+
<groupId>org.glassfish.jersey.test-framework</groupId>
47+
<artifactId>jersey-test-framework-core</artifactId>
48+
<version>${project.version}</version>
49+
<scope>test</scope>
50+
</dependency>
51+
<!-- <dependency>
52+
<groupId>org.glassfish.jersey.media</groupId>
53+
<artifactId>jersey-media-json-jackson</artifactId>
54+
<version>${project.version}</version>
55+
<scope>test</scope>
56+
</dependency> -->
57+
<dependency>
58+
<groupId>com.google.guava</groupId>
59+
<artifactId>guava</artifactId>
60+
<scope>test</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.glassfish.jersey.inject</groupId>
64+
<artifactId>jersey-hk2</artifactId>
65+
<version>${project.version}</version>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.glassfish.jersey.core</groupId>
69+
<artifactId>jersey-server</artifactId>
70+
<version>${project.version}</version>
71+
</dependency>
4572
</dependencies>
4673

4774
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.netty.httpserver;
18+
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertTrue;
21+
import java.util.concurrent.CountDownLatch;
22+
import java.util.concurrent.TimeUnit;
23+
import java.util.logging.Logger;
24+
import javax.ws.rs.GET;
25+
import javax.ws.rs.Path;
26+
import javax.ws.rs.Produces;
27+
import javax.ws.rs.client.InvocationCallback;
28+
import javax.ws.rs.core.Application;
29+
import javax.ws.rs.core.MediaType;
30+
import javax.ws.rs.core.Response;
31+
import org.glassfish.jersey.client.ClientConfig;
32+
import org.glassfish.jersey.client.ClientProperties;
33+
import org.glassfish.jersey.logging.LoggingFeature;
34+
import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
35+
import org.glassfish.jersey.server.ResourceConfig;
36+
import org.glassfish.jersey.test.JerseyTest;
37+
import org.junit.Test;
38+
39+
/**
40+
* @author Pavel Bucek
41+
*/
42+
public class HelloWorldTest extends JerseyTest {
43+
44+
private static final Logger LOGGER = Logger.getLogger(HelloWorldTest.class.getName());
45+
private static final String ROOT_PATH = "helloworld";
46+
47+
public HelloWorldTest() {
48+
super(new NettyTestContainerFactory());
49+
}
50+
51+
@Path("helloworld")
52+
public static class HelloWorldResource {
53+
public static final String CLICHED_MESSAGE = "Hello World!";
54+
55+
@GET
56+
@Produces("text/plain")
57+
public String getHello() {
58+
return CLICHED_MESSAGE;
59+
}
60+
}
61+
62+
@Override
63+
protected Application configure() {
64+
ResourceConfig config = new ResourceConfig(HelloWorldResource.class);
65+
config.register(new LoggingFeature(LOGGER, LoggingFeature.Verbosity.PAYLOAD_ANY));
66+
return config;
67+
}
68+
69+
@Override
70+
protected void configureClient(ClientConfig config) {
71+
config.property(ClientProperties.ASYNC_THREADPOOL_SIZE, 20);
72+
config.connectorProvider(new NettyConnectorProvider());
73+
}
74+
75+
@Test
76+
public void testConnection() {
77+
Response response = target().path(ROOT_PATH).request("text/plain").get();
78+
assertEquals(200, response.getStatus());
79+
}
80+
81+
@Test
82+
public void testClientStringResponse() {
83+
String s = target().path(ROOT_PATH).request().get(String.class);
84+
assertEquals(HelloWorldResource.CLICHED_MESSAGE, s);
85+
}
86+
87+
@Test
88+
public void testAsyncClientRequests() throws InterruptedException {
89+
final int REQUESTS = 20;
90+
final CountDownLatch latch = new CountDownLatch(REQUESTS);
91+
final long tic = System.currentTimeMillis();
92+
for (int i = 0; i < REQUESTS; i++) {
93+
final int id = i;
94+
target().path(ROOT_PATH).request().async().get(new InvocationCallback<Response>() {
95+
@Override
96+
public void completed(Response response) {
97+
try {
98+
final String result = response.readEntity(String.class);
99+
assertEquals(HelloWorldResource.CLICHED_MESSAGE, result);
100+
} finally {
101+
latch.countDown();
102+
}
103+
}
104+
105+
@Override
106+
public void failed(Throwable error) {
107+
error.printStackTrace();
108+
latch.countDown();
109+
}
110+
});
111+
}
112+
assertTrue(latch.await(10 * getAsyncTimeoutMultiplier(), TimeUnit.SECONDS));
113+
final long toc = System.currentTimeMillis();
114+
Logger.getLogger(HelloWorldTest.class.getName()).info("Executed in: " + (toc - tic));
115+
}
116+
117+
@Test
118+
public void testHead() {
119+
Response response = target().path(ROOT_PATH).request().head();
120+
assertEquals(200, response.getStatus());
121+
assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
122+
}
123+
124+
@Test
125+
public void testFooBarOptions() {
126+
Response response = target().path(ROOT_PATH).request().header("Accept", "foo/bar").options();
127+
assertEquals(200, response.getStatus());
128+
final String allowHeader = response.getHeaderString("Allow");
129+
_checkAllowContent(allowHeader);
130+
assertEquals("foo/bar", response.getMediaType().toString());
131+
assertEquals(0, response.getLength());
132+
}
133+
134+
@Test
135+
public void testTextPlainOptions() {
136+
Response response = target().path(ROOT_PATH).request().header("Accept", MediaType.TEXT_PLAIN).options();
137+
assertEquals(200, response.getStatus());
138+
final String allowHeader = response.getHeaderString("Allow");
139+
_checkAllowContent(allowHeader);
140+
assertEquals(MediaType.TEXT_PLAIN_TYPE, response.getMediaType());
141+
final String responseBody = response.readEntity(String.class);
142+
_checkAllowContent(responseBody);
143+
}
144+
145+
public void testJson() {
146+
Response response = target().path(ROOT_PATH).request().header("Accept", MediaType.APPLICATION_JSON).options();
147+
assertEquals(200, response.getStatus());
148+
final String allowHeader = response.getHeaderString("Allow");
149+
_checkAllowContent(allowHeader);
150+
assertEquals(MediaType.APPLICATION_JSON, response.getMediaType());
151+
final String responseBody = response.readEntity(String.class);
152+
_checkAllowContent(responseBody);
153+
154+
}
155+
156+
private void _checkAllowContent(final String content) {
157+
assertTrue(content.contains("GET"));
158+
assertTrue(content.contains("HEAD"));
159+
assertTrue(content.contains("OPTIONS"));
160+
}
161+
162+
@Test
163+
public void testMissingResourceNotFound() {
164+
Response response;
165+
166+
response = target().path(ROOT_PATH + "arbitrary").request().get();
167+
assertEquals(404, response.getStatus());
168+
response.close();
169+
170+
response = target().path(ROOT_PATH).path("arbitrary").request().get();
171+
assertEquals(404, response.getStatus());
172+
response.close();
173+
}
174+
175+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* *****************************************************************
3+
*
4+
* IBM Confidential
5+
* OCO Source Materials
6+
*
7+
* clu-algorithms-service
8+
* (C) Copyright IBM Corp. 2018 All Rights Reserved.
9+
*
10+
* The source code for this program is not published or otherwise
11+
* divested of its trade secrets, irrespective of what has been
12+
* deposited with the U.S. Copyright Office.
13+
*
14+
* US Government Users Restricted Rights - Use, duplication or
15+
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
16+
*
17+
* ***************************************************************** */
18+
package org.glassfish.jersey.netty.httpserver;
19+
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.io.OutputStream;
23+
import java.lang.annotation.Annotation;
24+
import java.lang.reflect.Type;
25+
import javax.ws.rs.WebApplicationException;
26+
import javax.ws.rs.core.MediaType;
27+
import javax.ws.rs.core.MultivaluedMap;
28+
import javax.ws.rs.ext.MessageBodyWriter;
29+
import org.glassfish.jersey.netty.httpserver.HugeEntityTest.TestEntity;
30+
31+
public class Helper {
32+
33+
public static final int ONE_MB_IN_BYTES = 1024 * 1024; // 1M
34+
public static final long TWENTY_GB_IN_BYTES = 20L * 1024L * 1024L * 1024L; // 20G seems sufficient
35+
36+
public static long drainAndCountInputStream(InputStream in) throws IOException {
37+
long totalBytesRead = 0L;
38+
39+
byte[] buffer = new byte[ONE_MB_IN_BYTES];
40+
int read;
41+
do {
42+
read = in.read(buffer);
43+
if (read > 0) {
44+
totalBytesRead += read;
45+
}
46+
} while (read != -1);
47+
48+
return totalBytesRead;
49+
}
50+
51+
/**
52+
* Utility writer that generates that many zero bytes as given by the input entity size field.
53+
*/
54+
public static class TestEntityWriter implements MessageBodyWriter<TestEntity> {
55+
56+
@Override
57+
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
58+
return type == TestEntity.class;
59+
}
60+
61+
@Override
62+
public long getSize(TestEntity t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
63+
return -1; // no matter what we return here, the output will get chunk-encoded
64+
}
65+
66+
@Override
67+
public void writeTo(TestEntity t,
68+
Class<?> type,
69+
Type genericType,
70+
Annotation[] annotations,
71+
MediaType mediaType,
72+
MultivaluedMap<String, Object> httpHeaders,
73+
OutputStream entityStream) throws IOException, WebApplicationException {
74+
75+
final byte[] buffer = new byte[Helper.ONE_MB_IN_BYTES];
76+
final long bufferCount = t.size / Helper.ONE_MB_IN_BYTES;
77+
final int remainder = (int) (t.size % Helper.ONE_MB_IN_BYTES);
78+
79+
for (long b = 0; b < bufferCount; b++) {
80+
entityStream.write(buffer);
81+
}
82+
83+
if (remainder > 0) {
84+
entityStream.write(buffer, 0, remainder);
85+
}
86+
}
87+
}
88+
89+
90+
}

0 commit comments

Comments
 (0)