FIX ISSUE#766,remove OkHttp3 dependency (#800)

* 1. remove the okhttp dependency, use own implementation instead.
2. add logtracing util.

* remove unnecessary fields

* fix notes

* fix post bug

* fix url encoding
pull/805/head
严荣振 2 years ago committed by GitHub
parent 81f90bf6a6
commit b74c2db9cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,7 +8,6 @@ sidebar_position: 2
- <a href="#控制台线程池管理和线程池实例的区别">控制台线程池管理和线程池实例的区别</a>
- <a href="#示例项目为什么会有跨域请求">示例项目为什么会有跨域请求</a>
- <a href="#更新代码后运行时服务端sql报错">更新代码后运行时服务端SQL报错</a>
- <a href="#okhttp3-call-timeout-方法不存在">okHttp3 call.timeout() 方法不存在</a>
- <a href="#生产环境如何不启用动态线程池">生产环境如何不启用动态线程池</a>
- <a href="#server-端宕机会影响-client-运行么">Server 端宕机会影响 Client 运行么</a>
- <a href="#hippo4j-的发布方式是怎样的-如何选择正确的版本">Hippo4J 的发布方式是怎样的?如何选择正确的版本</a>
@ -48,18 +47,6 @@ Hippo4J 按照租户、项目、线程池的维度划分。
> 友情提示:每次执行数据库表或数据变更时,一定要保持提前备份的好习惯。
## okHttp3 call.timeout() 方法不存在
请确保 okHttp3 依赖版本号 >= 3.12.0
```xml
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.0</version>
</dependency>
```
## 生产环境如何不启用动态线程池
测试环境已经引入 Hippo4J暂时不打算上线生产环境。

@ -62,9 +62,5 @@
<groupId>com.github.dozermapper</groupId>
<artifactId>dozer-core</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies>
</project>

@ -63,4 +63,11 @@ public interface JsonFacade {
* @return
*/
<T> List<T> parseArray(String text, Class<T> clazz);
/**
* validate Json.
* @param text
* @return
*/
boolean isJson(String text);
}

