forked from hugegraph/hugegraph-sync
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 <jin@apache.org>
This commit is contained in:
parent
b49be05171
commit
5ad55fb82f
|
|
@ -1,6 +1,7 @@
|
|||
name: "hugegraph-commons ci"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
name: "license checker"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 final ThreadLocal<String> authContext;
|
||||
|
||||
private static final String TOKEN_KEY = "tokenKey";
|
||||
private final OkHttpClient client;
|
||||
|
||||
private final Client client;
|
||||
private final WebTarget target;
|
||||
|
||||
private PoolingHttpClientConnectionManager pool;
|
||||
private ScheduledExecutorService cleanExecutor;
|
||||
private final String baseUrl;
|
||||
|
||||
public AbstractRestClient(String url, int timeout) {
|
||||
this(url, new ConfigBuilder().configTimeout(timeout).build());
|
||||
this(url, RestClientConfig.builder().timeout(timeout).build());
|
||||
}
|
||||
|
||||
public AbstractRestClient(String url, String user, String password,
|
||||
int timeout) {
|
||||
this(url, new ConfigBuilder().configTimeout(timeout)
|
||||
.configUser(user, password)
|
||||
.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 maxTotal, int maxPerRoute) {
|
||||
this(url, new ConfigBuilder().configTimeout(timeout)
|
||||
.configUser(user, password)
|
||||
.configPool(maxTotal, 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());
|
||||
}
|
||||
|
||||
public AbstractRestClient(String url, String token, int timeout) {
|
||||
this(url, new ConfigBuilder().configTimeout(timeout)
|
||||
.configToken(token)
|
||||
.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 token, int timeout,
|
||||
int maxTotal, int maxPerRoute) {
|
||||
this(url, new ConfigBuilder().configTimeout(timeout)
|
||||
.configToken(token)
|
||||
.configPool(maxTotal, maxPerRoute)
|
||||
.build());
|
||||
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, 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, RestClientConfig config) {
|
||||
this.baseUrl = url;
|
||||
this.client = buildOkHttpClient(config);
|
||||
this.authContext = new InheritableThreadLocal<>();
|
||||
}
|
||||
|
||||
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 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));
|
||||
|
||||
if (headers != null &&
|
||||
"gzip".equals(headers.get(RestHeaders.CONTENT_ENCODING))) {
|
||||
requestBody = gzipBody(requestBody);
|
||||
}
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
protected abstract void checkStatus(Response response,
|
||||
Response.Status... statuses);
|
||||
private static RequestBody gzipBody(final RequestBody body) {
|
||||
return new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return body.contentType();
|
||||
}
|
||||
|
||||
protected Response request(Callable<Response> method) {
|
||||
try {
|
||||
return method.call();
|
||||
} catch (Exception e) {
|
||||
throw new ClientException("Failed to do request", e);
|
||||
@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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
try (Response response = request(requestBuilder)) {
|
||||
checkStatus(response, 200);
|
||||
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.get();
|
||||
});
|
||||
|
||||
checkStatus(response, Response.Status.OK);
|
||||
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();
|
||||
});
|
||||
|
||||
checkStatus(response, Response.Status.NO_CONTENT,
|
||||
Response.Status.ACCEPTED);
|
||||
return new RestResult(response);
|
||||
}
|
||||
|
||||
protected abstract void checkStatus(Response response, int... statuses);
|
||||
|
||||
@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());
|
||||
}
|
||||
@SneakyThrows
|
||||
private X509TrustManager trustManagerForCertificates(String trustStoreFile,
|
||||
String trustStorePass) {
|
||||
char[] password = trustStorePass.toCharArray();
|
||||
|
||||
// load keyStore
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
try (FileInputStream in = new FileInputStream(trustStoreFile)) {
|
||||
keyStore.load(in, password);
|
||||
}
|
||||
|
||||
Builder builder = id == null ? target.path(path).request() :
|
||||
target.path(path).path(encode(id)).request();
|
||||
TrustManagerFactory trustManagerFactory =
|
||||
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(keyStore);
|
||||
|
||||
String encoding = null;
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
// Add headers
|
||||
builder = builder.headers(headers);
|
||||
encoding = (String) headers.getFirst("Content-Encoding");
|
||||
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
|
||||
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
|
||||
throw new IllegalStateException("Unexpected default trust managers:" +
|
||||
Arrays.toString(trustManagers));
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 RestClientConfig {
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.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;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.glassfish.jersey.internal.util.collection.ImmutableMultivaluedMap;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
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.RestResult;
|
||||
import org.apache.hugegraph.testutil.Assert;
|
||||
import org.apache.hugegraph.testutil.Whitebox;
|
||||
import lombok.SneakyThrows;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
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(), "");
|
||||
}
|
||||
|
||||
public RestClientImpl(String url, int timeout, int status,
|
||||
MultivaluedMap<String, Object> headers,
|
||||
String content) {
|
||||
super(url, timeout);
|
||||
this.status = status;
|
||||
this.headers = headers;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response request(Callable<Response> method) {
|
||||
Response response = Mockito.mock(Response.class);
|
||||
Mockito.when(response.getStatus()).thenReturn(this.status);
|
||||
Mockito.when(response.getHeaders()).thenReturn(this.headers);
|
||||
Mockito.when(response.readEntity(String.class))
|
||||
.thenReturn(this.content);
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkStatus(Response response,
|
||||
Response.Status... statuses) {
|
||||
boolean match = false;
|
||||
for (Response.Status status : statuses) {
|
||||
if (status.getStatusCode() == response.getStatus()) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
throw new ClientException("Invalid response '%s'", response);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
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");
|
||||
|
||||
WebTarget target = Mockito.mock(WebTarget.class);
|
||||
Builder builder = Mockito.mock(Builder.class);
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
OkHttpClient okHttpClient = Mockito.mock(OkHttpClient.class, Mockito.RETURNS_DEEP_STUBS);
|
||||
Mockito.when(okHttpClient.newCall(Mockito.any()).execute()).thenReturn(response);
|
||||
|
||||
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);
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
2
pom.xml
2
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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue