Let RestClient can reuse connections
Implement #14
Change-Id: I49c0675566145e233e40f25fe0c30c9614bf2c14
diff --git a/pom.xml b/pom.xml
index 079239d..6ead7db 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
<groupId>com.baidu.hugegraph</groupId>
<artifactId>hugegraph-common</artifactId>
- <version>1.5.8</version>
+ <version>1.5.9</version>
<name>hugegraph-common</name>
<url>https://github.com/hugegraph/hugegraph-common</url>
@@ -66,9 +66,11 @@
<javax.json.version>1.0</javax.json.version>
<jsr305.version>3.0.1</jsr305.version>
<javassist.version>3.21.0-GA</javassist.version>
- <jersey.version>2.25.1</jersey.version>
+ <!--TODO: Update to 2.29 when released-->
+ <jersey.version>2.22</jersey.version>
<jersey.hk2.version>2.27</jersey.hk2.version>
<junit.version>4.12</junit.version>
+ <mockito.version>2.25.1</mockito.version>
</properties>
<dependencies>
@@ -78,6 +80,12 @@
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
<!-- Logging -->
<dependency>
@@ -158,6 +166,11 @@
<version>${jersey.version}</version>
</dependency>
<dependency>
+ <groupId>org.glassfish.jersey.connectors</groupId>
+ <artifactId>jersey-apache-connector</artifactId>
+ <version>${jersey.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.hk2.version}</version>
@@ -199,7 +212,7 @@
<manifestEntries>
<!-- Must be on one line, otherwise the automatic
upgrade script cannot replace the version number -->
- <Implementation-Version>1.5.8.0</Implementation-Version>
+ <Implementation-Version>1.5.9.0</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
diff --git a/src/main/java/com/baidu/hugegraph/rest/RestClient.java b/src/main/java/com/baidu/hugegraph/rest/RestClient.java
index cce2d14..4a519b0 100644
--- a/src/main/java/com/baidu/hugegraph/rest/RestClient.java
+++ b/src/main/java/com/baidu/hugegraph/rest/RestClient.java
@@ -19,6 +19,8 @@
package com.baidu.hugegraph.rest;
+import static org.glassfish.jersey.apache.connector.ApacheClientProperties.CONNECTION_MANAGER;
+
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -33,6 +35,9 @@
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Variant;
+import org.apache.http.config.SocketConfig;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
@@ -45,24 +50,42 @@
public abstract class RestClient {
- private Client client;
- private WebTarget target;
+ private final ClientConfig config;
+ private final Client client;
+ private final WebTarget target;
public RestClient(String url, int timeout) {
- this(url, buildConfig(timeout));
+ this(url, new ConfigBuilder().config(timeout).build());
+ }
+
+ public RestClient(String url, int timeout, int maxTotal, int maxPerRoute) {
+ this(url, new ConfigBuilder().config(timeout)
+ .config(timeout, maxTotal, maxPerRoute)
+ .build());
}
public RestClient(String url, String user, String password, int timeout) {
- this(url, buildConfigWithBasicAuth(user, password, timeout));
+ this(url, new ConfigBuilder().config(timeout)
+ .config(user, password)
+ .build());
}
+ public RestClient(String url, String user, String password, int timeout,
+ int maxTotal, int maxPerRoute) {
+ this(url, new ConfigBuilder().config(timeout)
+ .config(user, password)
+ .config(timeout, maxTotal, maxPerRoute)
+ .build());
+ }
+
public RestClient(String url, ClientConfig config) {
- this.client = ClientBuilder.newClient(config);
+ this.config = config;
+ this.client = ClientBuilder.newClient(this.config);
this.client.register(GZipEncoder.class);
this.target = this.client.target(url);
}
- private Response request(Callable<Response> method) {
+ protected Response request(Callable<Response> method) {
try {
return method.call();
} catch (Exception e) {
@@ -208,30 +231,64 @@
return UriComponent.encode(raw, UriComponent.Type.PATH_SEGMENT);
}
- private static ClientConfig buildConfigWithBasicAuth(String username,
- String password,
- int timeout) {
- ClientConfig config = buildConfig(timeout);
- /*
- * NOTE: don't use non-preemptive mode
- * In non-preemptive mode the authentication information is added
- * only when server refuses the request with 401 status code and
- * then the request is repeated.
- * Non-preemptive has negative impact on the performance. The advantage
- * is that it does not send credentials when they are not needed.
- * https://jersey.github.io/documentation/latest/client.html#d0e5461
- */
- config.register(HttpAuthenticationFeature.basic(username, password));
- return config;
- }
-
- private static ClientConfig buildConfig(int timeout) {
- ClientConfig config = new ClientConfig();
- config.property(ClientProperties.CONNECT_TIMEOUT, timeout);
- config.property(ClientProperties.READ_TIMEOUT, timeout);
- return config;
- }
-
protected abstract void checkStatus(Response response,
Response.Status... statuses);
+
+ private static class ConfigBuilder {
+
+ private final ClientConfig config;
+
+ public ConfigBuilder() {
+ this.config = new ClientConfig();
+ }
+
+ public ConfigBuilder config(int timeout) {
+ this.config.property(ClientProperties.CONNECT_TIMEOUT, timeout);
+ this.config.property(ClientProperties.READ_TIMEOUT, timeout);
+ return this;
+ }
+
+ public ConfigBuilder config(String username, String password) {
+ /*
+ * NOTE: don't use non-preemptive mode
+ * In non-preemptive mode the authentication information is added
+ * only when server refuses the request with 401 status code and
+ * then the request is repeated.
+ * Non-preemptive has negative impact on the performance. The
+ * advantage is it doesn't send credentials when they are not needed
+ * https://jersey.github.io/documentation/latest/client.html#d0e5461
+ */
+ this.config.register(HttpAuthenticationFeature.basic(username,
+ password));
+ return this;
+ }
+
+ public ConfigBuilder config(int timeout, int maxTotal,
+ int maxPerRoute) {
+ /*
+ * Using httpclient with connection pooling, and configuring the
+ * jersey connector, reference:
+ * http://www.theotherian.com/2013/08/jersey-client-2.0-httpclient-timeouts-max-connections.html
+ * https://stackoverflow.com/questions/43228051/memory-issue-with-jax-rs-using-jersey/46175943#46175943
+ *
+ * But the jersey that has been released in the maven central
+ * repository seems to have a bug.
+ * https://github.com/jersey/jersey/pull/3752
+ */
+ PoolingHttpClientConnectionManager pcm =
+ new PoolingHttpClientConnectionManager();
+ pcm.setDefaultSocketConfig(SocketConfig.custom()
+ .setSoTimeout(timeout)
+ .build());
+ pcm.setMaxTotal(maxTotal);
+ pcm.setDefaultMaxPerRoute(maxPerRoute);
+ this.config.property(CONNECTION_MANAGER, pcm);
+ this.config.connectorProvider(new ApacheConnectorProvider());
+ return this;
+ }
+
+ public ClientConfig build() {
+ return this.config;
+ }
+ }
}
diff --git a/src/main/java/com/baidu/hugegraph/rest/RestResult.java b/src/main/java/com/baidu/hugegraph/rest/RestResult.java
index 3be29b8..39abf6c 100644
--- a/src/main/java/com/baidu/hugegraph/rest/RestResult.java
+++ b/src/main/java/com/baidu/hugegraph/rest/RestResult.java
@@ -34,9 +34,9 @@
private static final ObjectMapper mapper = new ObjectMapper();
- private int status;
- private MultivaluedMap<String, Object> headers;
- private String content;
+ private final int status;
+ private final MultivaluedMap<String, Object> headers;
+ private final String content;
public RestResult(Response response) {
this.status = response.getStatus();
diff --git a/src/main/java/com/baidu/hugegraph/util/VersionUtil.java b/src/main/java/com/baidu/hugegraph/util/VersionUtil.java
index 6ac64f2..92bef3d 100644
--- a/src/main/java/com/baidu/hugegraph/util/VersionUtil.java
+++ b/src/main/java/com/baidu/hugegraph/util/VersionUtil.java
@@ -44,7 +44,7 @@
/**
* Compare if a version is greater than the other one (inclusive)
* @param version The version to be compared
- * @param begin The lower bound of the range
+ * @param other The lower bound of the range
* @return true if it's greater than the other, otherwise false
*/
public static boolean gte(String version, String other) {
diff --git a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
index b7bafde..a34e52a 100644
--- a/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
+++ b/src/main/java/com/baidu/hugegraph/version/CommonVersion.java
@@ -27,5 +27,5 @@
// The second parameter of Version.of() is for all-in-one JAR
public static final Version VERSION = Version.of(CommonVersion.class,
- "1.5.8");
+ "1.5.9");
}
diff --git a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
index c8f6e4c..6850815 100644
--- a/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
+++ b/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
@@ -30,6 +30,8 @@
import com.baidu.hugegraph.unit.iterator.FlatMapperIteratorTest;
import com.baidu.hugegraph.unit.iterator.MapperIteratorTest;
import com.baidu.hugegraph.unit.perf.PerfUtilTest;
+import com.baidu.hugegraph.unit.rest.RestClientTest;
+import com.baidu.hugegraph.unit.rest.RestResultTest;
import com.baidu.hugegraph.unit.util.BytesTest;
import com.baidu.hugegraph.unit.util.CollectionUtilTest;
import com.baidu.hugegraph.unit.util.HashUtilTest;
@@ -46,6 +48,8 @@
HugeConfigTest.class,
EventHubTest.class,
PerfUtilTest.class,
+ RestClientTest.class,
+ RestResultTest.class,
VersionTest.class,
ExtendableIteratorTest.class,
diff --git a/src/test/java/com/baidu/hugegraph/unit/rest/RestClientTest.java b/src/test/java/com/baidu/hugegraph/unit/rest/RestClientTest.java
new file mode 100644
index 0000000..0b09768
--- /dev/null
+++ b/src/test/java/com/baidu/hugegraph/unit/rest/RestClientTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.baidu.hugegraph.rest.ClientException;
+import com.baidu.hugegraph.rest.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class RestClientTest {
+
+ private static class RestClientImpl extends RestClient {
+
+ private final int status;
+ private final MultivaluedMap<String, Object> headers;
+ private final String content;
+
+ public RestClientImpl(String url, int timeout,
+ int maxTotal, int maxPerRoute, int status) {
+ super(url, timeout, maxTotal, maxPerRoute);
+ this.status = status;
+ this.headers = ImmutableMultivaluedMap.empty();
+ this.content = "";
+ }
+
+ public RestClientImpl(String url, String user, String password,
+ int timeout, int status) {
+ super(url, user, password, timeout);
+ this.status = status;
+ this.headers = ImmutableMultivaluedMap.empty();
+ this.content = "";
+ }
+
+ public RestClientImpl(String url, String user, String password,
+ int timeout, int maxTotal, int maxPerRoute,
+ int status) {
+ super(url, user, password, timeout, maxTotal, maxPerRoute);
+ this.status = status;
+ this.headers = ImmutableMultivaluedMap.empty();
+ this.content = "";
+ }
+
+ public RestClientImpl(String url, int timeout, int status) {
+ this(url, timeout, status, ImmutableMultivaluedMap.empty(), "");
+ }
+
+ public RestClientImpl(String url, int timeout, int status,
+ MultivaluedMap<String, Object> headers) {
+ this(url, timeout, status, headers, "");
+ }
+
+ public RestClientImpl(String url, int timeout, int status,
+ String content) {
+ this(url, timeout, status, ImmutableMultivaluedMap.empty(), content);
+ }
+
+ public RestClientImpl(String url, int timeout, int status,
+ MultivaluedMap<String, Object> headers,
+ String content) {
+ super(url, timeout);
+ this.status = status;
+ this.headers = headers;
+ this.content = content;
+ }
+
+ @Override
+ protected Response request(Callable<Response> method) {
+ Response response = Mockito.mock(Response.class);
+ Mockito.when(response.getStatus()).thenReturn(this.status);
+ Mockito.when(response.getHeaders()).thenReturn(this.headers);
+ Mockito.when(response.readEntity(String.class))
+ .thenReturn(this.content);
+ return response;
+ }
+
+ @Override
+ protected void checkStatus(Response response,
+ Response.Status... statuses) {
+ boolean match = false;
+ for (Response.Status status : statuses) {
+ if (status.getStatusCode() == response.getStatus()) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ throw new ClientException("Invalid response '%s'", response);
+ }
+ }
+ }
+
+ @Test
+ public void testPost() {
+ RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestResult restResult = client.post("path", "body");
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ // TODO: How to verify it?
+ public void testPostWithMaxTotalAndPerRoute() {
+ RestClient client = new RestClientImpl("/test", 1000, 10, 5, 200);
+ RestResult restResult = client.post("path", "body");
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testPostWithUserAndPassword() {
+ RestClient client = new RestClientImpl("/test", "user", "", 1000, 200);
+ RestResult restResult = client.post("path", "body");
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testPostWithAllParams() {
+ RestClient client = new RestClientImpl("/test", "user", "", 1000,
+ 10, 5, 200);
+ RestResult restResult = client.post("path", "body");
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testPostWithHeaderAndContent() {
+ MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.add("key1", "value1-1");
+ headers.add("key1", "value1-2");
+ headers.add("Content-Encoding", "gzip");
+ String content = "{\"names\": [\"marko\", \"josh\", \"lop\"]}";
+ RestClient client = new RestClientImpl("/test", 1000, 200,
+ headers, content);
+ RestResult restResult = client.post("path", "body");
+ Assert.assertEquals(200, restResult.status());
+ Assert.assertEquals(headers, restResult.headers());
+ Assert.assertEquals(content, restResult.content());
+ Assert.assertEquals(ImmutableList.of("marko", "josh", "lop"),
+ restResult.readList("names", String.class));
+ }
+
+ @Test
+ public void testPostWithException() {
+ RestClient client = new RestClientImpl("/test", 1000, 400);
+ Assert.assertThrows(ClientException.class, () -> {
+ client.post("path", "body");
+ });
+ }
+
+ @Test
+ public void testPostWithParams() {
+ RestClient client = new RestClientImpl("/test", 1000, 200);
+ MultivaluedMap<String, Object> headers = ImmutableMultivaluedMap.empty();
+ Map<String, Object> params = ImmutableMap.of("param1", "value1");
+ RestResult restResult = client.post("path", "body", headers,
+ params);
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testPut() {
+ RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestResult restResult = client.put("path", "id1", "body");
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testPutWithParams() {
+ RestClient client = new RestClientImpl("/test", 1000, 200);
+ Map<String, Object> params = ImmutableMap.of("param1", "value1");
+ RestResult restResult = client.put("path", "id1", "body", params);
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testPutWithException() {
+ RestClient client = new RestClientImpl("/test", 1000, 400);
+ Assert.assertThrows(ClientException.class, () -> {
+ client.put("path", "id1", "body");
+ });
+ }
+
+ @Test
+ public void testGet() {
+ RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestResult restResult = client.get("path");
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testGetWithId() {
+ RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestResult restResult = client.get("path", "id1");
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testGetWithParams() {
+ RestClient client = new RestClientImpl("/test", 1000, 200);
+ Map<String, Object> params = new HashMap<>();
+ params.put("key1", ImmutableList.of("value1-1", "value1-2"));
+ params.put("key2", "value2");
+ RestResult restResult = client.get("path", params);
+ Assert.assertEquals(200, restResult.status());
+ }
+
+ @Test
+ public void testGetWithException() {
+ RestClient client = new RestClientImpl("/test", 1000, 400);
+ Assert.assertThrows(ClientException.class, () -> {
+ client.get("path", "id1");
+ });
+ }
+
+ @Test
+ public void testDeleteWithId() {
+ RestClient client = new RestClientImpl("/test", 1000, 204);
+ RestResult restResult = client.delete("path", "id1");
+ Assert.assertEquals(204, restResult.status());
+ }
+
+ @Test
+ public void testDeleteWithParams() {
+ RestClient client = new RestClientImpl("/test", 1000, 204);
+ Map<String, Object> params = ImmutableMap.of("param1", "value1");
+ RestResult restResult = client.delete("path", params);
+ Assert.assertEquals(204, restResult.status());
+ }
+
+ @Test
+ public void testDeleteWithException() {
+ RestClient client = new RestClientImpl("/test", 1000, 400);
+ Assert.assertThrows(ClientException.class, () -> {
+ client.delete("path", "id1");
+ });
+ }
+}
diff --git a/src/test/java/com/baidu/hugegraph/unit/rest/RestResultTest.java b/src/test/java/com/baidu/hugegraph/unit/rest/RestResultTest.java
new file mode 100644
index 0000000..357c4a0
--- /dev/null
+++ b/src/test/java/com/baidu/hugegraph/unit/rest/RestResultTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit.rest;
+
+import java.util.Map;
+
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.rest.SerializeException;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class RestResultTest {
+
+ @Test
+ public void testStatus() {
+ RestResult result = newRestResult(200);
+ Assert.assertEquals(200, result.status());
+ }
+
+ @Test
+ public void testHeaders() {
+ MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.add("key1", "value1-1");
+ headers.add("key1", "value1-2");
+ headers.add("key2", "value2");
+ RestResult result = newRestResult(200, headers);
+ Assert.assertEquals(200, result.status());
+ Assert.assertEquals(headers, result.headers());
+ }
+
+ @Test
+ public void testContent() {
+ String content = "{\"name\": \"marko\"}";
+ RestResult result = newRestResult(200, content);
+ Assert.assertEquals(200, result.status());
+ Assert.assertEquals(content, result.content());
+ Assert.assertEquals(ImmutableMap.of("name", "marko"),
+ result.readObject(Map.class));
+ }
+
+ @Test
+ public void testContentWithException() {
+ String content = "{illegal key: \"marko\"}";
+ RestResult result = newRestResult(200, content);
+ Assert.assertEquals(200, result.status());
+ Assert.assertEquals(content, result.content());
+ Assert.assertThrows(SerializeException.class, () -> {
+ result.readObject(Map.class);
+ });
+ }
+
+ @Test
+ public void testContentList() {
+ String content = "{\"names\": [\"marko\", \"josh\", \"lop\"]}";
+ RestResult result = newRestResult(200, content);
+ Assert.assertEquals(200, result.status());
+ Assert.assertEquals(content, result.content());
+ Assert.assertEquals(ImmutableList.of("marko", "josh", "lop"),
+ result.readList("names", String.class));
+
+ content = "[\"marko\", \"josh\", \"lop\"]";
+ result = newRestResult(200, content);
+ Assert.assertEquals(200, result.status());
+ Assert.assertEquals(content, result.content());
+ Assert.assertEquals(ImmutableList.of("marko", "josh", "lop"),
+ result.readList(String.class));
+ }
+
+ @Test
+ public void testContentListWithException() {
+ String content = "{\"names\": [\"marko\", \"josh\", \"lop\"]}";
+ RestResult result = newRestResult(200, content);
+ Assert.assertEquals(200, result.status());
+ Assert.assertEquals(content, result.content());
+ Assert.assertThrows(SerializeException.class, () -> {
+ result.readList("unexitsed key", String.class);
+ });
+
+ content = "{\"names\": [marko, josh, \"lop\"]}";
+ RestResult result2 = newRestResult(200, content);
+ Assert.assertEquals(200, result2.status());
+ Assert.assertEquals(content, result2.content());
+ Assert.assertThrows(SerializeException.class, () -> {
+ result2.readList("names", String.class);
+ });
+
+ content = "[marko, josh, \"lop\"]";
+ RestResult result3 = newRestResult(200, content);
+ Assert.assertEquals(200, result3.status());
+ Assert.assertEquals(content, result3.content());
+ Assert.assertThrows(SerializeException.class, () -> {
+ result3.readList(String.class);
+ });
+ }
+
+ private static RestResult newRestResult(int status) {
+ return newRestResult(status, "", ImmutableMultivaluedMap.empty());
+ }
+
+ private static RestResult newRestResult(int status, String content) {
+ return newRestResult(status, content, ImmutableMultivaluedMap.empty());
+ }
+
+ private static RestResult newRestResult(int status,
+ MultivaluedMap<String, Object> h) {
+ return newRestResult(status, "", h);
+ }
+
+ private static RestResult newRestResult(int status, String content,
+ MultivaluedMap<String, Object> h) {
+ Response response = Mockito.mock(Response.class);
+ Mockito.when(response.getStatus()).thenReturn(status);
+ Mockito.when(response.getHeaders()).thenReturn(h);
+ Mockito.when(response.readEntity(String.class))
+ .thenReturn(content);
+ return new RestResult(response);
+ }
+}