@ -38,11 +38,13 @@ public class Constants {
public static final String DEFAULT_NAMESPACE_ID = "public";
public static final String ENCODE = "UTF-8";
public static final String NULL = "";
public static final String UP = "UP";
public static final String ENCODE = "UTF-8";
public static final String CONTENT_TYPE = "Content-Type";
public static final int CONFIG_LONG_POLL_TIMEOUT = 30000;

@ -0,0 +1,37 @@
/*
* 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 cn.hippo4j.common.constant;
/**
* header constants.
*/
public interface HttpHeaderConsts {
String CLIENT_VERSION_HEADER = "Client-Version";
String USER_AGENT_HEADER = "User-Agent";
String REQUEST_SOURCE_HEADER = "Request-Source";
String CONTENT_TYPE = "Content-Type";
String CONTENT_LENGTH = "Content-Length";
String ACCEPT_CHARSET = "Accept-Charset";
String ACCEPT_ENCODING = "Accept-Encoding";
String CONTENT_ENCODING = "Content-Encoding";
String CONNECTION = "Requester";
String REQUEST_ID = "RequestId";
String REQUEST_MODULE = "Request-Module";
}

@ -0,0 +1,111 @@
/*
* 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 cn.hippo4j.common.constant;
import cn.hippo4j.common.toolkit.StringUtil;
/**
* Http Media types.
*
* @author Rongzhen Yan
*/
public final class HttpMediaType {
public static final String APPLICATION_ATOM_XML = "application/atom+xml";
public static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8";
public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
public static final String APPLICATION_SVG_XML = "application/svg+xml";
public static final String APPLICATION_XHTML_XML = "application/xhtml+xml";
public static final String APPLICATION_XML = "application/xml;charset=UTF-8";
public static final String APPLICATION_JSON = "application/json;charset=UTF-8";
public static final String MULTIPART_FORM_DATA = "multipart/form-data;charset=UTF-8";
public static final String TEXT_HTML = "text/html;charset=UTF-8";
public static final String TEXT_PLAIN = "text/plain;charset=UTF-8";
private HttpMediaType(String type, String charset) {
this.type = type;
this.charset = charset;
}
/**
* content type.
*/
private final String type;
/**
* content type charset.
*/
private final String charset;
/**
* Parse the given String contentType into a {@code MediaType} object.
*
* @param contentType mediaType
* @return MediaType
*/
public static HttpMediaType valueOf(String contentType) {
if (StringUtil.isEmpty(contentType)) {
throw new IllegalArgumentException("MediaType must not be empty");
}
String[] values = contentType.split(";");
String charset = Constants.ENCODE;
for (String value : values) {
if (value.startsWith("charset=")) {
charset = value.substring("charset=".length());
}
}
return new HttpMediaType(values[0], charset);
}
/**
* Use the given contentType and charset to assemble into a {@code MediaType} object.
*
* @param contentType contentType
* @param charset charset
* @return MediaType
*/
public static HttpMediaType valueOf(String contentType, String charset) {
if (StringUtil.isEmpty(contentType)) {
throw new IllegalArgumentException("MediaType must not be empty");
}
String[] values = contentType.split(";");
return new HttpMediaType(values[0], StringUtil.isEmpty(charset) ? Constants.ENCODE : charset);
}
public String getType() {
return type;
}
public String getCharset() {
return charset;
}
@Override
public String toString() {
return type + ";charset=" + charset;
}
}

@ -0,0 +1,42 @@
/*
* 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 cn.hippo4j.common.constant;
/**
* Http method constants.
*
* @author Rongzhen Yan
*/
public class HttpMethod {
public static final String GET = "GET";
public static final String HEAD = "HEAD";
public static final String POST = "POST";
public static final String PUT = "PUT";
public static final String PATCH = "PATCH";
public static final String DELETE = "DELETE";
public static final String OPTIONS = "OPTIONS";
public static final String TRACE = "TRACE";
}

@ -0,0 +1,67 @@
/*
* 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 cn.hippo4j.common.constant;
/**
* Http
* @author Rongzhen Yan
*/
public interface HttpResponseCode {
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
}

@ -1,299 +0,0 @@
/*
* 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 cn.hippo4j.common.toolkit;
import cn.hippo4j.common.web.exception.ServiceException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.util.CollectionUtils;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* HttpClient util.
*/
@Slf4j
public class HttpClientUtil {
private OkHttpClient hippo4JOkHttpClient;
private static AtomicReference<HttpClientUtil> reference = new AtomicReference<>();
private MediaType jsonMediaType = MediaType.parse("application/json; charset=utf-8");
private static int HTTP_OK_CODE = 200;
private HttpClientUtil() {
OkHttpClient.Builder build = new OkHttpClient.Builder();
build.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
supportHttps(build);
this.hippo4JOkHttpClient = build.build();
}
public static HttpClientUtil build() {
if (reference.get() == null) {
reference.compareAndSet(null, new HttpClientUtil());
}
return reference.get();
}
/**
* Get.
*
* @param url
* @return
*/
@SneakyThrows
public String get(String url) {
try {
return new String(doGet(url), "utf-8");
} catch (Exception e) {
log.error("HttpGet call failed. {}", url, e);
throw e;
}
}
/**
* Get request, supports adding query string.
*
* @param url
* @param queryString
* @return
*/
public String get(String url, Map<String, String> queryString) {
String fullUrl = buildUrl(url, queryString);
return get(fullUrl);
}
/**
* Deserialize directly after getting Json.
*
* @param url
* @param clazz
* @return
*/
public <T> T restApiGet(String url, Class<T> clazz) {
String resp = get(url);
return JSONUtil.parseObject(resp, clazz);
}
/**
* Call health check.
*
* @param url
* @param clazz
* @param <T>
* @return
*/
@SneakyThrows
public <T> T restApiGetHealth(String url, Class<T> clazz) {
String resp = new String(doGet(url), "utf-8");
return JSONUtil.parseObject(resp, clazz);
}
/**
* Get request, supports query string.
*
* @param url
* @param queryString
* @param clazz
* @param <T>
* @return
*/
public <T> T restApiGet(String url, Map<String, String> queryString, Class<T> clazz) {
String fullUrl = buildUrl(url, queryString);
String resp = get(fullUrl);
return JSONUtil.parseObject(resp, clazz);
}
/**
* Rest interface Post call.
*
* @param url
* @param body
* @return
*/
public String restApiPost(String url, Object body) {
try {
return doPost(url, body);
} catch (Exception e) {
log.error("HttpPost call failed. {} message: {}", url, e.getMessage());
throw e;
}
}
/**
* Rest interface Post call. Deserialize the return value directly.
*
* @param url
* @param body
* @return
*/
public <T> T restApiPost(String url, Object body, Class<T> clazz) {
String resp = restApiPost(url, body);
return JSONUtil.parseObject(resp, clazz);
}
/**
* Constructs a complete Url from the query string.
*
* @param url
* @param queryString
* @return
*/
public String buildUrl(String url, Map<String, String> queryString) {
if (null == queryString) {
return url;
}
StringBuilder builder = new StringBuilder(url);
boolean isFirst = true;
for (Map.Entry<String, String> entry : queryString.entrySet()) {
String key = entry.getKey();
if (key != null && entry.getValue() != null) {
if (isFirst) {
isFirst = false;
builder.append("?");
} else {
builder.append("&");
}
builder.append(key)
.append("=")
.append(queryString.get(key));
}
}
return builder.toString();
}
@SneakyThrows
private String doPost(String url, Object body) {
String bodyContent;
if (body instanceof String) {
bodyContent = (String) body;
} else {
bodyContent = JSONUtil.toJSONString(body);
}
RequestBody requestBody = RequestBody.create(jsonMediaType, bodyContent);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
try (Response resp = hippo4JOkHttpClient.newCall(request).execute()) {
try (ResponseBody responseBody = resp.body()) {
if (resp.code() != HTTP_OK_CODE) {
String msg = String.format("HttpPost response code error. [code] %s [url] %s [body] %s", resp.code(), url, bodyContent);
throw new ServiceException(msg);
}
return responseBody.string();
}
}
}
@SneakyThrows
private byte[] doGet(String url) {
Request request = new Request.Builder().get().url(url).build();
try (Response resp = hippo4JOkHttpClient.newCall(request).execute()) {
try (ResponseBody responseBody = resp.body()) {
if (resp.code() != HTTP_OK_CODE) {
String msg = String.format("HttpGet response code error. [code] %s [url] %s", resp.code(), url);
throw new ServiceException(msg);
}
return responseBody.bytes();
}
}
}
@SneakyThrows
public <T> T restApiGetByThreadPool(String url, Map<String, String> headers, Map<String, String> paramValues, Long readTimeoutMs, Class<T> clazz) {
String buildUrl = buildUrl(url, paramValues);
Request.Builder builder = new Request.Builder().get();
if (!CollectionUtils.isEmpty(headers)) {
builder.headers(Headers.of(headers));
}
Request request = builder.url(buildUrl).build();
Call call = hippo4JOkHttpClient.newCall(request);
// TODO Plan to optimize the timout api because its version is too high.
call.timeout().timeout(readTimeoutMs, TimeUnit.MILLISECONDS);
try (Response resp = call.execute()) {
try (ResponseBody responseBody = resp.body()) {
if (resp.code() != HTTP_OK_CODE) {
String msg = String.format("HttpGet response code error. [code] %s [url] %s", resp.code(), url);
log.error(msg);
throw new ServiceException(msg);
}
return JSONUtil.parseObject(responseBody.string(), clazz);
}
}
}
@SneakyThrows
public <T> T restApiPostByThreadPool(String url, Map<String, String> headers, Map<String, String> paramValues, Long readTimeoutMs, Class<T> clazz) {
String buildUrl = buildUrl(url, paramValues);
Request request = new Request.Builder()
.url(buildUrl)
.headers(Headers.of(headers))
.post(RequestBody.create(jsonMediaType, ""))
.build();
Call call = hippo4JOkHttpClient.newCall(request);
// TODO Plan to optimize the timout api because its version is too high.
call.timeout().timeout(readTimeoutMs, TimeUnit.MILLISECONDS);
try (Response resp = call.execute()) {
try (ResponseBody responseBody = resp.body()) {
if (resp.code() != HTTP_OK_CODE) {
String msg = String.format("HttpPost response code error. [code] %s [url] %s.", resp.code(), url);
log.error(msg);
throw new ServiceException(msg);
}
return JSONUtil.parseObject(responseBody.string(), clazz);
}
}
}
@SneakyThrows
private void supportHttps(OkHttpClient.Builder builder) {
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier((hostname, session) -> true);
}
}

@ -0,0 +1,341 @@
/*
* 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 cn.hippo4j.common.toolkit;
import cn.hippo4j.common.constant.Constants;
import lombok.SneakyThrows;
import java.io.*;
import java.net.HttpURLConnection;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* IO related tool methods.
*
* @author nacos
*/
public class IoUtils {
/**
* Try decompress by GZIP from stream.
*
* @param raw compress stream
* @return byte array after decompress
*/
public static byte[] tryDecompress(InputStream raw) throws IOException {
try (
GZIPInputStream gis = new GZIPInputStream(raw);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
copy(gis, out);
return out.toByteArray();
}
}
/**
* Try decompress by GZIP from byte array.
*
* @param raw compressed byte array
* @return byte array after decompress
* @throws Exception exception
*/
public static byte[] tryDecompress(byte[] raw) throws Exception {
if (!isGzipStream(raw)) {
return raw;
}
try (
GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(raw));
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
copy(gis, out);
return out.toByteArray();
}
}
/**
* Try compress by GZIP for string.
*
* @param str strings to be compressed.
* @param encoding encoding.
* @return byte[]
*/
public static byte[] tryCompress(String str, String encoding) {
if (str == null || str.length() == 0) {
return new byte[0];
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
gzip.write(str.getBytes(encoding));
} catch (Exception e) {
e.printStackTrace();
}
return out.toByteArray();
}
private static BufferedReader toBufferedReader(Reader reader) {
return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader);
}
/**
* Write string to a file.
*
* @param file file
* @param data string
* @param encoding encoding of string
* @throws IOException io exception
*/
public static void writeStringToFile(File file, String data, String encoding) throws IOException {
try (OutputStream os = new FileOutputStream(file)) {
os.write(data.getBytes(encoding));
os.flush();
}
}
/**
* Read lines.
*
* @param input reader
* @return list of line
* @throws IOException io exception
*/
public static List<String> readLines(Reader input) throws IOException {
BufferedReader reader = toBufferedReader(input);
List<String> list = new ArrayList<>();
while (true) {
String line = reader.readLine();
if (null != line) {
if (StringUtil.isNotEmpty(line)) {
list.add(line.trim());
}
} else {
break;
}
}
return list;
}
/**
* To string from stream.
*
* @param input stream
* @param encoding charset of stream
* @return string
* @throws IOException io exception
*/
@SneakyThrows
public static String toString(InputStream input, String encoding) {
if (input == null) {
return StringUtil.EMPTY;
}
return (null == encoding) ? toString(new InputStreamReader(input, Constants.ENCODE))
: toString(new InputStreamReader(input, encoding));
}
/**
* To string from reader.
*
* @param reader reader
* @return string
* @throws IOException io exception
*/
public static String toString(Reader reader) throws IOException {
CharArrayWriter sw = new CharArrayWriter();
copy(reader, sw);
return sw.toString();
}
/**
* Copy data.
*
* @param input source
* @param output target
* @return copy size
* @throws IOException io exception
*/
public static long copy(Reader input, Writer output) throws IOException {
char[] buffer = new char[1 << 12];
long count = 0;
for (int n = 0; (n = input.read(buffer)) >= 0;) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
/**
* Copy data.
*
* @param input source
* @param output target
* @return copy size
* @throws IOException io exception
*/
public static long copy(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[1024];
int bytesRead;
int totalBytes = 0;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
}
return totalBytes;
}
/**
* Delete file or dir.
*
* <p>If is dir, clean directory, do not delete dir.
*
* <p>If is file, delete file.
*
* @param fileOrDir file or dir
* @throws IOException io exception
*/
public static void delete(File fileOrDir) throws IOException {
if (fileOrDir == null) {
return;
}
if (fileOrDir.isDirectory()) {
cleanDirectory(fileOrDir);
} else {
if (fileOrDir.exists()) {
boolean isDeleteOk = fileOrDir.delete();
if (!isDeleteOk) {
throw new IOException("delete fail");
}
}
}
}
/**
* . Clean content under directory.
*
* @param directory directory
* @throws IOException io exception
*/
public static void cleanDirectory(File directory) throws IOException {
if (!directory.exists()) {
String message = directory + " does not exist";
throw new IllegalArgumentException(message);
}
if (!directory.isDirectory()) {
String message = directory + " is not a directory";
throw new IllegalArgumentException(message);
}
File[] files = directory.listFiles();
// null if security restricted
if (files == null) {
throw new IOException("Failed to list contents of " + directory);
}
IOException exception = null;
for (File file : files) {
try {
delete(file);
} catch (IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
/**
* Copy File.
*
* @param source source file path
* @param target target file path
* @throws IOException io exception
*/
public static void copyFile(String source, String target) throws IOException {
File sf = new File(source);
if (!sf.exists()) {
throw new IllegalArgumentException("source file does not exist.");
}
File tf = new File(target);
if (!tf.getParentFile().mkdirs()) {
throw new RuntimeException("failed to create parent directory.");
}
if (!tf.exists() && !tf.createNewFile()) {
throw new RuntimeException("failed to create target file.");
}
try (
FileChannel sc = new FileInputStream(sf).getChannel();
FileChannel tc = new FileOutputStream(tf).getChannel()) {
sc.transferTo(0, sc.size(), tc);
}
}
/**
* Judge whether is Gzip stream.
*
* @param bytes byte array
* @return true if is gzip, otherwise false
*/
public static boolean isGzipStream(byte[] bytes) {
int minByteArraySize = 2;
if (bytes == null || bytes.length < minByteArraySize) {
return false;
}
return GZIPInputStream.GZIP_MAGIC == ((bytes[1] << 8 | bytes[0]) & 0xFFFF);
}
/**
* Close http connection quietly.
*
* @param connection http connection
*/
public static void closeQuietly(HttpURLConnection connection) {
if (connection != null) {
try {
closeQuietly(connection.getInputStream());
} catch (Exception ignore) {
}
}
}
/**
* Close closable object quietly.
*
* @param closeable http connection
*/
public static void closeQuietly(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException ignored) {
}
}
public static void closeQuietly(Closeable... closeable) {
Arrays.stream(closeable).forEach(IoUtils::closeQuietly);
}
}

@ -56,4 +56,11 @@ public class JSONUtil {
}
return JSON_FACADE.parseArray(text, clazz);
}
public static boolean isJson(String json) {
if (StringUtil.isBlank(json)) {
return false;
}
return JSON_FACADE.isJson(json);
}
}

@ -20,11 +20,9 @@ package cn.hippo4j.common.toolkit;
import cn.hippo4j.common.api.JsonFacade;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.CollectionType;
import lombok.SneakyThrows;
@ -74,4 +72,14 @@ public class JacksonHandler implements JsonFacade {
CollectionType collectionType = MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, clazz);
return MAPPER.readValue(text, collectionType);
}
@Override
public boolean isJson(String text) {
try {
MAPPER.readTree(text);
return true;
} catch (JsonProcessingException jpe) {
return false;
}
}
}

@ -0,0 +1,169 @@
/*
* 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 cn.hippo4j.common.toolkit;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Predicate;
/**
* Map utils.
*/
public class MapUtil {
/**
* Null-safe check if the specified Dictionary is empty.
*
* <p>Null returns true.
*
* @param map the collection to check, may be null
* @return true if empty or null
*/
public static boolean isEmpty(Map map) {
return (map == null || map.isEmpty());
}
/**
* Null-safe check if the specified Dictionary is empty.
*
* <p>Null returns true.
*
* @param coll the collection to check, may be null
* @return true if empty or null
*/
public static boolean isEmpty(Dictionary coll) {
return (coll == null || coll.isEmpty());
}
/**
* Null-safe check if the specified Dictionary is not empty.
*
* <p>Null returns false.
*
* @param map the collection to check, may be null
* @return true if non-null and non-empty
*/
public static boolean isNotEmpty(Map map) {
return !isEmpty(map);
}
/**
* Null-safe check if the specified Dictionary is not empty.
*
* <p>Null returns false.
*
* @param coll the collection to check, may be null
* @return true if non-null and non-empty
*/
public static boolean isNotEmpty(Dictionary coll) {
return !isEmpty(coll);
}
/**
* Put into map if value is not null.
*
* @param target target map
* @param key key
* @param value value
*/
public static void putIfValNoNull(Map target, Object key, Object value) {
Objects.requireNonNull(key, "key");
if (value != null) {
target.put(key, value);
}
}
/**
* Put into map if value is not empty.
*
* @param target target map
* @param key key
* @param value value
*/
public static void putIfValNoEmpty(Map target, Object key, Object value) {
Objects.requireNonNull(key, "key");
if (value instanceof String) {
if (StringUtil.isNotEmpty((String) value)) {
target.put(key, value);
}
return;
}
if (value instanceof Collection) {
if (CollectionUtil.isNotEmpty((Collection) value)) {
target.put(key, value);
}
return;
}
if (value instanceof Map) {
if (isNotEmpty((Map) value)) {
target.put(key, value);
}
return;
}
if (value instanceof Dictionary) {
if (isNotEmpty((Dictionary) value)) {
target.put(key, value);
}
}
}
/**
* ComputeIfAbsent lazy load.
*
* @param target target Map data.
* @param key map key.
* @param mappingFunction function which is need to be executed.
* @param param1 function's parameter value1.
* @param param2 function's parameter value1.
* @return
*/
public static <K, C, V, T> V computeIfAbsent(Map<K, V> target, K key, BiFunction<C, T, V> mappingFunction, C param1,
T param2) {
Objects.requireNonNull(target, "target");
Objects.requireNonNull(key, "key");
Objects.requireNonNull(mappingFunction, "mappingFunction");
Objects.requireNonNull(param1, "param1");
Objects.requireNonNull(param2, "param2");
V val = target.get(key);
if (val == null) {
V ret = mappingFunction.apply(param1, param2);
target.put(key, ret);
return ret;
}
return val;
}
/**
* remove value, Thread safety depends on whether the Map is a thread-safe Map.
*
* @param map map
* @param key key
* @param removeJudge judge this key can be remove
* @param <K> key type
* @param <V> value type
* @return value
*/
public static <K, V> V removeKey(Map<K, V> map, K key, Predicate<V> removeJudge) {
return map.computeIfPresent(key, (k, v) -> removeJudge.test(v) ? null : v);
}
}

@ -0,0 +1,200 @@
/*
* 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 cn.hippo4j.common.toolkit.http;
import cn.hippo4j.common.constant.Constants;
import cn.hippo4j.common.constant.HttpHeaderConsts;
import cn.hippo4j.common.constant.HttpMediaType;
import cn.hippo4j.common.toolkit.MapUtil;
import cn.hippo4j.common.toolkit.StringUtil;
import java.util.*;
/**
* Http header.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class Header {
public static final Header EMPTY = Header.newInstance();
private final Map<String, String> header;
private final Map<String, List<String>> originalResponseHeader;
private static final String DEFAULT_CHARSET = "UTF-8";
private static final String DEFAULT_ENCODING = "gzip";
private Header() {
header = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
originalResponseHeader = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
addParam(HttpHeaderConsts.CONTENT_TYPE, HttpMediaType.APPLICATION_JSON);
addParam(HttpHeaderConsts.ACCEPT_CHARSET, DEFAULT_CHARSET);
// addParam(HttpHeaderConsts.ACCEPT_ENCODING, DEFAULT_ENCODING);
}
public static Header newInstance() {
return new Header();
}
/**
* Add the key and value to the header.
*
* @param key the key
* @param value the value
* @return header
*/
public Header addParam(String key, String value) {
if (StringUtil.isNotEmpty(key)) {
header.put(key, value);
}
return this;
}
public Header setContentType(String contentType) {
if (contentType == null) {
contentType = HttpMediaType.APPLICATION_JSON;
}
return addParam(HttpHeaderConsts.CONTENT_TYPE, contentType);
}
public Header build() {
return this;
}
public String getValue(String key) {
return header.get(key);
}
public Map<String, String> getHeader() {
return header;
}
public Iterator<Map.Entry<String, String>> iterator() {
return header.entrySet().iterator();
}
/**
* Transfer to KV part list. The odd index is key and the even index is value.
*
* @return KV string list
*/
public List<String> toList() {
List<String> list = new ArrayList<>(header.size() * 2);
Iterator<Map.Entry<String, String>> iterator = iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
list.add(entry.getKey());
list.add(entry.getValue());
}
return list;
}
/**
* Add all KV list to header. The odd index is key and the even index is value.
*
* @param list KV list
* @return header
*/
public Header addAll(List<String> list) {
if ((list.size() & 1) != 0) {
throw new IllegalArgumentException("list size must be a multiple of 2");
}
for (int i = 0; i < list.size();) {
String key = list.get(i++);
if (StringUtil.isNotEmpty(key)) {
header.put(key, list.get(i++));
}
}
return this;
}
/**
* Add all parameters to header.
*
* @param params parameters
*/
public void addAll(Map<String, String> params) {
if (MapUtil.isNotEmpty(params)) {
for (Map.Entry<String, String> entry : params.entrySet()) {
addParam(entry.getKey(), entry.getValue());
}
}
}
/**
* set original format response header.
*
* <p>Currently only corresponds to the response header of JDK.
*
* @param key original response header key
* @param values original response header values
*/
public void addOriginalResponseHeader(String key, List<String> values) {
if (StringUtil.isNotEmpty(key)) {
this.originalResponseHeader.put(key, values);
addParam(key, values.get(0));
}
}
/**
* get original format response header.
*
* <p>Currently only corresponds to the response header of JDK.
*
* @return Map original response header
*/
public Map<String, List<String>> getOriginalResponseHeader() {
return this.originalResponseHeader;
}
public String getCharset() {
String acceptCharset = getValue(HttpHeaderConsts.ACCEPT_CHARSET);
if (acceptCharset == null) {
String contentType = getValue(HttpHeaderConsts.CONTENT_TYPE);
acceptCharset = StringUtil.isNotBlank(contentType) ? analysisCharset(contentType) : Constants.ENCODE;
}
return acceptCharset;
}
private String analysisCharset(String contentType) {
String[] values = contentType.split(";");
String charset = Constants.ENCODE;
if (values.length == 0) {
return charset;
}
for (String value : values) {
if (value.startsWith("charset=")) {
charset = value.substring("charset=".length());
}
}
return charset;
}
public void clear() {
header.clear();
originalResponseHeader.clear();
}
@Override
public String toString() {
return "Header{" + "headerToMap=" + header + '}';
}
}

@ -0,0 +1,69 @@
/*
* 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 cn.hippo4j.common.toolkit.http;
import java.io.Closeable;
import java.io.InputStream;
/**
* Represents a client-side HTTP response.
*
* @author mai.jh
*/
public interface HttpClientResponse extends Closeable {
/**
* Return the headers of this message.
*
* @return a corresponding HttpHeaders object (never {@code null})
*/
Header getHeaders();
/**
* Return the body of the message as an input stream.
*
* @return String response body
*/
InputStream getBody();
/**
* Return the HTTP status code.
*
* @return the HTTP status as an integer
*/
int getStatusCode();
/**
* Return the HTTP status text of the response.
*
* @return the HTTP status text
*/
String getStatusText();
/**
* Return the body As string.
* @return
*/
String getBodyString();
/**
* close response InputStream.
*/
@Override
void close();
}

@ -0,0 +1,228 @@
/*
* 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 cn.hippo4j.common.toolkit.http;
import cn.hippo4j.common.constant.Constants;
import cn.hippo4j.common.constant.HttpMediaType;
import cn.hippo4j.common.constant.HttpMethod;
import cn.hippo4j.common.constant.HttpResponseCode;
import cn.hippo4j.common.toolkit.IoUtils;
import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.logtracing.LogMessage;
import cn.hippo4j.common.web.exception.ServiceException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
import static cn.hippo4j.common.constant.HttpHeaderConsts.CONTENT_LENGTH;
/**
* Http request utilities.
* @author Rongzhen Yan
*/
@Slf4j
public class HttpUtils {
private static final int CONNECT_TIMEOUT = 10000;
private static final int READ_TIMEOUT = 300000;
private HttpUtils() {
}
public static <T> T post(String url, Object body, Class<T> clazz) {
String result = post(url, body);
return JSONUtil.parseObject(result, clazz);
}
public static <T> T post(String url, Object body, long timeoutMillis, Class<T> clazz) {
String result = post(url, body, timeoutMillis);
return JSONUtil.parseObject(result, clazz);
}
public static <T> T post(String url, Map<String, String> headers, Map<String, String> params, long timeoutMillis, Class<T> clazz) {
String result = execute(buildUrl(url, params), HttpMethod.POST, null, headers, timeoutMillis).getBodyString();
return JSONUtil.parseObject(result, clazz);
}
public static <T> T post(String url, Map<String, String> headers, Object body, long timeoutMillis, Class<T> clazz) {
String result = execute(url, HttpMethod.POST, body, headers, timeoutMillis).getBodyString();
return JSONUtil.parseObject(result, clazz);
}
public static String post(String url, Object body) {
return execute(url, HttpMethod.POST, body, null).getBodyString();
}
public static String post(String url, Object body, long timeoutMillis) {
return execute(url, HttpMethod.POST, body, null, timeoutMillis).getBodyString();
}
public static String postJson(String url, String json) {
return executeJson(url, HttpMethod.POST, json, null).getBodyString();
}
public static String put(String url, Object body) {
return execute(url, HttpMethod.PUT, body, null).getBodyString();
}
public static String put(String url, Object body, Map<String, String> headers) {
return execute(url, HttpMethod.PUT, body, headers).getBodyString();
}
public static <T> T get(String url, Map<String, String> headers, Map<String, String> params, long readTimeoutMillis, Class<T> clazz) {
String result = execute(buildUrl(url, params), HttpMethod.GET, null, headers, readTimeoutMillis).getBodyString();
return JSONUtil.parseObject(result, clazz);
}
public static String get(String url, Map<String, String> params) {
return execute(buildUrl(url, params), HttpMethod.GET, null, null).getBodyString();
}
public static String get(String url) {
return execute(url, HttpMethod.GET, null, null).getBodyString();
}
public static <T> T get(String url, Class<T> clazz) {
return JSONUtil.parseObject(get(url), clazz);
}
/**
* Constructs a complete Url from the query string.
* @param url
* @param queryString
* @return
*/
@SneakyThrows
public static String buildUrl(String url, Map<String, String> queryString) {
if (null == queryString) {
return url;
}
StringBuilder builder = new StringBuilder(url);
boolean isFirst = true;
for (Map.Entry<String, String> entry : queryString.entrySet()) {
String key = entry.getKey();
if (key != null && entry.getValue() != null) {
if (isFirst) {
isFirst = false;
builder.append("?");
} else {
builder.append("&");
}
String value = URLEncoder.encode(queryString.get(key), Constants.ENCODE)
.replaceAll("\\+", "%20");
builder.append(key)
.append("=")
.append(value);
}
}
return builder.toString();
}
public static HttpClientResponse execute(String url, String method, Object param, Map<String, String> headers) {
HttpURLConnection connection = createConnection(url, method);
return doExecute(connection, param, headers);
}
@SneakyThrows
public static HttpClientResponse doExecute(HttpURLConnection connection, Object body, Map<String, String> headers) {
try {
if (headers != null) {
for (String key : headers.keySet()) {
connection.setRequestProperty(key, headers.get(key));
}
}
String bodyString;
if (body instanceof String) {
bodyString = (String) body;
} else {
bodyString = JSONUtil.toJSONString(body);
}
if (!StringUtil.isEmpty(bodyString)) {
connection.setDoOutput(true);
byte[] b = bodyString.getBytes();
connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(b.length));
OutputStream outputStream = connection.getOutputStream();
outputStream.write(b, 0, b.length);
outputStream.flush();
IoUtils.closeQuietly(outputStream);
}
connection.connect();
JdkHttpClientResponse response = new JdkHttpClientResponse(connection);
if (HttpResponseCode.SC_OK != response.getStatusCode()) {
String msg = String.format("HttpPost response code error. [code] %s [url] %s [body] %s", response.getStatusCode(), connection.getURL(), response.getBodyString());
throw new ServiceException(msg);
}
return response;
} catch (Throwable e) {
log.error(LogMessage.getInstance().setMsg("Http Call error.")
.kv("url", connection.getURL())
.kv("method", connection.getRequestMethod())
.kv("body", JSONUtil.toJSONString(body))
.kv2String("headers", JSONUtil.toJSONString(headers)), e);
throw e;
}
}
public static HttpClientResponse execute(String url, String method, Object body, Map<String, String> headers, long timeout) {
HttpURLConnection connection = createConnection(url, method, timeout);
return doExecute(connection, body, headers);
}
public static HttpClientResponse executeJson(String url, String method, String json, Map<String, String> headers) {
if (!JSONUtil.isJson(json)) {
log.error(LogMessage.getInstance().setMsg("Http Call error.")
.kv("url", url)
.kv("method", method)
.kv("json", json)
.kv2String("headers", JSONUtil.toJSONString(headers)));
throw new ServiceException("invalid http json body, please check it again.");
}
return execute(url, method, json, headers);
}
@SneakyThrows
private static HttpURLConnection createConnection(String url, String method) {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setConnectTimeout(CONNECT_TIMEOUT);
connection.setReadTimeout(READ_TIMEOUT);
connection.setRequestMethod(method);
connection.setRequestProperty(Constants.CONTENT_TYPE, HttpMediaType.APPLICATION_JSON);
return connection;
}
@SneakyThrows
private static HttpURLConnection createConnection(String url, String method, long timeout) {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setConnectTimeout(Integer.parseInt(String.valueOf(timeout)));
connection.setReadTimeout(Integer.parseInt(String.valueOf(timeout)));
connection.setRequestMethod(method);
connection.setRequestProperty(Constants.CONTENT_TYPE, HttpMediaType.APPLICATION_JSON);
return connection;
}
}

@ -0,0 +1,97 @@
/*
* 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 cn.hippo4j.common.toolkit.http;
import cn.hippo4j.common.constant.Constants;
import cn.hippo4j.common.constant.HttpHeaderConsts;
import cn.hippo4j.common.toolkit.IoUtils;
import lombok.SneakyThrows;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
/**
* Represents a client-side HTTP response with JDK implementation
*
* @author Rongzhen Yan
*/
public class JdkHttpClientResponse implements HttpClientResponse {
private final HttpURLConnection conn;
private InputStream responseStream;
private Header responseHeader;
private static final String CONTENT_ENCODING = "gzip";
public JdkHttpClientResponse(HttpURLConnection conn) {
this.conn = conn;
}
@Override
public Header getHeaders() {
if (this.responseHeader == null) {
this.responseHeader = Header.newInstance();
}
for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
this.responseHeader.addOriginalResponseHeader(entry.getKey(), entry.getValue());
}
return this.responseHeader;
}
@Override
@SneakyThrows
public InputStream getBody() {
Header headers = getHeaders();
InputStream errorStream = this.conn.getErrorStream();
this.responseStream = (errorStream != null ? errorStream : this.conn.getInputStream());
String contentEncoding = headers.getValue(HttpHeaderConsts.CONTENT_ENCODING);
// Used to process http content_encoding, when content_encoding is GZIP, use GZIPInputStream
if (CONTENT_ENCODING.equals(contentEncoding)) {
byte[] bytes = IoUtils.tryDecompress(this.responseStream);
return new ByteArrayInputStream(bytes);
}
return this.responseStream;
}
@Override
@SneakyThrows
public int getStatusCode() {
return this.conn.getResponseCode();
}
@Override
@SneakyThrows
public String getStatusText() {
return this.conn.getResponseMessage();
}
@Override
public String getBodyString() {
return IoUtils.toString(this.getBody(), Constants.ENCODE);
}
@Override
public void close() {
IoUtils.closeQuietly(this.responseStream);
}
}

@ -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 cn.hippo4j.common.toolkit.logtracing;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* KV
* @author Rongzhen Yan
*/
public class LogMessage {
private Map<String, Object> kvs = new ConcurrentHashMap<>();
private String msg = "";
private LogMessage() {
}
public static LogMessage getInstance() {
return new LogMessage();
}
public LogMessage setMsg(String msg) {
this.msg = msg;
return this;
}
public String msg(String msg, Object... args) {
LogMessage l = new LogMessage();
l.kvs = this.kvs;
return l.setMsgString(msg, args);
}
public LogMessage setMsg(String msg, Object... args) {
FormattingTuple ft = MessageFormatter.arrayFormat(msg, args);
this.msg = ft.getThrowable() == null ? ft.getMessage() : ft.getMessage() + "||_fmt_throw=" + ft.getThrowable();
return this;
}
public String setMsgString(String msg, Object... args) {
FormattingTuple ft = MessageFormatter.arrayFormat(msg, args);
this.msg = ft.getThrowable() == null ? ft.getMessage() : ft.getMessage() + "||_fmt_throw=" + ft.getThrowable();
return toString();
}
public LogMessage kv(String k, Object v) {
this.kvs.put(k, v == null ? "" : v);
return this;
}
public String kv2String(String k, Object v) {
this.kvs.put(k, v == null ? "" : v);
return toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (msg != null && !msg.isEmpty()) {
sb.append("||_msg=").append(msg);
}
for (Map.Entry<String, Object> kv : kvs.entrySet()) {
sb.append("||" + kv.getKey() + "=").append(kv.getValue());
}
return sb.toString();
}
}

