javeme commented on code in PR #133: URL: https://github.com/apache/incubator-hugegraph-commons/pull/133#discussion_r1390116533
########## 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 { Review Comment: Okhttp => OkHttp ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkhttpTokenInterceptor.java: ########## @@ -0,0 +1,46 @@ +/* + * 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.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +public class OkhttpTokenInterceptor implements Interceptor { Review Comment: Okhttp => OkHttp ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .build()); } public AbstractRestClient(String url, int timeout, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, null, null, timeout, maxTotal, maxPerRoute); } public AbstractRestClient(String url, int timeout, int idleTime, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configIdleTime(idleTime) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, OkHttpConfig.builder() + .idleTime(idleTime) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .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()); + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); } 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()); - } + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute, Review Comment: ditto ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkhttpTokenInterceptor.java: ########## @@ -0,0 +1,46 @@ +/* + * 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.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(HttpHeadersConstant.AUTHORIZATION) == null) { + Request authenticatedRequest = request.newBuilder() + .header(HttpHeadersConstant.AUTHORIZATION, + "Bearer " + this.token) Review Comment: define a const for "Bearer"? ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/HttpHeadersConstant.java: ########## @@ -0,0 +1,26 @@ +/* + * 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; + +public class HttpHeadersConstant { + 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"; + Review Comment: unexpected blank line ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/HttpHeadersConstant.java: ########## @@ -0,0 +1,26 @@ +/* + * 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; + +public class HttpHeadersConstant { Review Comment: prefer to merge this class into `HttpHeaders` class ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .build()); } public AbstractRestClient(String url, int timeout, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, null, null, timeout, maxTotal, maxPerRoute); } public AbstractRestClient(String url, int timeout, int idleTime, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configIdleTime(idleTime) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, OkHttpConfig.builder() + .idleTime(idleTime) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .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()); + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); } 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()); - } + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute, + String trustStoreFile, + String trustStorePassword) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, OkHttpConfig okhttpConfig) { + this.baseUrl = url; + this.client = buildOkHttpClient(okhttpConfig); + this.requestBuilder = new Request.Builder(); + this.authContext = new InheritableThreadLocal<>(); + } + + private static RequestBody buildRequestBody(Object body, RestHeaders headers) { + String contentType = parseContentType(headers); + String bodyContent; + if (HttpHeadersConstant.APPLICATION_JSON.equals(contentType)) { + if (body == null) { + bodyContent = "{}"; + } else { + bodyContent = JsonUtil.toJson(body); + } + } else { + bodyContent = String.valueOf(body); + } + RequestBody requestBody = RequestBody.create(MediaType.parse(contentType), bodyContent); - public AbstractRestClient(String url, String token, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configToken(token) - .build()); + if (headers != null && "gzip".equals(headers.get(HttpHeadersConstant.CONTENT_ENCODING))) { + requestBody = gzip(requestBody); + } + return requestBody; } - public AbstractRestClient(String url, String token, int timeout, - int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configToken(token) - .configPool(maxTotal, maxPerRoute) - .build()); + private static RequestBody gzip(final RequestBody body) { Review Comment: prefer `gzipBody()` ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpConfig.java: ########## @@ -0,0 +1,38 @@ +/* + * 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 lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Getter +@Setter +public class OkHttpConfig { Review Comment: It seems there is no binding relationship with OkHttp, we can use a more generic name like `RestClientConfig` or `HttpConfig` ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/HttpHeadersConstant.java: ########## @@ -0,0 +1,26 @@ +/* + * 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; + +public class HttpHeadersConstant { + public static final String CONTENT_TYPE = "Content-Type"; Review Comment: expect a blank line ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .build()); } public AbstractRestClient(String url, int timeout, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, null, null, timeout, maxTotal, maxPerRoute); } public AbstractRestClient(String url, int timeout, int idleTime, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configIdleTime(idleTime) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, OkHttpConfig.builder() + .idleTime(idleTime) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .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()); + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); } 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()); - } + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute, + String trustStoreFile, + String trustStorePassword) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, OkHttpConfig okhttpConfig) { + this.baseUrl = url; + this.client = buildOkHttpClient(okhttpConfig); + this.requestBuilder = new Request.Builder(); + this.authContext = new InheritableThreadLocal<>(); + } + + private static RequestBody buildRequestBody(Object body, RestHeaders headers) { + String contentType = parseContentType(headers); + String bodyContent; + if (HttpHeadersConstant.APPLICATION_JSON.equals(contentType)) { + if (body == null) { + bodyContent = "{}"; + } else { + bodyContent = JsonUtil.toJson(body); + } + } else { + bodyContent = String.valueOf(body); + } + RequestBody requestBody = RequestBody.create(MediaType.parse(contentType), bodyContent); - public AbstractRestClient(String url, String token, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configToken(token) - .build()); + if (headers != null && "gzip".equals(headers.get(HttpHeadersConstant.CONTENT_ENCODING))) { + requestBody = gzip(requestBody); + } + return requestBody; } - public AbstractRestClient(String url, String token, int timeout, - int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configToken(token) - .configPool(maxTotal, maxPerRoute) - .build()); + private static RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override + public MediaType contentType() { + return body.contentType(); + } + + @Override + public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override + public void writeTo(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(HttpHeadersConstant.CONTENT_TYPE); + if (contentType != null) { + return contentType; + } } + return HttpHeadersConstant.APPLICATION_JSON; } - protected abstract void checkStatus(Response response, - Response.Status... statuses); + private OkHttpClient buildOkHttpClient(OkHttpConfig okHttpConfig) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + + if (okHttpConfig.getTimeout() != null) { + builder.connectTimeout(okHttpConfig.getTimeout(), TimeUnit.MILLISECONDS) + .readTimeout(okHttpConfig.getTimeout(), TimeUnit.MILLISECONDS); + } + + if (okHttpConfig.getIdleTime() != null) { + ConnectionPool connectionPool = + new ConnectionPool(5, okHttpConfig.getIdleTime(), TimeUnit.MILLISECONDS); Review Comment: please note the 5 in `ConnectionPool(5, ...)`, can we pass it from OkHttpConfig? can we also add a test case for it? ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .build()); } public AbstractRestClient(String url, int timeout, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, null, null, timeout, maxTotal, maxPerRoute); } public AbstractRestClient(String url, int timeout, int idleTime, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configIdleTime(idleTime) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, OkHttpConfig.builder() + .idleTime(idleTime) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .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()); + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); } 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()); - } + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout) { Review Comment: also keep `int timeout` style? ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .build()); } public AbstractRestClient(String url, int timeout, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, null, null, timeout, maxTotal, maxPerRoute); } public AbstractRestClient(String url, int timeout, int idleTime, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configIdleTime(idleTime) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, OkHttpConfig.builder() + .idleTime(idleTime) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .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()); + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); } 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()); - } + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute) { Review Comment: ditto ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { Review Comment: also keep int timeout style? and seems one line is ok ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/OkHttpConfig.java: ########## @@ -0,0 +1,38 @@ +/* + * 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 lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Builder +@Getter +@Setter +public class OkHttpConfig { + + private String user; + private String password; + private String token; + private Integer timeout; + private Integer maxTotal; Review Comment: prefer: maxTotal => maxConnsTotal maxPerRoute => maxConnsPerRoute ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .build()); } public AbstractRestClient(String url, int timeout, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, null, null, timeout, maxTotal, maxPerRoute); } public AbstractRestClient(String url, int timeout, int idleTime, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configIdleTime(idleTime) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, OkHttpConfig.builder() + .idleTime(idleTime) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .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()); + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); } 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()); - } + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute, + String trustStoreFile, + String trustStorePassword) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, OkHttpConfig okhttpConfig) { Review Comment: prefer okhttpConfig => config ########## hugegraph-common/src/main/java/org/apache/hugegraph/rest/AbstractRestClient.java: ########## @@ -17,188 +17,245 @@ 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.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 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; 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 Client client; - private final WebTarget target; - - private PoolingHttpClientConnectionManager pool; - private ScheduledExecutorService cleanExecutor; + private final ThreadLocal<String> authContext; + private final OkHttpClient client; + private final String baseUrl; + private final Request.Builder requestBuilder; public AbstractRestClient(String url, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout).build()); + this(url, OkHttpConfig.builder() + .timeout(timeout) + .build()); } public AbstractRestClient(String url, String user, String password, - int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configUser(user, password) - .build()); + Integer timeout) { + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .build()); } public AbstractRestClient(String url, int timeout, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, null, null, timeout, maxTotal, maxPerRoute); } public AbstractRestClient(String url, int timeout, int idleTime, int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configIdleTime(idleTime) - .configPool(maxTotal, maxPerRoute) - .build()); + this(url, OkHttpConfig.builder() + .idleTime(idleTime) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .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()); + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); } 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()); - } + this(url, OkHttpConfig.builder() + .user(user).password(password) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .build()); + } + + public AbstractRestClient(String url, String token, Integer timeout, + Integer maxTotal, Integer maxPerRoute, + String trustStoreFile, + String trustStorePassword) { + this(url, OkHttpConfig.builder() + .token(token) + .timeout(timeout) + .maxTotal(maxTotal) + .maxPerRoute(maxPerRoute) + .trustStoreFile(trustStoreFile) + .trustStorePassword(trustStorePassword) + .build()); + } + + public AbstractRestClient(String url, OkHttpConfig okhttpConfig) { + this.baseUrl = url; + this.client = buildOkHttpClient(okhttpConfig); + this.requestBuilder = new Request.Builder(); + this.authContext = new InheritableThreadLocal<>(); + } + + private static RequestBody buildRequestBody(Object body, RestHeaders headers) { + String contentType = parseContentType(headers); + String bodyContent; + if (HttpHeadersConstant.APPLICATION_JSON.equals(contentType)) { + if (body == null) { + bodyContent = "{}"; + } else { + bodyContent = JsonUtil.toJson(body); + } + } else { + bodyContent = String.valueOf(body); + } + RequestBody requestBody = RequestBody.create(MediaType.parse(contentType), bodyContent); - public AbstractRestClient(String url, String token, int timeout) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configToken(token) - .build()); + if (headers != null && "gzip".equals(headers.get(HttpHeadersConstant.CONTENT_ENCODING))) { + requestBody = gzip(requestBody); + } + return requestBody; } - public AbstractRestClient(String url, String token, int timeout, - int maxTotal, int maxPerRoute) { - this(url, new ConfigBuilder().configTimeout(timeout) - .configToken(token) - .configPool(maxTotal, maxPerRoute) - .build()); + private static RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override + public MediaType contentType() { + return body.contentType(); + } + + @Override + public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override + public void writeTo(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(HttpHeadersConstant.CONTENT_TYPE); + if (contentType != null) { + return contentType; + } } + return HttpHeadersConstant.APPLICATION_JSON; } - protected abstract void checkStatus(Response response, - Response.Status... statuses); + private OkHttpClient buildOkHttpClient(OkHttpConfig okHttpConfig) { Review Comment: prefer `OkHttpConfig config` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@hugegraph.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org