This is an automated email from the ASF dual-hosted git repository.
jin pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/incubator-hugegraph-commons.git
The following commit(s) were added to refs/heads/master by this push:
new 5ad55fb feat(common): replace jersey dependencies with OkHttp
(Breaking Change) (#133)
5ad55fb is described below
commit 5ad55fb82f1e8720778aa1a337f5cd2d0535c8b6
Author: 小宇 <[email protected]>
AuthorDate: Wed Nov 22 09:37:02 2023 +0800
feat(common): replace jersey dependencies with OkHttp (Breaking Change)
(#133)
* remove jersey
* Update ci.yml
* refact: replace params okhttp3.Headers to internal RestHeaders
* fix: licence dependency
* fix: test error
* fix: code format issue
* fix: unit test error
---------
Co-authored-by: imbajin <[email protected]>
---
.github/workflows/ci.yml | 1 +
.github/workflows/license-checker.yml | 1 +
hugegraph-common/pom.xml | 107 +--
.../apache/hugegraph/rest/AbstractRestClient.java | 719 ++++++++-------------
.../hugegraph/rest/OkHttpBasicAuthInterceptor.java | 47 ++
.../hugegraph/rest/OkHttpTokenInterceptor.java | 50 ++
.../java/org/apache/hugegraph/rest/RestClient.java | 11 +-
.../apache/hugegraph/rest/RestClientConfig.java | 27 +-
.../org/apache/hugegraph/rest/RestHeaders.java | 90 +++
.../java/org/apache/hugegraph/rest/RestResult.java | 46 +-
.../java/org/apache/hugegraph/util/JsonUtil.java | 62 ++
.../apache/hugegraph/version/CommonVersion.java | 2 +-
.../apache/hugegraph/unit/rest/RestClientTest.java | 507 ++++++---------
.../apache/hugegraph/unit/rest/RestResultTest.java | 61 +-
.../hugegraph/unit/util/ReflectionUtilTest.java | 4 +-
.../org.mockito.plugins.MockMaker | 17 +
.../scripts/dependency/known-dependencies.txt | 29 +-
.../org/apache/hugegraph/version/RpcVersion.java | 2 +-
pom.xml | 2 +-
19 files changed, 866 insertions(+), 919 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0616bf8..cc0cd1a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,6 +1,7 @@
name: "hugegraph-commons ci"
on:
+ workflow_dispatch:
push:
branches:
- master
diff --git a/.github/workflows/license-checker.yml
b/.github/workflows/license-checker.yml
index 9c74ed0..0485bd1 100644
--- a/.github/workflows/license-checker.yml
+++ b/.github/workflows/license-checker.yml
@@ -1,6 +1,7 @@
name: "license checker"
on:
+ workflow_dispatch:
push:
branches:
- master
diff --git a/hugegraph-common/pom.xml b/hugegraph-common/pom.xml
index 01bf29b..2010869 100644
--- a/hugegraph-common/pom.xml
+++ b/hugegraph-common/pom.xml
@@ -15,8 +15,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@@ -32,11 +32,14 @@
<description>
hugegraph-common is a common module for HugeGraph and its peripheral
components.
hugegraph-common encapsulates locks, configurations, events,
iterators, rest and some
- numeric or collection util classes to simplify the development of
HugeGraph and its components.
+ numeric or collection util classes to simplify the development of
HugeGraph and its
+ components.
</description>
<properties>
<!-- Use parent params -->
+ <lombok.version>1.18.8</lombok.version>
+ <okhttp.version>4.10.0</okhttp.version>
</properties>
<dependencies>
@@ -196,76 +199,6 @@
<version>${jackson.version}</version>
</dependency>
- <!-- jersey -->
- <dependency>
- <groupId>org.glassfish.jersey.core</groupId>
- <artifactId>jersey-client</artifactId>
- <version>${jersey.version}</version>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.media</groupId>
- <artifactId>jersey-media-json-jackson</artifactId>
- <version>${jersey.version}</version>
- <exclusions>
- <exclusion>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-base</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.jaxrs</groupId>
- <artifactId>jackson-jaxrs-json-provider</artifactId>
- </exclusion>
- <exclusion>
- <artifactId>jackson-annotations</artifactId>
- <groupId>com.fasterxml.jackson.core</groupId>
- </exclusion>
- <exclusion>
- <artifactId>jackson-databind</artifactId>
- <groupId>com.fasterxml.jackson.core</groupId>
- </exclusion>
- <exclusion>
- <artifactId>jackson-module-jaxb-annotations</artifactId>
- <groupId>com.fasterxml.jackson.module</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.connectors</groupId>
- <artifactId>jersey-apache-connector</artifactId>
- <version>${jersey.version}</version>
- <exclusions>
- <exclusion>
- <artifactId>commons-codec</artifactId>
- <groupId>commons-codec</groupId>
- </exclusion>
- <exclusion>
- <artifactId>commons-logging</artifactId>
- <groupId>commons-logging</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.inject</groupId>
- <artifactId>jersey-hk2</artifactId>
- <version>${jersey.hk2.version}</version>
- <exclusions>
- <exclusion>
- <artifactId>javassist</artifactId>
- <groupId>org.javassist</groupId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>jakarta.xml.bind</groupId>
- <artifactId>jakarta.xml.bind-api</artifactId>
- <version>${jakarta.xml.version}</version>
- <exclusions>
- <exclusion>
- <artifactId>jakarta.activation-api</artifactId>
- <groupId>jakarta.activation</groupId>
- </exclusion>
- </exclusions>
- </dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
@@ -278,8 +211,36 @@
</exclusion>
</exclusions>
</dependency>
+
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>okhttp</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>logging-interceptor</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <version>${lombok.version}</version>
+ <optional>true</optional>
+ </dependency>
</dependencies>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.squareup.okhttp3</groupId>
+ <artifactId>okhttp-bom</artifactId>
+ <version>${okhttp.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
<build>
<plugins>
<plugin>
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java
index ea7e923..27d9add 100644
---
a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java
+++
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java
@@ -17,188 +17,219 @@
package org.apache.hugegraph.rest;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.net.URI;
-import java.security.KeyManagementException;
-import java.security.SecureRandom;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
+import java.security.KeyStore;
+import java.util.Arrays;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ScheduledExecutorService;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
-import org.apache.commons.collections.MapUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.http.HttpHeaders;
-import org.apache.http.config.Registry;
-import org.apache.http.config.RegistryBuilder;
-import org.apache.http.conn.socket.ConnectionSocketFactory;
-import org.apache.http.conn.socket.PlainConnectionSocketFactory;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
-import org.apache.http.pool.PoolStats;
-import org.apache.hugegraph.util.E;
-import org.apache.hugegraph.util.ExecutorUtil;
-import org.glassfish.jersey.SslConfigurator;
-import org.glassfish.jersey.apache.connector.ApacheClientProperties;
-import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
-import org.glassfish.jersey.client.ClientConfig;
-import org.glassfish.jersey.client.ClientProperties;
-import org.glassfish.jersey.client.JerseyClientBuilder;
-import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
-import org.glassfish.jersey.internal.util.collection.Ref;
-import org.glassfish.jersey.internal.util.collection.Refs;
-import org.glassfish.jersey.message.GZipEncoder;
-import org.glassfish.jersey.uri.UriComponent;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hugegraph.util.JsonUtil;
+import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableMap;
-import jakarta.ws.rs.client.Client;
-import jakarta.ws.rs.client.ClientRequestContext;
-import jakarta.ws.rs.client.ClientRequestFilter;
-import jakarta.ws.rs.client.Entity;
-import jakarta.ws.rs.client.Invocation.Builder;
-import jakarta.ws.rs.client.WebTarget;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.Response;
-import jakarta.ws.rs.core.Variant;
-
+import lombok.SneakyThrows;
+import okhttp3.ConnectionPool;
+import okhttp3.HttpUrl;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okio.BufferedSink;
+import okio.GzipSink;
+import okio.Okio;
+
+/**
+ * This class provides an abstract implementation of the RestClient interface.
+ * It provides methods for making HTTP requests (GET, POST, PUT, DELETE) to a
REST API.
+ * Note: It uses the OkHttp library to make these requests for now.
+ */
public abstract class AbstractRestClient implements RestClient {
- // Time unit: hours
- private static final long TTL = 24L;
- // Time unit: ms
- private static final long IDLE_TIME = 40L * 1000L;
-
- private static final String TOKEN_KEY = "tokenKey";
+ private final ThreadLocal<String> authContext;
- private final Client client;
- private final WebTarget target;
+ private final OkHttpClient client;
- private PoolingHttpClientConnectionManager pool;
- private ScheduledExecutorService cleanExecutor;
+ private final String baseUrl;
public AbstractRestClient(String url, int timeout) {
- this(url, new ConfigBuilder().configTimeout(timeout).build());
- }
-
- public AbstractRestClient(String url, String user, String password,
- int timeout) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configUser(user, password)
- .build());
+ this(url, RestClientConfig.builder().timeout(timeout).build());
}
- public AbstractRestClient(String url, int timeout,
- int maxTotal, int maxPerRoute) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configPool(maxTotal, maxPerRoute)
- .build());
+ public AbstractRestClient(String url, String user, String password, int
timeout) {
+ this(url, RestClientConfig.builder()
+ .user(user)
+ .password(password)
+ .timeout(timeout)
+ .build());
}
public AbstractRestClient(String url, int timeout, int idleTime,
- int maxTotal, int maxPerRoute) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configIdleTime(idleTime)
- .configPool(maxTotal, maxPerRoute)
- .build());
+ int maxConns, int maxConnsPerRoute) {
+ this(url, RestClientConfig.builder()
+ .idleTime(idleTime)
+ .timeout(timeout)
+ .maxConns(maxConns)
+ .maxConnsPerRoute(maxConnsPerRoute)
+ .build());
+ }
+
+ public AbstractRestClient(String url, String user, String password, int
timeout,
+ int maxConns, int maxConnsPerRoute,
+ String trustStoreFile, String
trustStorePassword) {
+ this(url, RestClientConfig.builder()
+ .user(user).password(password)
+ .timeout(timeout)
+ .maxConns(maxConns)
+ .maxConnsPerRoute(maxConnsPerRoute)
+ .trustStoreFile(trustStoreFile)
+ .trustStorePassword(trustStorePassword)
+ .build());
}
- public AbstractRestClient(String url, String user, String password,
- int timeout, int maxTotal, int maxPerRoute) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configUser(user, password)
- .configPool(maxTotal, maxPerRoute)
- .build());
- }
+ public AbstractRestClient(String url, String token, int timeout,
+ int maxConns, int maxConnsPerRoute,
+ String trustStoreFile, String
trustStorePassword) {
+ this(url, RestClientConfig.builder()
+ .token(token)
+ .timeout(timeout)
+ .maxConns(maxConns)
+ .maxConnsPerRoute(maxConnsPerRoute)
+ .trustStoreFile(trustStoreFile)
+ .trustStorePassword(trustStorePassword)
+ .build());
+ }
+
+ public AbstractRestClient(String url, RestClientConfig config) {
+ this.baseUrl = url;
+ this.client = buildOkHttpClient(config);
+ this.authContext = new InheritableThreadLocal<>();
+ }
+
+ private static RequestBody buildRequestBody(Object body, RestHeaders
headers) {
+ String contentType = parseContentType(headers);
+ String bodyContent;
+ if (RestHeaders.APPLICATION_JSON.equals(contentType)) {
+ if (body == null) {
+ bodyContent = "{}";
+ } else {
+ bodyContent = JsonUtil.toJson(body);
+ }
+ } else {
+ bodyContent = String.valueOf(body);
+ }
+ RequestBody requestBody = RequestBody.create(bodyContent.getBytes(),
+
MediaType.parse(contentType));
- public AbstractRestClient(String url, String user, String password,
- int timeout, int maxTotal, int maxPerRoute,
- String trustStoreFile,
- String trustStorePassword) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configUser(user, password)
- .configPool(maxTotal, maxPerRoute)
- .configSSL(trustStoreFile,
- trustStorePassword)
- .build());
+ if (headers != null &&
+ "gzip".equals(headers.get(RestHeaders.CONTENT_ENCODING))) {
+ requestBody = gzipBody(requestBody);
+ }
+ return requestBody;
}
- public AbstractRestClient(String url, String token, int timeout) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configToken(token)
- .build());
- }
+ private static RequestBody gzipBody(final RequestBody body) {
+ return new RequestBody() {
+ @Override
+ public MediaType contentType() {
+ return body.contentType();
+ }
- public AbstractRestClient(String url, String token, int timeout,
- int maxTotal, int maxPerRoute) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configToken(token)
- .configPool(maxTotal, maxPerRoute)
- .build());
+ @Override
+ public long contentLength() {
+ return -1; // We don't know the compressed length in advance!
+ }
+
+ @Override
+ public void writeTo(@NotNull BufferedSink sink) throws IOException
{
+ BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
+ body.writeTo(gzipSink);
+ gzipSink.close();
+ }
+ };
}
- public AbstractRestClient(String url, String token, int timeout,
- int maxTotal, int maxPerRoute,
- String trustStoreFile,
- String trustStorePassword) {
- this(url, new ConfigBuilder().configTimeout(timeout)
- .configToken(token)
- .configPool(maxTotal, maxPerRoute)
- .configSSL(trustStoreFile,
- trustStorePassword)
- .build());
- }
-
- public AbstractRestClient(String url, ClientConfig config) {
- configConnectionManager(url, config);
-
- this.client = JerseyClientBuilder.newClient(config);
- this.client.register(GZipEncoder.class);
- this.target = this.client.target(url);
- this.pool = (PoolingHttpClientConnectionManager) config.getProperty(
- ApacheClientProperties.CONNECTION_MANAGER);
- if (this.pool != null) {
- this.cleanExecutor = ExecutorUtil.newScheduledThreadPool(
- "conn-clean-worker-%d");
- Number idleTimeProp = (Number) config.getProperty("idleTime");
- final long idleTime = idleTimeProp == null ?
- IDLE_TIME : idleTimeProp.longValue();
- final long checkPeriod = idleTime / 2L;
- this.cleanExecutor.scheduleWithFixedDelay(() -> {
- PoolStats stats = this.pool.getTotalStats();
- int using = stats.getLeased() + stats.getPending();
- if (using > 0) {
- // Do clean only when all connections are idle
- return;
- }
- // Release connections when all clients are inactive
- this.pool.closeIdleConnections(idleTime,
TimeUnit.MILLISECONDS);
- this.pool.closeExpiredConnections();
- }, checkPeriod, checkPeriod, TimeUnit.MILLISECONDS);
+ private static String parseContentType(RestHeaders headers) {
+ if (headers != null) {
+ String contentType = headers.get(RestHeaders.CONTENT_TYPE);
+ if (contentType != null) {
+ return contentType;
+ }
}
+ return RestHeaders.APPLICATION_JSON;
}
- protected abstract void checkStatus(Response response,
- Response.Status... statuses);
+ private OkHttpClient buildOkHttpClient(RestClientConfig config) {
+ OkHttpClient.Builder builder = new OkHttpClient.Builder();
+
+ if (config.getTimeout() != null) {
+ builder.connectTimeout(config.getTimeout(), TimeUnit.MILLISECONDS)
+ .readTimeout(config.getTimeout(), TimeUnit.MILLISECONDS);
+ }
+
+ if (config.getMaxIdleConns() != null || config.getIdleTime() != null) {
+ ConnectionPool connectionPool = new
ConnectionPool(config.getMaxIdleConns(),
+
config.getIdleTime(),
+
TimeUnit.SECONDS);
+ builder.connectionPool(connectionPool);
+ }
+
+ // auth header interceptor
+ if (StringUtils.isNotBlank(config.getUser()) &&
+ StringUtils.isNotBlank(config.getPassword())) {
+ builder.addInterceptor(new
OkHttpBasicAuthInterceptor(config.getUser(),
+
config.getPassword()));
+ }
+ if (StringUtils.isNotBlank(config.getToken())) {
+ builder.addInterceptor(new
OkHttpTokenInterceptor(config.getToken()));
+ }
+
+ // ssl
+ configSsl(builder, this.baseUrl, config.getTrustStoreFile(),
+ config.getTrustStorePassword());
+
+ OkHttpClient okHttpClient = builder.build();
+
+ if (config.getMaxConns() != null) {
+ okHttpClient.dispatcher().setMaxRequests(config.getMaxConns());
+ }
+
+ if (config.getMaxConnsPerRoute() != null) {
+
okHttpClient.dispatcher().setMaxRequestsPerHost(config.getMaxConnsPerRoute());
+ }
+
+ return okHttpClient;
+ }
- protected Response request(Callable<Response> method) {
- try {
- return method.call();
- } catch (Exception e) {
- throw new ClientException("Failed to do request", e);
+ @SneakyThrows
+ private void configSsl(OkHttpClient.Builder builder, String url, String
trustStoreFile,
+ String trustStorePass) {
+ if (StringUtils.isBlank(trustStoreFile) ||
StringUtils.isBlank(trustStorePass)) {
+ return;
}
+
+ X509TrustManager trustManager =
trustManagerForCertificates(trustStoreFile, trustStorePass);
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, new TrustManager[]{trustManager}, null);
+ SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
+
+ builder.sslSocketFactory(sslSocketFactory, trustManager)
+ .hostnameVerifier(new HostNameVerifier(url));
}
@Override
@@ -207,31 +238,69 @@ public abstract class AbstractRestClient implements
RestClient {
}
@Override
- public RestResult post(String path, Object object,
- MultivaluedMap<String, Object> headers) {
+ public RestResult post(String path, Object object, RestHeaders headers) {
return this.post(path, object, headers, null);
}
@Override
- public RestResult post(String path, Object object,
- Map<String, Object> params) {
+ public RestResult post(String path, Object object, Map<String, Object>
params) {
return this.post(path, object, null, params);
}
+ private Request.Builder genRequestBuilder(String path, String id,
RestHeaders headers,
+ Map<String, Object> params) {
+ HttpUrl.Builder urlBuilder =
Objects.requireNonNull(HttpUrl.parse(this.baseUrl))
+ .newBuilder()
+ .addPathSegments(path);
+ if (id != null) {
+ urlBuilder.addPathSegment(id);
+ }
+
+ if (params != null) {
+ params.forEach((name, value) -> {
+ if (value == null) {
+ return;
+ }
+
+ if (value instanceof Collection) {
+ for (Object i : (Collection<?>) value) {
+ urlBuilder.addQueryParameter(name, String.valueOf(i));
+ }
+ } else {
+ urlBuilder.addQueryParameter(name, String.valueOf(value));
+ }
+ });
+ }
+
+ Request.Builder builder = newRequestBuilder().url(urlBuilder.build());
+
+ if (headers != null) {
+ builder.headers(headers.toOkHttpHeader());
+ }
+
+ this.attachAuthToRequest(builder);
+
+ return builder;
+ }
+
+ /**
+ * In order to provide subclasses with overloading opportunities
+ */
+ protected Request.Builder newRequestBuilder() {
+ return new Request.Builder();
+ }
+
+ @SneakyThrows
@Override
- public RestResult post(String path, Object object,
- MultivaluedMap<String, Object> headers,
+ public RestResult post(String path, Object object, RestHeaders headers,
Map<String, Object> params) {
- Pair<Builder, Entity<?>> pair = this.buildRequest(path, null, object,
- headers, params);
- Response response = this.request(() -> {
- // pair.getLeft() is builder, pair.getRight() is entity (http body)
- return pair.getLeft().post(pair.getRight());
- });
- // If check status failed, throw client exception.
- checkStatus(response, Response.Status.CREATED,
- Response.Status.OK, Response.Status.ACCEPTED);
- return new RestResult(response);
+ Request.Builder requestBuilder = genRequestBuilder(path, null,
headers, params);
+ requestBuilder.post(buildRequestBody(object, headers));
+
+ try (Response response = request(requestBuilder)) {
+ checkStatus(response, 200, 201, 202);
+ return new RestResult(response);
+ }
}
@Override
@@ -240,30 +309,27 @@ public abstract class AbstractRestClient implements
RestClient {
}
@Override
- public RestResult put(String path, String id, Object object,
- MultivaluedMap<String, Object> headers) {
+ public RestResult put(String path, String id, Object object, RestHeaders
headers) {
return this.put(path, id, object, headers, null);
}
@Override
- public RestResult put(String path, String id, Object object,
- Map<String, Object> params) {
+ public RestResult put(String path, String id, Object object, Map<String,
Object> params) {
return this.put(path, id, object, null, params);
}
+ @SneakyThrows
@Override
public RestResult put(String path, String id, Object object,
- MultivaluedMap<String, Object> headers,
+ RestHeaders headers,
Map<String, Object> params) {
- Pair<Builder, Entity<?>> pair = this.buildRequest(path, id, object,
- headers, params);
- Response response = this.request(() -> {
- // pair.getLeft() is builder, pair.getRight() is entity (http body)
- return pair.getLeft().put(pair.getRight());
- });
- // If check status failed, throw client exception.
- checkStatus(response, Response.Status.OK, Response.Status.ACCEPTED);
- return new RestResult(response);
+ Request.Builder requestBuilder = genRequestBuilder(path, id, headers,
params);
+ requestBuilder.put(buildRequestBody(object, headers));
+
+ try (Response response = request(requestBuilder)) {
+ checkStatus(response, 200, 202);
+ return new RestResult(response);
+ }
}
@Override
@@ -281,29 +347,14 @@ public abstract class AbstractRestClient implements
RestClient {
return this.get(path, id, ImmutableMap.of());
}
+ @SneakyThrows
private RestResult get(String path, String id, Map<String, Object> params)
{
- Ref<WebTarget> target = Refs.of(this.target);
- for (String key : params.keySet()) {
- Object value = params.get(key);
- if (value instanceof Collection) {
- for (Object i : (Collection<?>) value) {
- target.set(target.get().queryParam(key, i));
- }
- } else {
- target.set(target.get().queryParam(key, value));
- }
- }
+ Request.Builder requestBuilder = genRequestBuilder(path, id, null,
params);
- Response response = this.request(() -> {
- WebTarget webTarget = target.get();
- Builder builder = id == null ? webTarget.path(path).request() :
- webTarget.path(path).path(encode(id)).request();
- this.attachAuthToRequest(builder);
- return builder.get();
- });
-
- checkStatus(response, Response.Status.OK);
- return new RestResult(response);
+ try (Response response = request(requestBuilder)) {
+ checkStatus(response, 200);
+ return new RestResult(response);
+ }
}
@Override
@@ -316,40 +367,35 @@ public abstract class AbstractRestClient implements
RestClient {
return this.delete(path, id, ImmutableMap.of());
}
+ @SneakyThrows
private RestResult delete(String path, String id,
Map<String, Object> params) {
- Ref<WebTarget> target = Refs.of(this.target);
- for (String key : params.keySet()) {
- target.set(target.get().queryParam(key, params.get(key)));
+ Request.Builder requestBuilder = genRequestBuilder(path, id, null,
params);
+ requestBuilder.delete();
+
+ try (Response response = request(requestBuilder)) {
+ checkStatus(response, 204, 202);
+ return new RestResult(response);
}
+ }
- Response response = this.request(() -> {
- WebTarget webTarget = target.get();
- Builder builder = id == null ? webTarget.path(path).request() :
- webTarget.path(path).path(encode(id)).request();
- this.attachAuthToRequest(builder);
- return builder.delete();
- });
+ protected abstract void checkStatus(Response response, int... statuses);
- checkStatus(response, Response.Status.NO_CONTENT,
- Response.Status.ACCEPTED);
- return new RestResult(response);
+ @SneakyThrows
+ protected Response request(Request.Builder requestBuilder) {
+ return this.client.newCall(requestBuilder.build()).execute();
}
+ @SneakyThrows
@Override
public void close() {
- if (this.pool != null) {
- this.pool.close();
- this.cleanExecutor.shutdownNow();
+ if (this.client != null) {
+ this.client.dispatcher().executorService().shutdown();
+ this.client.connectionPool().evictAll();
+ if (this.client.cache() != null) {
+ this.client.cache().close();
+ }
}
- this.client.close();
- }
-
- private final ThreadLocal<String> authContext =
- new InheritableThreadLocal<>();
-
- public void setAuthContext(String auth) {
- this.authContext.set(auth);
}
public void resetAuthContext() {
@@ -360,132 +406,39 @@ public abstract class AbstractRestClient implements
RestClient {
return this.authContext.get();
}
- private void attachAuthToRequest(Builder builder) {
+ public void setAuthContext(String auth) {
+ this.authContext.set(auth);
+ }
+
+ private void attachAuthToRequest(Request.Builder builder) {
// Add auth header
String auth = this.getAuthContext();
if (StringUtils.isNotEmpty(auth)) {
- builder.header(HttpHeaders.AUTHORIZATION, auth);
+ builder.addHeader(RestHeaders.AUTHORIZATION, auth);
}
}
- private Pair<Builder, Entity<?>> buildRequest(
- String path, String id, Object object,
- MultivaluedMap<String, Object> headers,
- Map<String, Object> params) {
- WebTarget target = this.target;
- if (params != null && !params.isEmpty()) {
- for (Map.Entry<String, Object> param : params.entrySet()) {
- target = target.queryParam(param.getKey(), param.getValue());
- }
- }
-
- Builder builder = id == null ? target.path(path).request() :
- target.path(path).path(encode(id)).request();
-
- String encoding = null;
- if (headers != null && !headers.isEmpty()) {
- // Add headers
- builder = builder.headers(headers);
- encoding = (String) headers.getFirst("Content-Encoding");
- }
- // Add auth header
- this.attachAuthToRequest(builder);
-
- /*
- * We should specify the encoding of the entity object manually,
- * because Entity.json() method will reset "content encoding =
- * null" that has been set up by headers before.
- */
- MediaType customContentType = parseCustomContentType(headers);
- Entity<?> entity;
- if (encoding == null) {
- entity = Entity.entity(object, customContentType);
- } else {
- Variant variant = new Variant(customContentType,
- (String) null, encoding);
- entity = Entity.entity(object, variant);
- }
- return Pair.of(builder, entity);
- }
+ @SneakyThrows
+ private X509TrustManager trustManagerForCertificates(String trustStoreFile,
+ String
trustStorePass) {
+ char[] password = trustStorePass.toCharArray();
- /**
- * parse user custom content-type, returns MediaType.APPLICATION_JSON_TYPE
default.
- * @param headers custom http header
- */
- private static MediaType parseCustomContentType(MultivaluedMap<String,
Object> headers) {
- String customContentType = null;
- if (MapUtils.isNotEmpty(headers) && headers.get("Content-Type") !=
null) {
- List<?> contentTypeObj = headers.get("Content-Type");
- if (contentTypeObj != null && !contentTypeObj.isEmpty()) {
- customContentType = contentTypeObj.get(0).toString();
- }
- return MediaType.valueOf(customContentType);
- }
- return MediaType.APPLICATION_JSON_TYPE;
- }
-
- private static void configConnectionManager(String url, ClientConfig conf)
{
- /*
- * Using httpclient with connection pooling, and configuring the
- * jersey connector. 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 pool = connectionManager(url, conf);
- Object maxTotal = conf.getProperty("maxTotal");
- Object maxPerRoute = conf.getProperty("maxPerRoute");
- if (maxTotal != null) {
- pool.setMaxTotal((int) maxTotal);
+ // load keyStore
+ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ try (FileInputStream in = new FileInputStream(trustStoreFile)) {
+ keyStore.load(in, password);
}
- if (maxPerRoute != null) {
- pool.setDefaultMaxPerRoute((int) maxPerRoute);
- }
- conf.property(ApacheClientProperties.CONNECTION_MANAGER, pool);
- conf.connectorProvider(new ApacheConnectorProvider());
- }
- private static PoolingHttpClientConnectionManager connectionManager(
- String url,
- ClientConfig conf) {
- String protocol = (String) conf.getProperty("protocol");
- if (protocol == null || "http".equals(protocol)) {
- return new PoolingHttpClientConnectionManager(TTL, TimeUnit.HOURS);
- }
+ TrustManagerFactory trustManagerFactory =
+
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(keyStore);
- assert "https".equals(protocol);
- String trustStoreFile = (String) conf.getProperty("trustStoreFile");
- E.checkArgument(trustStoreFile != null && !trustStoreFile.isEmpty(),
- "The trust store file must be set when use https");
- String trustStorePass = (String)
conf.getProperty("trustStorePassword");
- E.checkArgument(trustStorePass != null,
- "The trust store password must be set when use https");
- SSLContext context = SslConfigurator.newInstance()
- .trustStoreFile(trustStoreFile)
- .trustStorePassword(trustStorePass)
- .securityProtocol("SSL")
- .createSSLContext();
- TrustManager[] trustAllManager = NoCheckTrustManager.create();
- try {
- context.init(null, trustAllManager, new SecureRandom());
- } catch (KeyManagementException e) {
- throw new ClientException("Failed to init security management", e);
+ TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ if (trustManagers.length != 1 || !(trustManagers[0] instanceof
X509TrustManager)) {
+ throw new IllegalStateException("Unexpected default trust
managers:" +
+ Arrays.toString(trustManagers));
}
-
- HostnameVerifier verifier = new HostNameVerifier(url);
- ConnectionSocketFactory httpSocketFactory, httpsSocketFactory;
- httpSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
- httpsSocketFactory = new SSLConnectionSocketFactory(context, verifier);
- Registry<ConnectionSocketFactory> registry =
- RegistryBuilder.<ConnectionSocketFactory>create()
- .register("http", httpSocketFactory)
- .register("https", httpsSocketFactory)
- .build();
- return new PoolingHttpClientConnectionManager(registry, null,
- null, null, TTL,
- TimeUnit.HOURS);
- }
-
- public static String encode(String raw) {
- return UriComponent.encode(raw, UriComponent.Type.PATH_SEGMENT);
+ return (X509TrustManager) trustManagers[0];
}
public static class HostNameVerifier implements HostnameVerifier {
@@ -505,107 +458,9 @@ public abstract class AbstractRestClient implements
RestClient {
if (!this.url.isEmpty() && this.url.endsWith(hostname)) {
return true;
} else {
- HostnameVerifier verifier = HttpsURLConnection
- .getDefaultHostnameVerifier();
+ HostnameVerifier verifier =
HttpsURLConnection.getDefaultHostnameVerifier();
return verifier.verify(hostname, session);
}
}
}
-
- private static class NoCheckTrustManager implements X509TrustManager {
-
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String
authType)
- throws CertificateException {
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String
authType)
- throws CertificateException {
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
-
- public static TrustManager[] create() {
- return new TrustManager[]{new NoCheckTrustManager()};
- }
- }
-
- private static class ConfigBuilder {
-
- private final ClientConfig config;
-
- ConfigBuilder() {
- this.config = new ClientConfig();
- }
-
- public ConfigBuilder configTimeout(int timeout) {
- this.config.property(ClientProperties.CONNECT_TIMEOUT, timeout);
- this.config.property(ClientProperties.READ_TIMEOUT, timeout);
- return this;
- }
-
- public ConfigBuilder configUser(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 configToken(String token) {
- this.config.property(TOKEN_KEY, token);
- this.config.register(BearerRequestFilter.class);
- return this;
- }
-
- public ConfigBuilder configPool(int maxTotal, int maxPerRoute) {
- this.config.property("maxTotal", maxTotal);
- this.config.property("maxPerRoute", maxPerRoute);
- return this;
- }
-
- public ConfigBuilder configIdleTime(int idleTime) {
- this.config.property("idleTime", idleTime);
- return this;
- }
-
- public ConfigBuilder configSSL(String trustStoreFile,
- String trustStorePassword) {
- if (trustStoreFile == null || trustStoreFile.isEmpty() ||
- trustStorePassword == null) {
- this.config.property("protocol", "http");
- } else {
- this.config.property("protocol", "https");
- }
- this.config.property("trustStoreFile", trustStoreFile);
- this.config.property("trustStorePassword", trustStorePassword);
- return this;
- }
-
- public ClientConfig build() {
- return this.config;
- }
- }
-
- public static class BearerRequestFilter implements ClientRequestFilter {
-
- @Override
- public void filter(ClientRequestContext context) {
- String token = context.getClient().getConfiguration()
- .getProperty(TOKEN_KEY).toString();
- context.getHeaders().add(HttpHeaders.AUTHORIZATION,
- "Bearer " + token);
- }
- }
}
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpBasicAuthInterceptor.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpBasicAuthInterceptor.java
new file mode 100644
index 0000000..f7b1509
--- /dev/null
+++
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpBasicAuthInterceptor.java
@@ -0,0 +1,47 @@
+/*
+ * 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 org.apache.hugegraph.rest;
+
+import java.io.IOException;
+
+import okhttp3.Credentials;
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+
+public class OkHttpBasicAuthInterceptor implements Interceptor {
+
+ private final String credentials;
+
+ public OkHttpBasicAuthInterceptor(String user, String password) {
+ this.credentials = Credentials.basic(user, password);
+ }
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request request = chain.request();
+ if (request.header(RestHeaders.AUTHORIZATION) == null) {
+ Request authenticatedRequest = request.newBuilder()
+
.header(RestHeaders.AUTHORIZATION,
+ this.credentials)
+ .build();
+ return chain.proceed(authenticatedRequest);
+ }
+ return chain.proceed(request);
+ }
+}
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpTokenInterceptor.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpTokenInterceptor.java
new file mode 100644
index 0000000..f564033
--- /dev/null
+++
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpTokenInterceptor.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.apache.hugegraph.rest;
+
+import static org.apache.hugegraph.rest.RestHeaders.AUTHORIZATION;
+import static org.apache.hugegraph.rest.RestHeaders.BEARER_PREFIX;
+
+import java.io.IOException;
+
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+
+
+public class OkHttpTokenInterceptor implements Interceptor {
+
+ private final String token;
+
+ public OkHttpTokenInterceptor(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request request = chain.request();
+ if (request.header(AUTHORIZATION) == null) {
+ Request authenticatedRequest = request.newBuilder()
+ .header(AUTHORIZATION,
+ BEARER_PREFIX +
this.token)
+ .build();
+ return chain.proceed(authenticatedRequest);
+ }
+ return chain.proceed(request);
+ }
+}
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestClient.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestClient.java
index dc3b7b7..d5b58d9 100644
--- a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestClient.java
+++ b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestClient.java
@@ -19,31 +19,28 @@ package org.apache.hugegraph.rest;
import java.util.Map;
-import jakarta.ws.rs.core.MultivaluedMap;
-
public interface RestClient {
/**
* Post method
*/
RestResult post(String path, Object object);
- RestResult post(String path, Object object, MultivaluedMap<String, Object>
headers);
+ RestResult post(String path, Object object, RestHeaders headers);
RestResult post(String path, Object object, Map<String, Object> params);
- RestResult post(String path, Object object, MultivaluedMap<String, Object>
headers,
- Map<String, Object> params);
+ RestResult post(String path, Object object, RestHeaders headers,
Map<String, Object> params);
/**
* Put method
*/
RestResult put(String path, String id, Object object);
- RestResult put(String path, String id, Object object,
MultivaluedMap<String, Object> headers);
+ RestResult put(String path, String id, Object object, RestHeaders headers);
RestResult put(String path, String id, Object object, Map<String, Object>
params);
- RestResult put(String path, String id, Object object,
MultivaluedMap<String, Object> headers,
+ RestResult put(String path, String id, Object object, RestHeaders headers,
Map<String, Object> params);
/**
diff --git
a/hugegraph-rpc/src/main/java/org/apache/hugegraph/version/RpcVersion.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestClientConfig.java
similarity index 60%
copy from
hugegraph-rpc/src/main/java/org/apache/hugegraph/version/RpcVersion.java
copy to
hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestClientConfig.java
index f3cf926..ef3e9b0 100644
--- a/hugegraph-rpc/src/main/java/org/apache/hugegraph/version/RpcVersion.java
+++
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestClientConfig.java
@@ -15,14 +15,27 @@
* under the License.
*/
-package org.apache.hugegraph.version;
+package org.apache.hugegraph.rest;
-import org.apache.hugegraph.util.VersionUtil.Version;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
-public class RpcVersion {
+@Builder
+@Getter
+@Setter
+public class RestClientConfig {
- public static final String NAME = "hugegraph-rpc";
-
- // The second parameter of Version.of() is for all-in-one JAR
- public static final Version VERSION = Version.of(RpcVersion.class,
"1.0.1");
+ private String user;
+ private String password;
+ private String token;
+ // unit in milliseconds
+ private Integer timeout;
+ private Integer maxConns;
+ private Integer maxConnsPerRoute;
+ // unit in seconds
+ private Integer idleTime = 30;
+ private Integer maxIdleConns = 5;
+ private String trustStoreFile;
+ private String trustStorePassword;
}
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestHeaders.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestHeaders.java
new file mode 100644
index 0000000..03c082e
--- /dev/null
+++ b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestHeaders.java
@@ -0,0 +1,90 @@
+/*
+ * 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 org.apache.hugegraph.rest;
+
+import java.util.Date;
+import java.util.Iterator;
+
+import kotlin.Pair;
+
+public class RestHeaders {
+
+ public static final String CONTENT_TYPE = "Content-Type";
+
+ public static final String CONTENT_ENCODING = "Content-Encoding";
+
+ public static final String AUTHORIZATION = "Authorization";
+
+ public static final String APPLICATION_JSON = "application/json";
+
+ public static final String BEARER_PREFIX = "Bearer ";
+
+ private final okhttp3.Headers.Builder headersBuilder;
+
+ public RestHeaders() {
+ this.headersBuilder = new okhttp3.Headers.Builder();
+ }
+
+ public static RestHeaders convertToRestHeaders(okhttp3.Headers headers) {
+ RestHeaders restHeaders = new RestHeaders();
+
+ if (headers != null) {
+ Iterator<Pair<String, String>> iter = headers.iterator();
+ while (iter.hasNext()) {
+ Pair<String, String> pair = iter.next();
+ restHeaders.add(pair.getFirst(), pair.getSecond());
+ }
+ }
+ return restHeaders;
+ }
+
+ public String get(String key) {
+ return this.headersBuilder.get(key);
+ }
+
+ public Date getDate(String key) {
+ return this.headersBuilder.build().getDate(key);
+ }
+
+ public RestHeaders add(String key, String value) {
+ this.headersBuilder.add(key, value);
+ return this;
+ }
+
+ public RestHeaders add(String key, Date value) {
+ this.headersBuilder.add(key, value);
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.toOkHttpHeader().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof RestHeaders) {
+ return this.toOkHttpHeader().equals(((RestHeaders)
obj).toOkHttpHeader());
+ }
+ return false;
+ }
+
+ public okhttp3.Headers toOkHttpHeader() {
+ return this.headersBuilder.build();
+ }
+}
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestResult.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestResult.java
index 82b7f39..0aa482b 100644
--- a/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestResult.java
+++ b/hugegraph-common/src/main/java/org/apache/hugegraph/rest/RestResult.java
@@ -21,39 +21,47 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.Response;
-
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.SneakyThrows;
+import okhttp3.Response;
+
public class RestResult {
private static final ObjectMapper MAPPER = new ObjectMapper();
private final int status;
- private final MultivaluedMap<String, Object> headers;
+ private final RestHeaders headers;
private final String content;
public RestResult(Response response) {
- this(response.getStatus(), response.readEntity(String.class),
- response.getHeaders());
+ this(response.code(), getResponseContent(response),
+ RestHeaders.convertToRestHeaders(response.headers()));
}
- public RestResult(int status, String content,
- MultivaluedMap<String, Object> headers) {
+ public RestResult(int status, String content, RestHeaders headers) {
this.status = status;
this.headers = headers;
this.content = content;
}
+ @SneakyThrows
+ private static String getResponseContent(Response response) {
+ return response.body().string();
+ }
+
+ public static void registerModule(Module module) {
+ MAPPER.registerModule(module);
+ }
+
public int status() {
return this.status;
}
- public MultivaluedMap<String, Object> headers() {
+ public RestHeaders headers() {
return this.headers;
}
@@ -65,8 +73,7 @@ public class RestResult {
try {
return MAPPER.readValue(this.content, clazz);
} catch (Exception e) {
- throw new SerializeException(
- "Failed to deserialize: %s", e, this.content);
+ throw new SerializeException("Failed to deserialize: %s", e,
this.content);
}
}
@@ -76,16 +83,14 @@ public class RestResult {
JsonNode root = MAPPER.readTree(this.content);
JsonNode element = root.get(key);
if (element == null) {
- throw new SerializeException(
- "Can't find value of the key: %s in json.", key);
+ throw new SerializeException("Can't find value of the key: %s
in json.", key);
}
JavaType type = MAPPER.getTypeFactory()
.constructParametrizedType(ArrayList.class,
List.class,
clazz);
return MAPPER.convertValue(element, type);
} catch (IOException e) {
- throw new SerializeException(
- "Failed to deserialize %s", e, this.content);
+ throw new SerializeException("Failed to deserialize %s", e,
this.content);
}
}
@@ -97,18 +102,13 @@ public class RestResult {
try {
return MAPPER.readValue(this.content, type);
} catch (IOException e) {
- throw new SerializeException(
- "Failed to deserialize %s", e, this.content);
+ throw new SerializeException("Failed to deserialize %s", e,
this.content);
}
}
@Override
public String toString() {
- return String.format("{status=%s, headers=%s, content=%s}",
- this.status, this.headers, this.content);
- }
-
- public static void registerModule(Module module) {
- MAPPER.registerModule(module);
+ return String.format("{status=%s, headers=%s, content=%s}",
this.status, this.headers,
+ this.content);
}
}
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/util/JsonUtil.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/util/JsonUtil.java
new file mode 100644
index 0000000..fc9586b
--- /dev/null
+++ b/hugegraph-common/src/main/java/org/apache/hugegraph/util/JsonUtil.java
@@ -0,0 +1,62 @@
+/*
+ * 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 org.apache.hugegraph.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.hugegraph.rest.SerializeException;
+
+import java.io.IOException;
+
+public final class JsonUtil {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ public static void registerModule(Module module) {
+ MAPPER.registerModule(module);
+ }
+
+ public static String toJson(Object object) {
+ try {
+ return MAPPER.writeValueAsString(object);
+ } catch (JsonProcessingException e) {
+ throw new SerializeException("Failed to serialize object '%s'",
+ e, object);
+ }
+ }
+
+ public static <T> T fromJson(String json, Class<T> clazz) {
+ try {
+ return MAPPER.readValue(json, clazz);
+ } catch (IOException e) {
+ throw new SerializeException("Failed to deserialize json '%s'",
+ e, json);
+ }
+ }
+
+ public static <T> T convertValue(JsonNode node, Class<T> clazz) {
+ try {
+ return MAPPER.convertValue(node, clazz);
+ } catch (IllegalArgumentException e) {
+ throw new SerializeException("Failed to deserialize json node
'%s'",
+ e, node);
+ }
+ }
+}
diff --git
a/hugegraph-common/src/main/java/org/apache/hugegraph/version/CommonVersion.java
b/hugegraph-common/src/main/java/org/apache/hugegraph/version/CommonVersion.java
index bcdad92..a049ff4 100644
---
a/hugegraph-common/src/main/java/org/apache/hugegraph/version/CommonVersion.java
+++
b/hugegraph-common/src/main/java/org/apache/hugegraph/version/CommonVersion.java
@@ -24,5 +24,5 @@ public class CommonVersion {
public static final String NAME = "hugegraph-common";
// The second parameter of Version.of() is for all-in-one JAR
- public static final Version VERSION = Version.of(CommonVersion.class,
"1.0.1");
+ public static final Version VERSION = Version.of(CommonVersion.class,
"1.2.0");
}
diff --git
a/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestClientTest.java
b/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestClientTest.java
index eb9c7c2..f7b998d 100644
---
a/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestClientTest.java
+++
b/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestClientTest.java
@@ -21,249 +21,90 @@ import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
-import jakarta.ws.rs.client.Invocation.Builder;
-import jakarta.ws.rs.client.WebTarget;
-import jakarta.ws.rs.core.MultivaluedHashMap;
-import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.Response;
-
-import org.apache.hugegraph.unit.BaseUnitTest;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
-
-import org.apache.http.HttpClientConnection;
-import org.apache.http.HttpHeaders;
-import org.apache.http.HttpHost;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
-import org.apache.http.pool.PoolStats;
-
import org.apache.hugegraph.rest.AbstractRestClient;
import org.apache.hugegraph.rest.ClientException;
import org.apache.hugegraph.rest.RestClient;
+import org.apache.hugegraph.rest.RestClientConfig;
+import org.apache.hugegraph.rest.RestHeaders;
import org.apache.hugegraph.rest.RestResult;
import org.apache.hugegraph.testutil.Assert;
import org.apache.hugegraph.testutil.Whitebox;
+import org.apache.hugegraph.unit.BaseUnitTest;
+import org.junit.Test;
+import org.mockito.Mockito;
-public class RestClientTest {
-
- private static class RestClientImpl extends AbstractRestClient {
-
- private final int status;
- private final MultivaluedMap<String, Object> headers;
- private final String content;
-
- public RestClientImpl(String url, int timeout, int idleTime,
- int maxTotal, int maxPerRoute, int status) {
- super(url, timeout, idleTime, maxTotal, maxPerRoute);
- this.status = status;
- this.headers = ImmutableMultivaluedMap.empty();
- this.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, String user, String password,
- int timeout, int maxTotal, int maxPerRoute,
- String trustStoreFile, String trustStorePassword,
- int status) {
- super(url, user, password, timeout, maxTotal, maxPerRoute,
- trustStoreFile, trustStorePassword);
- this.status = status;
- this.headers = ImmutableMultivaluedMap.empty();
- this.content = "";
- }
-
- public RestClientImpl(String url, String token,
- int timeout, int status) {
- super(url, token, timeout);
- this.status = status;
- this.headers = ImmutableMultivaluedMap.empty();
- this.content = "";
- }
-
- public RestClientImpl(String url, String token, int timeout,
- int maxTotal, int maxPerRoute, int status) {
- super(url, token, timeout, maxTotal, maxPerRoute);
- this.status = status;
- this.headers = ImmutableMultivaluedMap.empty();
- this.content = "";
- }
-
- public RestClientImpl(String url, String token, int timeout,
- int maxTotal, int maxPerRoute,
- String trustStoreFile,
- String trustStorePassword, int status) {
- super(url, token, timeout, maxTotal, maxPerRoute,
- trustStoreFile, trustStorePassword);
- this.status = status;
- this.headers = ImmutableMultivaluedMap.empty();
- this.content = "";
- }
-
- public RestClientImpl(String url, int timeout, int status) {
- this(url, timeout, status, ImmutableMultivaluedMap.empty(), "");
- }
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.net.HttpHeaders;
- 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;
- }
+import lombok.SneakyThrows;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
- @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;
- }
+public class RestClientTest {
- @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);
- }
- }
- }
+ private static final String TEST_URL = "http://localhost:8080";
@Test
public void testPost() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
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);
+ public void testPostWithMaxConnsAndPerRoute() {
+ RestClientConfig restClientConfig =
+
RestClientConfig.builder().timeout(1000).maxConns(10).maxConnsPerRoute(5).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.post("path", "body");
Assert.assertEquals(200, restResult.status());
}
- @Test
- public void testCleanExecutor() throws Exception {
- // Modify IDLE_TIME 100ms to speed test
- int newIdleTime = 100;
- int newCheckPeriod = newIdleTime + 20;
-
- RestClient client = new RestClientImpl("/test", 1000, newIdleTime,
- 10, 5, 200);
-
- PoolingHttpClientConnectionManager pool;
- pool = Whitebox.getInternalState(client, "pool");
- pool = Mockito.spy(pool);
- Whitebox.setInternalState(client, "pool", pool);
- HttpRoute route = new HttpRoute(HttpHost.create(
- "http://127.0.0.1:8080"));
- // Create a connection manually, it will be put into leased list
- HttpClientConnection conn = pool.requestConnection(route, null)
- .get(1L, TimeUnit.SECONDS);
- PoolStats stats = pool.getTotalStats();
- int usingConns = stats.getLeased() + stats.getPending();
- Assert.assertGte(1, usingConns);
-
- // Sleep more than two check periods for busy connection
- Thread.sleep(newCheckPeriod);
- Mockito.verify(pool, Mockito.never()).closeExpiredConnections();
- stats = pool.getTotalStats();
- usingConns = stats.getLeased() + stats.getPending();
- Assert.assertGte(1, usingConns);
-
- // The connection will be put into available list
- pool.releaseConnection(conn, null, 0, TimeUnit.SECONDS);
-
- stats = pool.getTotalStats();
- usingConns = stats.getLeased() + stats.getPending();
- Assert.assertEquals(0, usingConns);
- /*
- * Sleep more than two check periods for free connection,
- * ensure connection has been closed
- */
- Thread.sleep(newCheckPeriod);
- Mockito.verify(pool, Mockito.atLeastOnce())
- .closeExpiredConnections();
- Mockito.verify(pool, Mockito.atLeastOnce())
- .closeIdleConnections(newIdleTime, TimeUnit.MILLISECONDS);
- }
-
@Test
public void testPostWithUserAndPassword() {
- RestClient client = new RestClientImpl("/test", "user", "", 1000, 200);
+ RestClientConfig restClientConfig =
+
RestClientConfig.builder().user("user").password("").timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.post("path", "body");
Assert.assertEquals(200, restResult.status());
}
@Test
public void testPostWithToken() {
- RestClient client = new RestClientImpl("/test", "token", 1000, 200);
+ RestClientConfig restClientConfig =
+
RestClientConfig.builder().token("token").timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
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);
+ RestClientConfig restClientConfig =
+
RestClientConfig.builder().user("user").password("").timeout(1000).maxConns(10)
+ .maxConnsPerRoute(5).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.post("path", "body");
Assert.assertEquals(200, restResult.status());
}
@Test
public void testPostWithTokenAndAllParams() {
- RestClient client = new RestClientImpl("/test", "token", 1000,
- 10, 5, 200);
+ RestClientConfig restClientConfig =
+
RestClientConfig.builder().token("token").timeout(1000).maxConns(10)
+ .maxConnsPerRoute(5).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.post("path", "body");
Assert.assertEquals(200, restResult.status());
}
@@ -276,9 +117,11 @@ public class RestClientTest {
BaseUnitTest.downloadFileByUrl(url, trustStoreFile);
String trustStorePassword = "changeit";
- RestClient client = new RestClientImpl("/test", "user", "", 1000,
- 10, 5, trustStoreFile,
- trustStorePassword, 200);
+ RestClientConfig restClientConfig =
+
RestClientConfig.builder().user("user").password("").timeout(1000).maxConns(10)
+
.maxConnsPerRoute(5).trustStoreFile(trustStoreFile)
+
.trustStorePassword(trustStorePassword).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.post("path", "body");
Assert.assertEquals(200, restResult.status());
}
@@ -291,9 +134,11 @@ public class RestClientTest {
BaseUnitTest.downloadFileByUrl(url, trustStoreFile);
String trustStorePassword = "changeit";
- RestClient client = new RestClientImpl("/test", "token", 1000,
- 10, 5, trustStoreFile,
- trustStorePassword, 200);
+ RestClientConfig restClientConfig =
+
RestClientConfig.builder().token("token").timeout(1000).maxConns(10)
+
.maxConnsPerRoute(5).trustStoreFile(trustStoreFile)
+
.trustStorePassword(trustStorePassword).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.post("path", "body");
Assert.assertEquals(200, restResult.status());
}
@@ -327,13 +172,12 @@ public class RestClientTest {
@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");
+ RestHeaders headers = new RestHeaders().add("key1", "value1-1")
+ .add("key1", "value1-2")
+ .add("Content-Encoding",
"gzip");
String content = "{\"names\": [\"marko\", \"josh\", \"lop\"]}";
- RestClient client = new RestClientImpl("/test", 1000, 200,
- headers, content);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200, headers, content);
RestResult restResult = client.post("path", "body");
Assert.assertEquals(200, restResult.status());
Assert.assertEquals(headers, restResult.headers());
@@ -344,7 +188,8 @@ public class RestClientTest {
@Test
public void testPostWithException() {
- RestClient client = new RestClientImpl("/test", 1000, 400);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
400);
Assert.assertThrows(ClientException.class, () -> {
client.post("path", "body");
});
@@ -352,8 +197,10 @@ public class RestClientTest {
@Test
public void testPostWithParams() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
- MultivaluedMap<String, Object> headers =
ImmutableMultivaluedMap.empty();
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
+ RestHeaders headers = new RestHeaders();
+
Map<String, Object> params = ImmutableMap.of("param1", "value1");
RestResult restResult = client.post("path", "body", headers,
params);
@@ -362,25 +209,27 @@ public class RestClientTest {
@Test
public void testPut() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.put("path", "id1", "body");
Assert.assertEquals(200, restResult.status());
}
@Test
public void testPutWithHeaders() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
- MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
- headers.add("key1", "value1-1");
- headers.add("key1", "value1-2");
- headers.add("Content-Encoding", "gzip");
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
+ RestHeaders headers = new RestHeaders().add("key1", "value1-1")
+ .add("key2", "value1-2")
+ .add("Content-Encoding",
"gzip");
RestResult restResult = client.put("path", "id1", "body", headers);
Assert.assertEquals(200, restResult.status());
}
@Test
public void testPutWithParams() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
Map<String, Object> params = ImmutableMap.of("param1", "value1");
RestResult restResult = client.put("path", "id1", "body", params);
Assert.assertEquals(200, restResult.status());
@@ -388,7 +237,8 @@ public class RestClientTest {
@Test
public void testPutWithException() {
- RestClient client = new RestClientImpl("/test", 1000, 400);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
400);
Assert.assertThrows(ClientException.class, () -> {
client.put("path", "id1", "body");
});
@@ -396,21 +246,24 @@ public class RestClientTest {
@Test
public void testGet() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.get("path");
Assert.assertEquals(200, restResult.status());
}
@Test
public void testGetWithId() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
RestResult restResult = client.get("path", "id1");
Assert.assertEquals(200, restResult.status());
}
@Test
public void testGetWithParams() {
- RestClient client = new RestClientImpl("/test", 1000, 200);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
200);
Map<String, Object> params = new HashMap<>();
params.put("key1", ImmutableList.of("value1-1", "value1-2"));
params.put("key2", "value2");
@@ -420,7 +273,8 @@ public class RestClientTest {
@Test
public void testGetWithException() {
- RestClient client = new RestClientImpl("/test", 1000, 400);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
400);
Assert.assertThrows(ClientException.class, () -> {
client.get("path", "id1");
});
@@ -428,14 +282,16 @@ public class RestClientTest {
@Test
public void testDeleteWithId() {
- RestClient client = new RestClientImpl("/test", 1000, 204);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
204);
RestResult restResult = client.delete("path", "id1");
Assert.assertEquals(204, restResult.status());
}
@Test
public void testDeleteWithParams() {
- RestClient client = new RestClientImpl("/test", 1000, 204);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
204);
Map<String, Object> params = ImmutableMap.of("param1", "value1");
RestResult restResult = client.delete("path", params);
Assert.assertEquals(204, restResult.status());
@@ -443,38 +299,17 @@ public class RestClientTest {
@Test
public void testDeleteWithException() {
- RestClient client = new RestClientImpl("/test", 1000, 400);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClient client = new RestClientImpl(TEST_URL, restClientConfig,
400);
Assert.assertThrows(ClientException.class, () -> {
client.delete("path", "id1");
});
}
- @Test
- public void testClose() {
- RestClient client = new RestClientImpl("/test", 1000, 10, 5, 200);
- RestResult restResult = client.post("path", "body");
- Assert.assertEquals(200, restResult.status());
-
- client.close();
- Assert.assertThrows(IllegalStateException.class, () -> {
- client.post("path", "body");
- });
-
- PoolingHttpClientConnectionManager pool;
- pool = Whitebox.getInternalState(client, "pool");
- Assert.assertNotNull(pool);
- AtomicBoolean isShutDown = Whitebox.getInternalState(pool,
"isShutDown");
- Assert.assertTrue(isShutDown.get());
-
- ScheduledExecutorService cleanExecutor;
- cleanExecutor = Whitebox.getInternalState(client, "cleanExecutor");
- Assert.assertNotNull(cleanExecutor);
- Assert.assertTrue(cleanExecutor.isShutdown());
- }
-
@Test
public void testAuthContext() {
- RestClientImpl client = new RestClientImpl("/test", 1000, 10, 5, 200);
+ RestClientConfig restClientConfig =
RestClientConfig.builder().timeout(1000).build();
+ RestClientImpl client = new RestClientImpl(TEST_URL, restClientConfig,
200);
Assert.assertNull(client.getAuthContext());
String token = UUID.randomUUID().toString();
@@ -485,48 +320,33 @@ public class RestClientTest {
Assert.assertNull(client.getAuthContext());
}
- private static class MockRestClientImpl extends AbstractRestClient {
-
- public MockRestClientImpl(String url, int timeout) {
- super(url, timeout);
- }
-
- @Override
- protected void checkStatus(Response response,
- Response.Status... statuses) {
- // pass
- }
- }
-
+ @SneakyThrows
@Test
public void testRequest() {
- MockRestClientImpl client = new MockRestClientImpl("test", 1000);
-
- WebTarget target = Mockito.mock(WebTarget.class);
- Builder builder = Mockito.mock(Builder.class);
-
- Mockito.when(target.path("test")).thenReturn(target);
- Mockito.when(target.path("test")
- .path(AbstractRestClient.encode("id")))
- .thenReturn(target);
- Mockito.when(target.path("test").request()).thenReturn(builder);
- Mockito.when(target.path("test")
- .path(AbstractRestClient.encode("id"))
- .request())
- .thenReturn(builder);
-
- Response response = Mockito.mock(Response.class);
- Mockito.when(response.getStatus()).thenReturn(200);
- Mockito.when(response.getHeaders())
- .thenReturn(new MultivaluedHashMap<>());
- Mockito.when(response.readEntity(String.class)).thenReturn("content");
-
- Mockito.when(builder.delete()).thenReturn(response);
- Mockito.when(builder.get()).thenReturn(response);
- Mockito.when(builder.put(Mockito.any())).thenReturn(response);
- Mockito.when(builder.post(Mockito.any())).thenReturn(response);
-
- Whitebox.setInternalState(client, "target", target);
+ Response response = Mockito.mock(Response.class,
Mockito.RETURNS_DEEP_STUBS);
+ Mockito.when(response.code()).thenReturn(200);
+ Mockito.when(response.headers())
+ .thenReturn(new RestHeaders().toOkHttpHeader());
+ Mockito.when(response.body().string()).thenReturn("content");
+
+ Request.Builder requestBuilder = Mockito.mock(Request.Builder.class,
+
Mockito.RETURNS_DEEP_STUBS);
+ Mockito.when(requestBuilder.delete()).thenReturn(requestBuilder);
+ Mockito.when(requestBuilder.get()).thenReturn(requestBuilder);
+
Mockito.when(requestBuilder.put(Mockito.any())).thenReturn(requestBuilder);
+
Mockito.when(requestBuilder.post(Mockito.any())).thenReturn(requestBuilder);
+ Mockito.when(requestBuilder.url((HttpUrl)
Mockito.any())).thenReturn(requestBuilder);
+ MockRestClientImpl client = new MockRestClientImpl(TEST_URL, 1000) {
+ @Override
+ protected Request.Builder newRequestBuilder() {
+ return requestBuilder;
+ }
+ };
+
+ OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class,
Mockito.RETURNS_DEEP_STUBS);
+
Mockito.when(okHttpClient.newCall(Mockito.any()).execute()).thenReturn(response);
+
+ Whitebox.setInternalState(client, "client", okHttpClient);
RestResult result;
@@ -534,97 +354,140 @@ public class RestClientTest {
client.setAuthContext("token1");
result = client.delete("test", ImmutableMap.of());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token1");
+ Mockito.verify(requestBuilder).addHeader(RestHeaders.AUTHORIZATION,
"token1");
+
client.resetAuthContext();
client.setAuthContext("token2");
result = client.delete("test", "id");
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token2");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token2");
client.resetAuthContext();
// Test get
client.setAuthContext("token3");
result = client.get("test");
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token3");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token3");
client.resetAuthContext();
client.setAuthContext("token4");
result = client.get("test", ImmutableMap.of());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token4");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token4");
client.resetAuthContext();
client.setAuthContext("token5");
result = client.get("test", "id");
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token5");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token5");
client.resetAuthContext();
// Test put
client.setAuthContext("token6");
- result = client.post("test", new Object());
+ result = client.post("test", null);
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token6");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token6");
client.resetAuthContext();
client.setAuthContext("token7");
- result = client.post("test", new Object(), new MultivaluedHashMap<>());
+ result = client.post("test", null, new RestHeaders());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token7");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token7");
client.resetAuthContext();
client.setAuthContext("token8");
- result = client.post("test", new Object(), ImmutableMap.of());
+ result = client.post("test", null, ImmutableMap.of());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token8");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token8");
client.resetAuthContext();
client.setAuthContext("token9");
- result = client.post("test", new Object(), new MultivaluedHashMap<>(),
+ result = client.post("test", null, new RestHeaders(),
ImmutableMap.of());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token9");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token9");
client.resetAuthContext();
// Test post
client.setAuthContext("token10");
- result = client.post("test", new Object());
+ result = client.post("test", null);
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token10");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token10");
client.resetAuthContext();
client.setAuthContext("token11");
- result = client.post("test", new Object(), new MultivaluedHashMap<>());
+ result = client.post("test", null, new RestHeaders());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token11");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token11");
client.resetAuthContext();
client.setAuthContext("token12");
- result = client.post("test", new Object(), ImmutableMap.of());
+ result = client.post("test", null, ImmutableMap.of());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token12");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token12");
client.resetAuthContext();
client.setAuthContext("token13");
- result = client.post("test", new Object(), new MultivaluedHashMap<>(),
+ result = client.post("test", null, new RestHeaders(),
ImmutableMap.of());
Assert.assertEquals(200, result.status());
- Mockito.verify(builder).header(HttpHeaders.AUTHORIZATION,
- "token13");
+ Mockito.verify(requestBuilder).addHeader(HttpHeaders.AUTHORIZATION,
"token13");
client.resetAuthContext();
}
+
+ private static class RestClientImpl extends AbstractRestClient {
+
+ private final int status;
+ private final RestHeaders headers;
+ private final String content;
+
+ public RestClientImpl(String url, RestClientConfig config, int status)
{
+ this(url, config, status, new RestHeaders(), "");
+ }
+
+ public RestClientImpl(String url, RestClientConfig config, int status,
RestHeaders headers,
+ String content) {
+ super(url, config);
+ this.status = status;
+ this.headers = headers;
+ this.content = content;
+ }
+
+ @SneakyThrows
+ @Override
+ protected Response request(Request.Builder requestBuilder) {
+ Response response = Mockito.mock(Response.class,
Mockito.RETURNS_DEEP_STUBS);
+ Mockito.when(response.code()).thenReturn(this.status);
+
Mockito.when(response.headers()).thenReturn(this.headers.toOkHttpHeader());
+ Mockito.when(response.body().string()).thenReturn(this.content);
+ return response;
+ }
+
+ @Override
+ protected void checkStatus(Response response, int... statuses) {
+ boolean match = false;
+ for (int status : statuses) {
+ if (status == response.code()) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ throw new ClientException("Invalid response '%s'", response);
+ }
+ }
+ }
+
+ private static class MockRestClientImpl extends AbstractRestClient {
+
+ public MockRestClientImpl(String url, int timeout) {
+ super(url, timeout);
+ }
+
+ @Override
+ protected void checkStatus(Response response, int... statuses) {
+ // pass
+ }
+ }
}
diff --git
a/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestResultTest.java
b/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestResultTest.java
index 2ae9a4e..06eb03a 100644
---
a/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestResultTest.java
+++
b/hugegraph-common/src/test/java/org/apache/hugegraph/unit/rest/RestResultTest.java
@@ -19,22 +19,44 @@ package org.apache.hugegraph.unit.rest;
import java.util.Map;
-import jakarta.ws.rs.core.MultivaluedHashMap;
-import jakarta.ws.rs.core.MultivaluedMap;
-import jakarta.ws.rs.core.Response;
-import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
+import org.apache.hugegraph.rest.RestHeaders;
+import org.apache.hugegraph.rest.RestResult;
+import org.apache.hugegraph.rest.SerializeException;
+import org.apache.hugegraph.testutil.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import org.apache.hugegraph.rest.RestResult;
-import org.apache.hugegraph.rest.SerializeException;
-import org.apache.hugegraph.testutil.Assert;
+import lombok.SneakyThrows;
+import okhttp3.Response;
public class RestResultTest {
+ private static RestResult newRestResult(int status) {
+ return newRestResult(status, "", new RestHeaders());
+ }
+
+ private static RestResult newRestResult(int status, String content) {
+ return newRestResult(status, content, new RestHeaders());
+ }
+
+ private static RestResult newRestResult(int status, RestHeaders headers) {
+ return newRestResult(status, "", headers);
+ }
+
+ @SneakyThrows
+ private static RestResult newRestResult(int status, String content,
+ RestHeaders headers) {
+ Response response = Mockito.mock(Response.class,
Mockito.RETURNS_DEEP_STUBS);
+ Mockito.when(response.code()).thenReturn(status);
+ Mockito.when(response.headers()).thenReturn(headers.toOkHttpHeader());
+ Mockito.when(response.body().string())
+ .thenReturn(content);
+ return new RestResult(response);
+ }
+
@Test
public void testStatus() {
RestResult result = newRestResult(200);
@@ -43,7 +65,7 @@ public class RestResultTest {
@Test
public void testHeaders() {
- MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
+ RestHeaders headers = new RestHeaders();
headers.add("key1", "value1-1");
headers.add("key1", "value1-2");
headers.add("key2", "value2");
@@ -116,27 +138,4 @@ public class RestResultTest {
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);
- }
}
diff --git
a/hugegraph-common/src/test/java/org/apache/hugegraph/unit/util/ReflectionUtilTest.java
b/hugegraph-common/src/test/java/org/apache/hugegraph/unit/util/ReflectionUtilTest.java
index 510d145..c83b2ad 100644
---
a/hugegraph-common/src/test/java/org/apache/hugegraph/unit/util/ReflectionUtilTest.java
+++
b/hugegraph-common/src/test/java/org/apache/hugegraph/unit/util/ReflectionUtilTest.java
@@ -94,7 +94,7 @@ public class ReflectionUtilTest extends BaseUnitTest {
@SuppressWarnings("unchecked")
List<ClassInfo> classes = IteratorUtils.toList(ReflectionUtil.classes(
"org.apache.hugegraph.util"));
- Assert.assertEquals(17, classes.size());
+ Assert.assertEquals(18, classes.size());
classes.sort(Comparator.comparing(ClassInfo::getName));
Assert.assertEquals("org.apache.hugegraph.util.Bytes",
classes.get(0).getName());
@@ -103,7 +103,7 @@ public class ReflectionUtilTest extends BaseUnitTest {
Assert.assertEquals("org.apache.hugegraph.util.CollectionUtil",
classes.get(2).getName());
Assert.assertEquals("org.apache.hugegraph.util.VersionUtil",
- classes.get(16).getName());
+ classes.get(17).getName());
}
@Test
diff --git
a/hugegraph-common/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
b/hugegraph-common/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..a76010b
--- /dev/null
+++
b/hugegraph-common/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+mock-maker-inline
diff --git a/hugegraph-dist/scripts/dependency/known-dependencies.txt
b/hugegraph-dist/scripts/dependency/known-dependencies.txt
index c6de269..53b4567 100644
--- a/hugegraph-dist/scripts/dependency/known-dependencies.txt
+++ b/hugegraph-dist/scripts/dependency/known-dependencies.txt
@@ -1,6 +1,6 @@
animal-sniffer-annotations-1.18.jar
annotations-4.1.1.4.jar
-aopalliance-repackaged-3.0.1.jar
+annotations-13.0.jar
bolt-1.6.2.jar
checker-qual-3.5.0.jar
commons-beanutils-1.9.4.jar
@@ -27,11 +27,6 @@ gson-2.8.6.jar
guava-30.0-jre.jar
hamcrest-core-1.3.jar
hessian-3.3.7.jar
-hk2-api-3.0.1.jar
-hk2-locator-3.0.1.jar
-hk2-utils-3.0.1.jar
-httpclient-4.5.13.jar
-httpcore-4.4.13.jar
j2objc-annotations-1.3.jar
jackson-annotations-2.14.0-rc1.jar
jackson-core-2.14.0-rc1.jar
@@ -42,22 +37,10 @@ jackson-jaxrs-json-provider-2.14.0-rc1.jar
jackson-module-jaxb-annotations-2.14.0-rc1.jar
jakarta.activation-2.0.1.jar
jakarta.activation-api-1.2.2.jar
-jakarta.annotation-api-2.0.0.jar
-jakarta.inject-api-2.0.0.jar
-jakarta.ws.rs-api-3.0.0.jar
-jakarta.xml.bind-api-4.0.0-RC2.jar
javassist-3.28.0-GA.jar
-javax.activation-api-1.2.0.jar
javax.json-1.0.jar
-jaxb-api-2.3.1.jar
jaxb-core-3.0.2.jar
jaxb-impl-3.0.2.jar
-jersey-apache-connector-3.0.3.jar
-jersey-client-3.0.3.jar
-jersey-common-3.0.3.jar
-jersey-entity-filtering-3.0.3.jar
-jersey-hk2-3.0.3.jar
-jersey-media-json-jackson-3.0.3.jar
joda-time-2.10.8.jar
jsr305-3.0.1.jar
junit-4.13.1.jar
@@ -71,7 +54,6 @@ opentracing-api-0.22.0.jar
opentracing-mock-0.22.0.jar
opentracing-noop-0.22.0.jar
opentracing-util-0.22.0.jar
-osgi-resource-locator-1.0.3.jar
perfmark-api-0.19.0.jar
proto-google-common-protos-1.17.0.jar
protobuf-java-3.11.0.jar
@@ -84,3 +66,12 @@ swagger-core-1.5.18.jar
swagger-models-1.5.18.jar
tracer-core-3.0.8.jar
validation-api-1.1.0.Final.jar
+kotlin-stdlib-1.6.20.jar
+kotlin-stdlib-common-1.5.31.jar
+kotlin-stdlib-jdk7-1.6.10.jar
+kotlin-stdlib-jdk8-1.6.10.jar
+logging-interceptor-4.10.0.jar
+lombok-1.18.8.jar
+okhttp-4.10.0.jar
+okio-jvm-3.0.0.jar
+
diff --git
a/hugegraph-rpc/src/main/java/org/apache/hugegraph/version/RpcVersion.java
b/hugegraph-rpc/src/main/java/org/apache/hugegraph/version/RpcVersion.java
index f3cf926..c7ab405 100644
--- a/hugegraph-rpc/src/main/java/org/apache/hugegraph/version/RpcVersion.java
+++ b/hugegraph-rpc/src/main/java/org/apache/hugegraph/version/RpcVersion.java
@@ -24,5 +24,5 @@ public class RpcVersion {
public static final String NAME = "hugegraph-rpc";
// The second parameter of Version.of() is for all-in-one JAR
- public static final Version VERSION = Version.of(RpcVersion.class,
"1.0.1");
+ public static final Version VERSION = Version.of(RpcVersion.class,
"1.2.0");
}
diff --git a/pom.xml b/pom.xml
index 8f2e39b..2b8a486 100644
--- a/pom.xml
+++ b/pom.xml
@@ -90,7 +90,7 @@
<properties>
<!-- Note: We need also update the version in CommonVersion.java &
RpcVersion.java now -->
- <revision>1.0.1</revision>
+ <revision>1.2.0</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<top.level.dir>${project.basedir}/..</top.level.dir>
<compiler.source>1.8</compiler.source>