@ -15,17 +15,19 @@
* limitations under the License.
*/
package cn.hippo4j.common.toolkit;
package cn.hippo4j.common.toolkit.http;
import cn.hippo4j.common.toolkit.JSONUtil;
import lombok.Getter;
import lombok.Setter;
import org.junit.Assert;
import org.junit.Test;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
public class HttpClientUtilTest {
public class HttpUtilsTest {
/**
* test post url
@ -37,11 +39,9 @@ public class HttpClientUtilTest {
*/
static String getUrl = "https://hippo4j.cn/";
HttpClientUtil httpClientUtil = HttpClientUtil.build();
@Test
public void get() {
String s = httpClientUtil.get(getUrl);
String s = HttpUtils.get(getUrl);
Assert.assertNotNull(s);
}
@ -52,7 +52,7 @@ public class HttpClientUtilTest {
loginInfo.setPassword("hippo4j");
loginInfo.setUsername("hippo4j");
loginInfo.setRememberMe(1);
String s = httpClientUtil.restApiPost(loginUrl, loginInfo);
String s = HttpUtils.post(loginUrl, loginInfo);
Result result = JSONUtil.parseObject(s, Result.class);
Assert.assertNotNull(result);
String data = result.getData().getData();
@ -66,18 +66,28 @@ public class HttpClientUtilTest {
loginInfo.setPassword("hippo4j");
loginInfo.setUsername("hippo4j");
loginInfo.setRememberMe(1);
Result result = httpClientUtil.restApiPost(loginUrl, loginInfo, Result.class);
Result result = HttpUtils.post(loginUrl, loginInfo, Result.class);
Assert.assertNotNull(result);
String data = result.getData().getData();
Assert.assertNotNull(data);
}
@Test
public void testRestApiPostTimeout() {
String loginUrl = postUrl + "auth/login";
LoginInfo loginInfo = new LoginInfo();
loginInfo.setPassword("hippo4j");
loginInfo.setUsername("hippo4j");
loginInfo.setRememberMe(1);
Assert.assertThrows(SocketTimeoutException.class, () -> HttpUtils.post(loginUrl, loginInfo, 1, Result.class));
}
@Test
public void buildUrl() {
Map<String, String> map = new HashMap<>();
map.put("password", "hippo4j");
map.put("username", "hippo4j");
String s = httpClientUtil.buildUrl(getUrl, map);
String s = HttpUtils.buildUrl(getUrl, map);
Assert.assertEquals(getUrl + "?password=hippo4j&username=hippo4j", s);
}

@ -23,9 +23,9 @@ import cn.hippo4j.common.design.observer.AbstractSubjectCenter;
import cn.hippo4j.common.design.observer.Observer;
import cn.hippo4j.common.design.observer.ObserverMessage;
import cn.hippo4j.common.toolkit.CollectionUtil;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.common.web.base.Result;
import cn.hippo4j.config.model.biz.adapter.ThreadPoolAdapterReqDTO;
import cn.hippo4j.config.model.biz.adapter.ThreadPoolAdapterRespDTO;
@ -46,8 +46,6 @@ import static cn.hippo4j.common.constant.Constants.IDENTIFY_SLICER_SYMBOL;
@Service
public class ThreadPoolAdapterService {
private HttpClientUtil httpClientUtil = HttpClientUtil.build();
/**
* Map&lt;mark, Map&lt;tenantItem, Map&lt;threadPoolKey, List&lt;ThreadPoolAdapterState&gt;&gt;&gt;&gt;
*/
@ -103,7 +101,7 @@ public class ThreadPoolAdapterService {
param.put("mark", requestParameter.getMark());
param.put("threadPoolKey", requestParameter.getThreadPoolKey());
try {
String resultStr = httpClientUtil.get(url, param);
String resultStr = HttpUtils.get(url, param);
if (StringUtil.isNotBlank(resultStr)) {
Result<ThreadPoolAdapterRespDTO> restResult = JSONUtil.parseObject(resultStr, new TypeReference<Result<ThreadPoolAdapterRespDTO>>() {
});

@ -18,8 +18,8 @@
package cn.hippo4j.config.service.biz.impl;
import cn.hippo4j.common.constant.ConfigModifyTypeConstants;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.config.model.biz.threadpool.ConfigModifyVerifyReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -31,8 +31,6 @@ import org.springframework.stereotype.Service;
@Service
public class AdapterThreadPoolConfigModificationVerifyServiceImpl extends AbstractConfigModificationVerifyService {
private final HttpClientUtil httpClientUtil = HttpClientUtil.build();
@Override
public Integer type() {
return ConfigModifyTypeConstants.ADAPTER_THREAD_POOL;
@ -42,7 +40,7 @@ public class AdapterThreadPoolConfigModificationVerifyServiceImpl extends Abstra
protected void updateThreadPoolParameter(ConfigModifyVerifyReqDTO reqDTO) {
for (String each : getClientAddress(reqDTO)) {
String urlString = StringUtil.newBuilder("http://", each, "/adapter/thread-pool/update");
httpClientUtil.restApiPost(urlString, reqDTO, Object.class);
HttpUtils.post(urlString, reqDTO);
}
}
}

@ -18,8 +18,8 @@
package cn.hippo4j.config.service.biz.impl;
import cn.hippo4j.common.constant.ConfigModifyTypeConstants;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.config.model.biz.threadpool.ConfigModifyVerifyReqDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -31,8 +31,6 @@ import org.springframework.stereotype.Service;
@Service
public class WebThreadPoolConfigModificationVerifyServiceImpl extends AbstractConfigModificationVerifyService {
private final HttpClientUtil httpClientUtil = HttpClientUtil.build();
@Override
public Integer type() {
return ConfigModifyTypeConstants.WEB_THREAD_POOL;
@ -42,7 +40,7 @@ public class WebThreadPoolConfigModificationVerifyServiceImpl extends AbstractCo
protected void updateThreadPoolParameter(ConfigModifyVerifyReqDTO reqDTO) {
for (String each : getClientAddress(reqDTO)) {
String urlString = StringUtil.newBuilder("http://", each, "/web/update/pool");
httpClientUtil.restApiPost(urlString, reqDTO, Object.class);
HttpUtils.post(urlString, reqDTO);
}
}
}

@ -19,6 +19,7 @@ package cn.hippo4j.console.controller;
import cn.hippo4j.common.constant.ConfigModifyTypeConstants;
import cn.hippo4j.common.toolkit.*;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.common.web.base.Result;
import cn.hippo4j.common.web.base.Results;
import cn.hippo4j.config.model.biz.adapter.ThreadPoolAdapterReqDTO;
@ -48,8 +49,6 @@ public class ThreadPoolAdapterController {
private final ConfigModificationVerifyServiceChoose configModificationVerifyServiceChoose;
private HttpClientUtil httpClientUtil = HttpClientUtil.build();
@GetMapping(REGISTER_ADAPTER_BASE_PATH + "/query")
public Result<List<ThreadPoolAdapterRespDTO>> queryAdapterThreadPool(ThreadPoolAdapterReqDTO requestParameter) {
List<ThreadPoolAdapterRespDTO> result = threadPoolAdapterService.query(requestParameter);
@ -67,7 +66,7 @@ public class ThreadPoolAdapterController {
if (UserContext.getUserRole().equals("ROLE_ADMIN")) {
for (String each : requestParameter.getClientAddressList()) {
String urlString = StringUtil.newBuilder("http://", each, "/adapter/thread-pool/update");
httpClientUtil.restApiPost(urlString, requestParameter, Object.class);
HttpUtils.post(urlString, requestParameter);
}
} else {
ConfigModifySaveReqDTO modifySaveReqDTO = BeanUtil.convert(requestParameter, ConfigModifySaveReqDTO.class);

@ -21,6 +21,7 @@ import cn.hippo4j.common.constant.ConfigModifyTypeConstants;
import cn.hippo4j.common.constant.Constants;
import cn.hippo4j.common.model.InstanceInfo;
import cn.hippo4j.common.toolkit.*;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.common.web.base.Result;
import cn.hippo4j.common.web.base.Results;
import cn.hippo4j.common.web.exception.ErrorCodeEnum;
@ -60,8 +61,6 @@ public class ThreadPoolController {
private final ConfigModificationVerifyServiceChoose configModificationVerifyServiceChoose;
private HttpClientUtil httpClientUtil = HttpClientUtil.build();
private static final String HTTP = "http://";
@PostMapping("/query/page")
@ -110,14 +109,14 @@ public class ThreadPoolController {
public Result runState(@PathVariable("tpId") String tpId,
@RequestParam(value = "clientAddress") String clientAddress) {
String urlString = StringUtil.newBuilder(HTTP, clientAddress, "/run/state/", tpId);
return httpClientUtil.restApiGet(urlString, Result.class);
return HttpUtils.get(urlString, Result.class);
}
@GetMapping("/run/thread/state/{tpId}")
public Result runThreadState(@PathVariable("tpId") String tpId,
@RequestParam(value = "clientAddress") String clientAddress) {
String urlString = StringUtil.newBuilder(HTTP, clientAddress, "/run/thread/state/", tpId);
return httpClientUtil.restApiGet(urlString, Result.class);
return HttpUtils.get(urlString, Result.class);
}
@GetMapping("/list/client/instance/{itemId}")
@ -153,13 +152,13 @@ public class ThreadPoolController {
@GetMapping("/web/base/info")
public Result getPoolBaseState(@RequestParam(value = "clientAddress") String clientAddress) {
String urlString = StringUtil.newBuilder(HTTP, clientAddress, "/web/base/info");
return httpClientUtil.restApiGet(urlString, Result.class);
return HttpUtils.get(urlString, Result.class);
}
@GetMapping("/web/run/state")
public Result getPoolRunState(@RequestParam(value = "clientAddress") String clientAddress) {
String urlString = StringUtil.newBuilder(HTTP, clientAddress, "/web/run/state");
return httpClientUtil.restApiGet(urlString, Result.class);
return HttpUtils.get(urlString, Result.class);
}
@PostMapping("/web/update/pool")
@ -167,7 +166,7 @@ public class ThreadPoolController {
if (UserContext.getUserRole().equals("ROLE_ADMIN")) {
for (String each : requestParam.getClientAddressList()) {
String urlString = StringUtil.newBuilder(HTTP, each, "/web/update/pool");
httpClientUtil.restApiPost(urlString, requestParam, Object.class);
HttpUtils.post(urlString, requestParam);
}
} else {
ConfigModifySaveReqDTO modifySaveReqDTO = BeanUtil.convert(requestParam, ConfigModifySaveReqDTO.class);

@ -17,8 +17,8 @@
package cn.hippo4j.message.platform;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import cn.hippo4j.common.toolkit.Singleton;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.message.dto.NotifyConfigDTO;
import cn.hippo4j.message.enums.NotifyPlatformEnum;
import cn.hippo4j.message.enums.NotifyTypeEnum;
@ -46,8 +46,6 @@ import static cn.hippo4j.message.platform.constant.LarkAlarmConstants.*;
@RequiredArgsConstructor
public class LarkSendMessageHandler implements SendMessageHandler<AlarmNotifyRequest, ChangeParameterNotifyRequest> {
private HttpClientUtil httpClientUtil = HttpClientUtil.build();
@Override
public String getType() {
return NotifyPlatformEnum.LARK.name();
@ -174,7 +172,7 @@ public class LarkSendMessageHandler implements SendMessageHandler<AlarmNotifyReq
private void execute(String secretKey, String text) {
String serverUrl = LARK_BOT_URL + secretKey;
try {
httpClientUtil.restApiPost(serverUrl, text, Object.class);
HttpUtils.postJson(serverUrl, text);
} catch (Exception ex) {
log.error("Lark failed to send message", ex);
}

@ -18,8 +18,8 @@
package cn.hippo4j.message.platform;
import cn.hippo4j.common.toolkit.FileUtil;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import cn.hippo4j.common.toolkit.Singleton;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.message.enums.NotifyPlatformEnum;
import cn.hippo4j.message.platform.base.AbstractRobotSendMessageHandler;
import cn.hippo4j.message.platform.base.RobotMessageActualContent;
@ -36,8 +36,6 @@ import static cn.hippo4j.message.platform.constant.WeChatAlarmConstants.*;
@Slf4j
public class WeChatSendMessageHandler extends AbstractRobotSendMessageHandler {
private final HttpClientUtil httpClientUtil = HttpClientUtil.build();
@Override
public String getType() {
return NotifyPlatformEnum.WECHAT.name();
@ -66,7 +64,8 @@ public class WeChatSendMessageHandler extends AbstractRobotSendMessageHandler {
Markdown markdown = new Markdown();
markdown.setContent(robotMessageExecuteDTO.getText());
weChatReq.setMarkdown(markdown);
httpClientUtil.restApiPost(serverUrl, weChatReq, Object.class);
HttpUtils.post(serverUrl, weChatReq);
} catch (Exception ex) {
log.error("WeChat failed to send message", ex);
}

@ -17,7 +17,6 @@
package cn.hippo4j.springboot.starter.config;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import cn.hippo4j.springboot.starter.remote.HttpAgent;
import cn.hippo4j.springboot.starter.remote.ServerHttpAgent;
import org.springframework.context.annotation.Bean;
@ -27,14 +26,9 @@ import org.springframework.context.annotation.Bean;
*/
public class HttpClientConfiguration {
@Bean
public HttpClientUtil hippo4JHttpClientUtil() {
return HttpClientUtil.build();
}
@Bean
@SuppressWarnings("all")
public HttpAgent httpAgent(BootstrapProperties properties, HttpClientUtil hippo4JHttpClientUtil) {
return new ServerHttpAgent(properties, hippo4JHttpClientUtil);
public HttpAgent httpAgent(BootstrapProperties properties) {
return new ServerHttpAgent(properties);
}
}

@ -20,11 +20,11 @@ package cn.hippo4j.springboot.starter.remote;
import cn.hippo4j.common.config.ApplicationContextHolder;
import cn.hippo4j.common.constant.Constants;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.common.web.base.Result;
import cn.hippo4j.common.design.builder.ThreadFactoryBuilder;
import cn.hippo4j.springboot.starter.config.BootstrapProperties;
import cn.hippo4j.springboot.starter.security.SecurityProxy;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import java.util.HashMap;
import java.util.Map;
@ -41,8 +41,6 @@ public class ServerHttpAgent implements HttpAgent {
private final ServerListManager serverListManager;
private final HttpClientUtil httpClientUtil;
private SecurityProxy securityProxy;
private ServerHealthCheck serverHealthCheck;
@ -51,11 +49,10 @@ public class ServerHttpAgent implements HttpAgent {
private final long securityInfoRefreshIntervalMills = TimeUnit.SECONDS.toMillis(5);
public ServerHttpAgent(BootstrapProperties properties, HttpClientUtil httpClientUtil) {
public ServerHttpAgent(BootstrapProperties properties) {
this.dynamicThreadPoolProperties = properties;
this.httpClientUtil = httpClientUtil;
this.serverListManager = new ServerListManager(dynamicThreadPoolProperties);
this.securityProxy = new SecurityProxy(httpClientUtil, properties);
this.securityProxy = new SecurityProxy(properties);
this.securityProxy.applyToken(this.serverListManager.getServerUrls());
this.executorService = new ScheduledThreadPoolExecutor(
new Integer(1),
@ -85,35 +82,35 @@ public class ServerHttpAgent implements HttpAgent {
@Override
public Result httpGetSimple(String path) {
path = injectSecurityInfoByPath(path);
return httpClientUtil.restApiGetHealth(buildUrl(path), Result.class);
return HttpUtils.get(buildUrl(path), Result.class);
}
@Override
public Result httpPost(String path, Object body) {
isHealthStatus();
path = injectSecurityInfoByPath(path);
return httpClientUtil.restApiPost(buildUrl(path), body, Result.class);
return HttpUtils.post(buildUrl(path), body, Result.class);
}
@Override
public Result httpPostByDiscovery(String path, Object body) {
isHealthStatus();
path = injectSecurityInfoByPath(path);
return httpClientUtil.restApiPost(buildUrl(path), body, Result.class);
return HttpUtils.post(buildUrl(path), body, Result.class);
}
@Override
public Result httpGetByConfig(String path, Map<String, String> headers, Map<String, String> paramValues, long readTimeoutMs) {
isHealthStatus();
injectSecurityInfo(paramValues);
return httpClientUtil.restApiGetByThreadPool(buildUrl(path), headers, paramValues, readTimeoutMs, Result.class);
return HttpUtils.get(buildUrl(path), headers, paramValues, readTimeoutMs, Result.class);
}
@Override
public Result httpPostByConfig(String path, Map<String, String> headers, Map<String, String> paramValues, long readTimeoutMs) {
isHealthStatus();
injectSecurityInfo(paramValues);
return httpClientUtil.restApiPostByThreadPool(buildUrl(path), headers, paramValues, readTimeoutMs, Result.class);
return HttpUtils.post(buildUrl(path), headers, paramValues, readTimeoutMs, Result.class);
}
@Override
@ -141,7 +138,7 @@ public class ServerHttpAgent implements HttpAgent {
@Deprecated
private String injectSecurityInfoByPath(String path) {
String resultPath = httpClientUtil.buildUrl(path, injectSecurityInfo(new HashMap<>()));
String resultPath = HttpUtils.buildUrl(path, injectSecurityInfo(new HashMap<>()));
return resultPath;
}
}

@ -21,9 +21,9 @@ import cn.hippo4j.common.constant.Constants;
import cn.hippo4j.common.model.TokenInfo;
import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.http.HttpUtils;
import cn.hippo4j.common.web.base.Result;
import cn.hippo4j.springboot.starter.config.BootstrapProperties;
import cn.hippo4j.common.toolkit.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
@ -39,8 +39,6 @@ public class SecurityProxy {
private static final String APPLY_TOKEN_URL = Constants.BASE_PATH + "/auth/users/apply/token";
private final HttpClientUtil httpClientUtil;
private final String username;
private final String password;
@ -53,10 +51,9 @@ public class SecurityProxy {
private long tokenRefreshWindow;
public SecurityProxy(HttpClientUtil httpClientUtil, BootstrapProperties properties) {
public SecurityProxy(BootstrapProperties properties) {
username = properties.getUsername();
password = properties.getPassword();
this.httpClientUtil = httpClientUtil;
}
public boolean applyToken(List<String> servers) {
@ -82,7 +79,7 @@ public class SecurityProxy {
bodyMap.put("userName", username);
bodyMap.put("password", password);
try {
Result<String> result = httpClientUtil.restApiPost(url, bodyMap, Result.class);
Result result = HttpUtils.post(url, bodyMap, Result.class);
if (!result.isSuccess()) {
log.error("Error getting access token. message: {}", result.getMessage());
return false;

@ -56,7 +56,6 @@
<!-- Tool -->
<dozer.version>6.5.0</dozer.version>
<caffeine.version>2.9.3</caffeine.version>
<okhttp3.version>3.12.0</okhttp3.version>
<hibernate-validator.version>6.1.5.Final</hibernate-validator.version>
<transmittable-thread-local.version>2.12.1</transmittable-thread-local.version>
<jjwt.version>0.9.0</jjwt.version>
@ -111,11 +110,6 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-message</artifactId>

Loading…
Cancel
Save