Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	hippo4j-config/src/test/java/cn/hippo4j/config/toolkit/Md5ConfigUtilTest.java
pull/804/head
guoz 3 years ago
commit 1916185f26

@ -64,4 +64,4 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Build with Maven - name: Build with Maven
run: echo y | mvn clean install -Dskip.gpg=true -Dspotless.apply.skip=true run: echo y | mvn clean install -Dskip.gpg=true -Dspotless.apply.skip=true -Dmaven.javadoc.skip=true

@ -33,8 +33,8 @@ jobs:
with: with:
image_size: 50 image_size: 50
columns_per_row: 9 columns_per_row: 9
committer_email: m7798432@163.com committer_email: wechat202110@163.com
committer_username: pirme committer_username: hippo4jbot[bot]
commit_message: 'Update the list of contributors' commit_message: 'Update the list of contributors'
env: env:
GITHUB_TOKEN: ${{ secrets.ACTION_TOKEN }} GITHUB_TOKEN: ${{ secrets.ACTION_TOKEN }}

@ -28,7 +28,7 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
提供以下功能支持: 提供以下功能支持:
- 全局管控 - 管理应用线程池实例 - 全局管控 - 管理应用线程池实例
- 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。 - 动态变更 - 应用运行时动态变更线程池参数,包括不限于:核心、最大线程数、阻塞队列容量、拒绝策略等。
- 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。 - 通知报警 - 内置四种报警通知策略,线程池活跃度、容量水位、拒绝策略以及任务执行时间超长。
@ -36,7 +36,7 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
- 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。 - 功能扩展 - 支持线程池任务传递上下文;项目关闭时,支持等待线程池在指定时间内完成任务。
- 多种模式 - 内置两种使用模式:[依赖配置中心](https://hippo4j.cn/docs/user_docs/getting_started/config/hippo4j-config-start) 和 [无中间件依赖](https://hippo4j.cn/docs/user_docs/getting_started/server/hippo4j-server-start)。 - 多种模式 - 内置两种使用模式:[依赖配置中心](https://hippo4j.cn/docs/user_docs/getting_started/config/hippo4j-config-start) 和 [无中间件依赖](https://hippo4j.cn/docs/user_docs/getting_started/server/hippo4j-server-start)。
- 容器管理 - Tomcat、Jetty、Undertow 容器线程池运行时查看和线程数变更。 - 容器管理 - Tomcat、Jetty、Undertow 容器线程池运行时查看和线程数变更。
- 中间件适配 - Dubbo、Hystrix、RocketMQ、RabbitMQ 等消费线程池运行时数据查看和线程数变更。 - 中间件适配 - Dubbo、Hystrix、Polaris、RabbitMQ、RocketMQ 等消费线程池运行时数据查看和线程数变更。
## 快速开始 ## 快速开始
@ -93,6 +93,20 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>李金来</b></sub> <sub><b>李金来</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/shanjianq">
<img src="https://avatars.githubusercontent.com/u/49084314?v=4" width="50;" alt="shanjianq"/>
<br />
<sub><b>Shanjianq</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hippo4jbot">
<img src="https://avatars.githubusercontent.com/u/93201205?v=4" width="50;" alt="hippo4jbot"/>
<br />
<sub><b>Hippo4jbot[bot]</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/iwangjie"> <a href="https://github.com/iwangjie">
<img src="https://avatars.githubusercontent.com/u/23075587?v=4" width="50;" alt="iwangjie"/> <img src="https://avatars.githubusercontent.com/u/23075587?v=4" width="50;" alt="iwangjie"/>
@ -108,32 +122,32 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
</a> </a>
</td> </td>
<td align="center"> <td align="center">
<a href="https://github.com/shanjianq"> <a href="https://github.com/pizihao">
<img src="https://avatars.githubusercontent.com/u/49084314?v=4" width="50;" alt="shanjianq"/> <img src="https://avatars.githubusercontent.com/u/48643103?v=4" width="50;" alt="pizihao"/>
<br /> <br />
<sub><b>Shanjianq</b></sub> <sub><b>Pizihao</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/xqxyxchy"> <a href="https://github.com/Gdk666">
<img src="https://avatars.githubusercontent.com/u/21134578?v=4" width="50;" alt="xqxyxchy"/> <img src="https://avatars.githubusercontent.com/u/22442067?v=4" width="50;" alt="Gdk666"/>
<br /> <br />
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td> </td>
<td align="center"> <td align="center">
<a href="https://github.com/Gdk666"> <a href="https://github.com/xqxyxchy">
<img src="https://avatars.githubusercontent.com/u/22442067?v=4" width="50;" alt="Gdk666"/> <img src="https://avatars.githubusercontent.com/u/21134578?v=4" width="50;" alt="xqxyxchy"/>
<br /> <br />
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/pizihao"> <a href="https://github.com/road2master">
<img src="https://avatars.githubusercontent.com/u/48643103?v=4" width="50;" alt="pizihao"/> <img src="https://avatars.githubusercontent.com/u/53806703?v=4" width="50;" alt="road2master"/>
<br /> <br />
<sub><b>Pizihao</b></sub> <sub><b>Lijx</b></sub>
</a> </a>
</td> </td>
<td align="center"> <td align="center">
@ -143,6 +157,13 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/baymax55">
<img src="https://avatars.githubusercontent.com/u/35788491?v=4" width="50;" alt="baymax55"/>
<br />
<sub><b>Baymax55</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/zhuanghaozhe"> <a href="https://github.com/zhuanghaozhe">
<img src="https://avatars.githubusercontent.com/u/73152769?v=4" width="50;" alt="zhuanghaozhe"/> <img src="https://avatars.githubusercontent.com/u/73152769?v=4" width="50;" alt="zhuanghaozhe"/>
@ -157,13 +178,6 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/road2master">
<img src="https://avatars.githubusercontent.com/u/53806703?v=4" width="50;" alt="road2master"/>
<br />
<sub><b>Lijx</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/Atmanuclear"> <a href="https://github.com/Atmanuclear">
<img src="https://avatars.githubusercontent.com/u/25747005?v=4" width="50;" alt="Atmanuclear"/> <img src="https://avatars.githubusercontent.com/u/25747005?v=4" width="50;" alt="Atmanuclear"/>
@ -177,7 +191,8 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Hippo4j</b></sub> <sub><b>Hippo4j</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/imyzt"> <a href="https://github.com/imyzt">
<img src="https://avatars.githubusercontent.com/u/28680198?v=4" width="50;" alt="imyzt"/> <img src="https://avatars.githubusercontent.com/u/28680198?v=4" width="50;" alt="imyzt"/>
@ -191,8 +206,7 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Liutao</b></sub> <sub><b>Liutao</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/monsterxxp"> <a href="https://github.com/monsterxxp">
<img src="https://avatars.githubusercontent.com/u/37952446?v=4" width="50;" alt="monsterxxp"/> <img src="https://avatars.githubusercontent.com/u/37952446?v=4" width="50;" alt="monsterxxp"/>
@ -207,6 +221,13 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/wulangcode">
<img src="https://avatars.githubusercontent.com/u/48200100?v=4" width="50;" alt="wulangcode"/>
<br />
<sub><b>WuLang</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/gywanghai"> <a href="https://github.com/gywanghai">
<img src="https://avatars.githubusercontent.com/u/102774648?v=4" width="50;" alt="gywanghai"/> <img src="https://avatars.githubusercontent.com/u/102774648?v=4" width="50;" alt="gywanghai"/>
@ -234,7 +255,8 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Sean Wu</b></sub> <sub><b>Sean Wu</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/HKMV"> <a href="https://github.com/HKMV">
<img src="https://avatars.githubusercontent.com/u/26456469?v=4" width="50;" alt="HKMV"/> <img src="https://avatars.githubusercontent.com/u/26456469?v=4" width="50;" alt="HKMV"/>
@ -242,21 +264,13 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>Serenity</b></sub> <sub><b>Serenity</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/baymax55">
<img src="https://avatars.githubusercontent.com/u/35788491?v=4" width="50;" alt="baymax55"/>
<br />
<sub><b>Baymax55</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/gewuwo"> <a href="https://github.com/gewuwo">
<img src="https://avatars.githubusercontent.com/u/97213587?v=4" width="50;" alt="gewuwo"/> <img src="https://avatars.githubusercontent.com/u/97213587?v=4" width="50;" alt="gewuwo"/>
<br /> <br />
<sub><b>格悟沃</b></sub> <sub><b>格悟沃</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/hushtian"> <a href="https://github.com/hushtian">
<img src="https://avatars.githubusercontent.com/u/55479601?v=4" width="50;" alt="hushtian"/> <img src="https://avatars.githubusercontent.com/u/55479601?v=4" width="50;" alt="hushtian"/>
@ -305,7 +319,8 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>游祖光</b></sub> <sub><b>游祖光</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/puppet4"> <a href="https://github.com/puppet4">
<img src="https://avatars.githubusercontent.com/u/28887178?v=4" width="50;" alt="puppet4"/> <img src="https://avatars.githubusercontent.com/u/28887178?v=4" width="50;" alt="puppet4"/>
@ -313,14 +328,20 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>Tudo</b></sub> <sub><b>Tudo</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/yanrongzhen">
<img src="https://avatars.githubusercontent.com/u/106363931?v=4" width="50;" alt="yanrongzhen"/>
<br />
<sub><b>严荣振</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/2EXP"> <a href="https://github.com/2EXP">
<img src="https://avatars.githubusercontent.com/u/26007713?v=4" width="50;" alt="2EXP"/> <img src="https://avatars.githubusercontent.com/u/26007713?v=4" width="50;" alt="2EXP"/>
<br /> <br />
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/onesimplecoder"> <a href="https://github.com/onesimplecoder">
<img src="https://avatars.githubusercontent.com/u/30288465?v=4" width="50;" alt="onesimplecoder"/> <img src="https://avatars.githubusercontent.com/u/30288465?v=4" width="50;" alt="onesimplecoder"/>
@ -362,7 +383,8 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Douspeng</b></sub> <sub><b>Douspeng</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/hl1248"> <a href="https://github.com/hl1248">
<img src="https://avatars.githubusercontent.com/u/70790953?v=4" width="50;" alt="hl1248"/> <img src="https://avatars.githubusercontent.com/u/70790953?v=4" width="50;" alt="hl1248"/>
@ -377,14 +399,6 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>Lynn</b></sub> <sub><b>Lynn</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/sanliangitch">
<img src="https://avatars.githubusercontent.com/u/48200100?v=4" width="50;" alt="sanliangitch"/>
<br />
<sub><b>WuLang</b></sub>
</a>
</td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/alexhaoxuan"> <a href="https://github.com/alexhaoxuan">
<img src="https://avatars.githubusercontent.com/u/46749051?v=4" width="50;" alt="alexhaoxuan"/> <img src="https://avatars.githubusercontent.com/u/46749051?v=4" width="50;" alt="alexhaoxuan"/>
@ -433,7 +447,8 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Guide</b></sub> <sub><b>Guide</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/hbw1994"> <a href="https://github.com/hbw1994">
<img src="https://avatars.githubusercontent.com/u/22744421?v=4" width="50;" alt="hbw1994"/> <img src="https://avatars.githubusercontent.com/u/22744421?v=4" width="50;" alt="hbw1994"/>
@ -447,8 +462,7 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/stronglong"> <a href="https://github.com/stronglong">
<img src="https://avatars.githubusercontent.com/u/15846157?v=4" width="50;" alt="stronglong"/> <img src="https://avatars.githubusercontent.com/u/15846157?v=4" width="50;" alt="stronglong"/>
@ -463,13 +477,6 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td> </td>
<td align="center">
<a href="https://github.com/jialei-jack">
<img src="https://avatars.githubusercontent.com/u/93201205?v=4" width="50;" alt="jialei-jack"/>
<br />
<sub><b>Jialei-jack</b></sub>
</a>
</td>
<td align="center"> <td align="center">
<a href="https://github.com/klsq94"> <a href="https://github.com/klsq94">
<img src="https://avatars.githubusercontent.com/u/16208392?v=4" width="50;" alt="klsq94"/> <img src="https://avatars.githubusercontent.com/u/16208392?v=4" width="50;" alt="klsq94"/>
@ -504,15 +511,15 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Op-lht</b></sub> <sub><b>Op-lht</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/wangjie-github"> <a href="https://github.com/wangjie-github">
<img src="https://avatars.githubusercontent.com/u/35762878?v=4" width="50;" alt="wangjie-github"/> <img src="https://avatars.githubusercontent.com/u/35762878?v=4" width="50;" alt="wangjie-github"/>
<br /> <br />
<sub><b>Wangjie</b></sub> <sub><b>Wangjie</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/wangyi123456"> <a href="https://github.com/wangyi123456">
<img src="https://avatars.githubusercontent.com/u/25248959?v=4" width="50;" alt="wangyi123456"/> <img src="https://avatars.githubusercontent.com/u/25248959?v=4" width="50;" alt="wangyi123456"/>
@ -568,15 +575,15 @@ Hippo-4J 通过对 JDK 线程池增强,以及扩展三方框架底层线程池
<br /> <br />
<sub><b>Zhaojinchao</b></sub> <sub><b>Zhaojinchao</b></sub>
</a> </a>
</td> </td></tr>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/zj1997"> <a href="https://github.com/zj1997">
<img src="https://avatars.githubusercontent.com/u/31212787?v=4" width="50;" alt="zj1997"/> <img src="https://avatars.githubusercontent.com/u/31212787?v=4" width="50;" alt="zj1997"/>
<br /> <br />
<sub><b>Null</b></sub> <sub><b>Null</b></sub>
</a> </a>
</td></tr> </td>
<tr>
<td align="center"> <td align="center">
<a href="https://github.com/li-xiao-shuang"> <a href="https://github.com/li-xiao-shuang">
<img src="https://avatars.githubusercontent.com/u/34903552?v=4" width="50;" alt="li-xiao-shuang"/> <img src="https://avatars.githubusercontent.com/u/34903552?v=4" width="50;" alt="li-xiao-shuang"/>

@ -0,0 +1,130 @@
<?xml version="1.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.
-->
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="NewlineAtEndOfFile"/>
<module name="RegexpSingleline">
<property name="format" value="printStackTrace"/>
<property name="message" value="Prohibit invoking printStackTrace in source code !"/>
</module>
<module name="TreeWalker">
<module name="AvoidStarImport">
<property name="excludes" value="java.io,java.net,java.lang.Math"/>
<property name="allowClassImports" value="false"/>
<property name="allowStaticMemberImports" value="true"/>
</module>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<module name="JavadocType">
<property name="allowUnknownTags" value="true"/>
<property name="allowMissingParamTags" value="true"/>
<message key="javadoc.missing" value="Class Comments: Missing Javadoc Comments"/>
</module>
<!-- Do not scan method annotations for now -->
<!--<module name="JavadocMethod">
<property name="tokens" value="METHOD_DEF"/>
<property name="allowMissingPropertyJavadoc" value="true"/>
<message key="javadoc.missing" value="Method Comments: Missing Javadoc Comments"/>
</module>-->
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" />
</module>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="ParameterName "/>
<module name="ConstantName"/>
<module name="ArrayTypeStyle"/>
<module name="UpperEll"/>
<module name="LineLength">
<property name="max" value="200"/>
</module>
<module name="MethodLength">
<property name="tokens" value="METHOD_DEF"/>
<property name="max" value="150"/>
</module>
<module name="ParameterNumber">
<property name="max" value="5"/>
<property name="ignoreOverriddenMethods" value="true"/>
<property name="tokens" value="METHOD_DEF"/>
</module>
<module name="MethodParamPad"/>
<module name="TypecastParenPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<module name="MagicNumber">
<property name="ignoreNumbers" value="0, 1, 2"/>
<property name="ignoreAnnotation" value="true"/>
<property name="ignoreHashCodeMethod" value="true"/>
<property name="ignoreFieldDeclaration" value="true"/>
</module>
<module name="MissingSwitchDefault"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="FinalClass"/>
<module name="InterfaceIsType"/>
<module name="VisibilityModifier">
<property name="packageAllowed" value="true"/>
<property name="protectedAllowed" value="true"/>
</module>
<module name="StringLiteralEquality"/>
<module name="NestedForDepth">
<property name="max" value="3"/>
</module>
<module name="NestedIfDepth">
<property name="max" value="4"/>
</module>
<module name="UncommentedMain">
<property name="excludedClasses" value=".*Application$"/>
</module>
<module name="Regexp">
<property name="format" value="System\.out\.println"/>
<property name="illegalPattern" value="true"/>
</module>
<module name="ReturnCount">
<property name="max" value="4"/>
</module>
<module name="NestedTryDepth ">
<property name="max" value="4"/>
</module>
<module name="SuperFinalize"/>
<module name="SuperClone"/>
</module>
</module>

@ -0,0 +1,28 @@
<?xml version="1.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.
-->
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress checks="MagicNumber" files="ResizableCapacityLinkedBlockingQueue.java"/>
<suppress checks="InnerAssignment" files="ResizableCapacityLinkedBlockingQueue.java"/>
<suppress checks="StaticVariableName" files="ApplicationContextHolder.java"/>
<suppress checks="StaticVariableName" files="JacksonHandler.java"/>
<suppress checks="MagicNumber" files="ByteConvertUtil.java"/>
</suppressions>

@ -18,6 +18,7 @@
* limitations under the License. * limitations under the License.
*/ */
--> -->
<profiles version="13"> <profiles version="13">
<profile kind="CodeFormatterProfile" name="'Hippo4j Current'" version="13"> <profile kind="CodeFormatterProfile" name="'Hippo4j Current'" version="13">
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/> <setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>

@ -13,9 +13,9 @@ sidebar_position: 2
<td align="center" width="26%">联系方式</td> <td align="center" width="26%">联系方式</td>
</tr> </tr>
<tr> <tr>
<td align="center" ><a href="https://github.com/agentart"><img src="https://avatars.githubusercontent.com/u/77398366?v=4?s=64" width="64px;"/></a></td> <td align="center" ><a href="https://github.com/itmachen"><img src="https://avatars.githubusercontent.com/u/77398366?v=4?s=64" width="64px;"/></a></td>
<td align="center" >马称</td> <td align="center" >马称</td>
<td align="center" ><a href="https://github.com/agentart">agentart</a></td> <td align="center" ><a href="https://github.com/itmachen">itmachen</a></td>
<td align="center" ><a href="http://www.xiaomage.info/">小马哥的技术专栏</a></td> <td align="center" ><a href="http://www.xiaomage.info/">小马哥的技术专栏</a></td>
<td align="center" >machen@apache.org</td> <td align="center" >machen@apache.org</td>
</tr> </tr>
@ -54,4 +54,11 @@ sidebar_position: 2
<td align="center" ><a href="https://www.yuque.com/chenghu-08dla/pizig1">pizihao</a></td> <td align="center" ><a href="https://www.yuque.com/chenghu-08dla/pizig1">pizihao</a></td>
<td align="center" >hao3073liu@163.com</td> <td align="center" >hao3073liu@163.com</td>
</tr> </tr>
<tr>
<td align="center"><a href="https://github.com/pizihao"><img src="https://avatars.githubusercontent.com/u/49084314?v=4?s=64" width="64px;"/></a></td>
<td align="center">叶炜</td>
<td align="center" ><a href="https://github.com/shanjianq">shanjianq</a></td>
<td align="center" >-</td>
<td align="center" >17855368071@163.com</td>
</tr>
</table> </table>

@ -1,7 +0,0 @@
---
sidebar_position: 2
---
# 阻塞队列自定义
同拒绝策略自定义。

@ -0,0 +1,45 @@
---
sidebar_position: 4
---
# 参数默认配置
曾有多名小伙伴反馈说,项目中线程池一多,配置文件中配置就显得很臃肿。为此 hippo4j-config 开发出了动态线程池默认配置。
```yaml
spring:
dynamic:
thread-pool:
default-executor:
core-pool-size: 4
maximum-pool-size: 6
blocking-queue: ResizableCapacityLinkedBlockingQueue
queue-capacity: 1024
execute-time-out: 1000
keep-alive-time: 9999
rejected-handler: AbortPolicy
active-alarm: 90
capacity-alarm: 85
alarm: true
allow-core-thread-time-out: true
notify:
interval: 5
receives: chen.ma
executors:
- thread-pool-id: message-produce
- thread-pool-id: message-consume
core-pool-size: 80
maximum-pool-size: 100
execute-time-out: 1000
notify:
interval: 6
receives: chen.ma
```
`spring.dynamic.thread-pool.executors` 层级下,仅需要配置 `thread-pool-id`,其余配置从 `spring.dynamic.thread-pool.default-executor` 读取。
如果 `spring.dynamic.thread-pool.executors` 下配置和 `spring.dynamic.thread-pool.default-executor` 冲突,以前者为主。
通过该自定义配置方式,可减少大量重复线程池参数配置项,提高核心配置简洁度。
提示:`spring.dynamic.thread-pool.default-executor` 层级下参数,不提供动态刷新功能。

@ -1,5 +1,5 @@
--- ---
sidebar_position: 2 sidebar_position: 3
--- ---
# 线程池监控 # 线程池监控

@ -0,0 +1,80 @@
---
sidebar_position: 3
---
# 个性化配置
以下所述特性自 hippo4j-config v1.4.2 及以上版本提供,由 hippo4j 核心开发者 [@pizihao](https://github.com/pizihao) 完成相应功能开发。
## 需求背景
**1容器及三方框架线程池自定义启用**
最初设计容器线程池和三方框架线程池的动态变更是和启动无关的。也就是说,启动时不会根据配置文件中相关参数去修改两者对应的线程池配置。
这么设计的初衷是因为,不想让 hippo4j 过多的去介入框架原有的功能。因为容器和三方框架都支持线程池参数的自定义。
也就造成,可能你在配置中心配置了对应的容器和三方框架线程池参数,启动时是无效的。但当修改配置文件任一配置,容器和三方框架线程池配置将生效。
为了更好的用户体验,决定加入启用标识来控制:是否在项目初始化启动时,对容器和三方框架线程池参数进行修改。
**2客户端集群个性化配置**
大家都知道hippo4j-config 是依赖配置中心做线程池配置动态变更。这种模式有一种缺点:改动配置文件后,所有客户端都会变更。
有些小伙伴希望 hippo4j-config 能够像 hippo4j-server 一样,能够针对单独的客户端进行配置变更。
## 容器及三方框架线程池自定义启用
容器及三方框架线程池添加启用配置,为了保持统一,动态线程池配置中也有该参数配置。配置项默认开启。
```yaml
spring:
dynamic:
thread-pool:
tomcat:
enable: true
executors:
- thread-pool-id: message-consume
enable: false
adapter-executors:
- threadPoolKey: 'input'
enable: true
```
## 客户端集群个性化配置
分别在动态线程池、容器线程池以及三方框架线程池配置下增加 `nodes` 配置节点,通过该配置可匹配需要变更的节点。
```yaml
spring:
dynamic:
thread-pool:
tomcat:
nodes: 192.168.1.5:*,192.168.1.6:8080
executors:
- thread-pool-id: message-consume
nodes: 192.168.1.5:*
adapter-executors:
- threadPoolKey: 'input'
nodes: 192.168.1.5:*
```
来一段代码方法中的注释,大家就基本明白如何使用了。
```java
/**
* Matching nodes<br>
* nodes is ip + port.Get 'nodes' in the new Properties,Compare this with the ip + port of Application.<br>
* support prefix pattern matching. e.g: <br>
* <ul>
* <li>192.168.1.5:* -- Matches all ports of 192.168.1.5</li>
* <li>192.168.1.*:2009 -- Matches 2009 port of 192.168.1.*</li>
* <li>* -- all</li>
* <li>empty -- all</li>
* </ul>
* The format of ip + port is ip : port.
*/
```
`nodes` 可与 `enable` 同时使用。如此,基于配置中心的动态线程池实现方式,将能够更方便的支持个性化需求。

@ -16,7 +16,7 @@ Nacos、Apollo、Zookeeper、ETCD 配置中心任选其一。
</dependency> </dependency>
``` ```
启动类上添加注解 @EnableDynamicThreadPool 启动类上添加注解 `@EnableDynamicThreadPool`
```java ```java
@SpringBootApplication @SpringBootApplication

@ -69,13 +69,11 @@ spring:
package cn.hippo4j.example; package cn.hippo4j.example;
import cn.hippo4j.core.executor.DynamicThreadPool; import cn.hippo4j.core.executor.DynamicThreadPool;
import cn.hippo4j.core.executor.support.ResizableCapacityLinkedBlockIngQueue;
import cn.hippo4j.core.executor.support.ThreadPoolBuilder; import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration @Configuration
public class ThreadPoolConfig { public class ThreadPoolConfig {

@ -4,11 +4,6 @@ sidebar_position: 1
# 加群沟通 # 加群沟通
扫码添加微信,备注:`hippo4j`,邀您加入群聊。
对于这个项目,是否有什么不一样看法,欢迎在 Issue 一起沟通交流;或者添加小编微信进交流群。 ![](https://images-machen.oss-cn-beijing.aliyuncs.com/185774220-c11951f9-e130-4d60-8204-afb5c51d4401.png)
![](https://images-machen.oss-cn-beijing.aliyuncs.com/64E583A0-B1DD-49A3-9AEC-8D246E9D5C12.PNG?x-oss-process=image/resize,h_500,w_800)

@ -8,13 +8,13 @@ sidebar_position: 2
- <a href="#控制台线程池管理和线程池实例的区别">控制台线程池管理和线程池实例的区别</a> - <a href="#控制台线程池管理和线程池实例的区别">控制台线程池管理和线程池实例的区别</a>
- <a href="#示例项目为什么会有跨域请求">示例项目为什么会有跨域请求</a> - <a href="#示例项目为什么会有跨域请求">示例项目为什么会有跨域请求</a>
- <a href="#更新代码后运行时服务端sql报错">更新代码后运行时服务端SQL报错</a> - <a href="#更新代码后运行时服务端sql报错">更新代码后运行时服务端SQL报错</a>
- <a href="#okhttp3-call-timeout-方法不存在">okHttp3 call.timeout() 方法不存在</a>
- <a href="#生产环境如何不启用动态线程池">生产环境如何不启用动态线程池</a> - <a href="#生产环境如何不启用动态线程池">生产环境如何不启用动态线程池</a>
- <a href="#server-端宕机会影响-client-运行么">Server 端宕机会影响 Client 运行么</a> - <a href="#server-端宕机会影响-client-运行么">Server 端宕机会影响 Client 运行么</a>
- <a href="#hippo4j-的发布方式是怎样的-如何选择正确的版本">Hippo4J 的发布方式是怎样的?如何选择正确的版本</a> - <a href="#hippo4j-的发布方式是怎样的-如何选择正确的版本">Hippo4J 的发布方式是怎样的?如何选择正确的版本</a>
- <a href="#群机器人接受不到通知报警">群机器人接受不到通知报警</a> - <a href="#群机器人接受不到通知报警">群机器人接受不到通知报警</a>
- <a href="#设置线程池参数优先级问题">设置线程池参数优先级问题</a> - <a href="#设置线程池参数优先级问题">设置线程池参数优先级问题</a>
- <a href="#线程池实例中修改队列容量参数问题">线程池实例中修改队列容量参数问题</a> - <a href="#线程池实例中修改队列容量参数问题">线程池实例中修改队列容量参数问题</a>
- <a href="#控制台-sockettimeoutexception-connect-timed-out">控制台 SocketTimeoutException: connect timed out</a>
## 租户和项目在 Hippo4J 中是什么意思 ## 租户和项目在 Hippo4J 中是什么意思
@ -47,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暂时不打算上线生产环境。 测试环境已经引入 Hippo4J暂时不打算上线生产环境。
@ -94,3 +82,9 @@ Hippo4J 发布时可能会涉及到两端发布,分别是 Server 和 Starter
## 线程池实例中修改队列容量参数问题 ## 线程池实例中修改队列容量参数问题
在线程池管理中添加时,只有当选择队列类型为 `ResizableCapacityLinkedBlockingQueue` 时,后续再进行修改容量大小时才会实时的刷新修改成功。 在线程池管理中添加时,只有当选择队列类型为 `ResizableCapacityLinkedBlockingQueue` 时,后续再进行修改容量大小时才会实时的刷新修改成功。
## 控制台 SocketTimeoutException: connect timed out
控制台中触发的某些操作涉及到 hippo4j-server 调用客户端项目。如果 hippo4j-server 部署在测试环境,而客户端项目为本地启动,则会触发该问题。
为什么编辑线程池参数不报错?因为线程池的动态变更是客户端主动发起连接,和服务端保持了一个长轮询,所以不存在服务端主动调用客户端行为。

@ -0,0 +1,23 @@
---
sidebar_position: 5
---
# 推荐公众号
## JavaGuide
专注Java后端学习和大厂面试的公众号
![](https://images-machen.oss-cn-beijing.aliyuncs.com/JavaGuide.png)
## HelloGitHub
HelloGitHub专注于开源社区技术和知识内容分享。
![](https://images-machen.oss-cn-beijing.aliyuncs.com/HelloGitHub.png)
## macrozheng
专注Java技术分享解析优质开源项目。涵盖SpringBoot、SpringCloud、Docker、K8S等实用技术作者Github开源项目mall50K+Star
![](https://images-machen.oss-cn-beijing.aliyuncs.com/macrozheng.png)

@ -0,0 +1,23 @@
---
sidebar_position: 6
---
# 公众号合作
## 推荐须知
hippo4j 作为一款新兴动态线程池框架,开源出来的时间比较晚,目前迫切需要不同的途径进行推广。
如果您是公众号运营者或者开源爱好者,欢迎将 hippo4j 推荐给您的粉丝。
1. 您无需为 hippo4j 专门撰写文案,只需要直接导入 [推荐文章](https://mp.weixin.qq.com/s/JTTwcBEiK_MnFcPTZl3zGA) 即可。
2. 在文章底部或内容中留下项目官网或者 GitHub 仓库链接。
3. 文章需至少 1000+ 的阅读量。
作为推荐回报hippo4j 可以为您:
1. 在框架官方文档 [推荐公众号](/docs/user_docs/other/official-ccounts) 页面处留下您的公众号二维码。
2. 在框架官方交流群里@全体成员推广您的公众号一次,附带介绍语。
3. 您的公众号所有新推文章都可以将链接发送到 hippo4j 交流群中,增加阅读量。
如果您还有除公众号以外的其它途径可以与 hippo4j 相互推荐,欢迎 [加群沟通](/docs/user_docs/other/group)。

@ -44,7 +44,7 @@ sidebar_position: 3
> 更聪明问题X.org 6.8.1 的鼠标指针,在某牌显卡 MV1005 芯片组环境下 - 会变形。 > 更聪明问题X.org 6.8.1 的鼠标指针,在某牌显卡 MV1005 芯片组环境下 - 会变形。
### <a name="使用清晰、正确、精准且合乎语法的语句">使用清晰、正确、精准且合乎语法的语句</a> ### 使用清晰、正确、精准且合乎语法的语句
我们从经验中发现,粗心的提问者通常也会粗心地写程序与思考(我敢打包票)。回答粗心大意者的问题很不值得,我们宁愿把时间耗在别处。 我们从经验中发现,粗心的提问者通常也会粗心地写程序与思考(我敢打包票)。回答粗心大意者的问题很不值得,我们宁愿把时间耗在别处。

@ -0,0 +1,265 @@
---
sidebar_position: 4
---
# 更新日志
## 1.4.1 (Sep 12, 2022)
这是一个功能增强版本,修复了若干 BUG。建议按照当前版本升级。具体信息可查看 Release 标签地址:[1.4.1](https://github.com/opengoofy/hippo4j/milestone/11?closed=1)
**Feature**
- 支持 H2 数据库 @weihubeats
- 动态线程池配置变更时,支持单个、多个或全部节点变 @pizihao
- 增加线程池活跃度和容量报警可选择关闭
- @DynamicThreadPool 线程池不存在则创建 @shanjianq
- 支持 ETCD 配置中心动态调整参数 @weihubeats
- 创建动态线程池支持 spring 线程池 @BigXin0109
- 线程池实例变更增加执行超时时间
- 线程池相关查询页面增加阻塞队列属性
- 定义动态线程池时,抽象默认配置
- 提供 ExecutorContext 封装上下文细节 @road2master
- Docker 制作服务端镜像,帮助开发者快速启动 @BigXin0109
- RabbitMQ 适配器增加多个 MQ 数据源 @weihubeats
**Bug**
- 动态线程池设置关闭时启动报错 @dousp
- ExecutorTtlWrapper 类型的 Executor 不生效 @BigXin0109
- Undertow 获取 WebServer 类型参数异常 @shining-stars-lk
- 修复线程池核心、最大线程数校验限制
- ByteConvertUtil#getPrintSize 单位转换错误 @onesimplecoder
- 创建线程池单选框选择错误
- ReflectUtil#getFieldsDirectly missing fields @BigXin0109
- 本地代码中设置的 capacity 无效 @BigXin0109
- 服务端线程池超时时间存在拆箱空指针异常 @oreoft
- 未读取服务端返回执行超时时间属性
- ResizableCapacityLinkedBlockingQueue#put 当前元素数量大于 capacity 未阻塞
**Optimize**
- 长轮询任务判断逻辑优化 @shining-stars-lk
- 线程池存在实例不允许删除线程池 @shanjianq
- 优化租户、项目列表展示排版
- 通知报警模块项目和线程池下拉查询排序修改
- 动态线程池拒绝策略触发,以异步的方式报警
- 优化框架中线程池工厂产生的线程名称 @road2master
## 1.4.0 (Aug 16, 2022)
`hippo4j server` 兼容历史低版本,`hippo4j config` 中部分属性名进行了调整,请参考 [hippo4j config 快速开始](https://hippo4j.cn/docs/user_docs/getting-started/hippo4j-core-start)。
注意事项:
1. 如果是对已运行 hippo4j server 升级,执行 `/conf/sql-upgrade` 目录下对应的升级脚本。
2. 需客户端在 1.4.0 及以上版本才可在 hippo4j server 设置线程执行超时时间属性。
**Feature**
- 添加 Alibaba Dubbo 线程池监控及动态变更
- hippo4j server 支持任务执行超时时间动态修改
- 阿里 TTL 框架线程池适配
- 添加动态线程池自动注册功能
- 订阅回调线程池参数变更
- 动态线程池监控增加 SPI 自定义功能
- hippo4j server 支持多种线程池监控方式,例如 Prometheus
- 通知相关参数添加动态变更功能
**Bug**
- 线程池变更executeTimeOut 变更极端情况下会出现异常
- 用户登录时候,如果输入了不存在的用户名,后台报空指针异常
- 修复了对 spring-boot 服务中 tomcat 线程池的兼容问题
- 排除 Tomcat Jar 使用 Undertow 启动报错
**Optimize**
- hippo4j-core-spring-boot-starter 模块修改名称为 hippo4j-config-spring-boot-starter
- 拆分容器线程池子页面Tomcat、Undertow、Jetty
- 服务端访问客户端时对 URL 转码
- MyBatisPlus 修改全局填充方法优化
- 控制台线程池列表下拉框默认正序
- 控制台线程池实例菜单,对于非可修改容量队列外,不允许修改队列容量
- 动态线程池控制台功能变更
- 租户和项目列表分页查询按照创建时间倒序展示
- 线程池监控页面图表 UI 优化
- 设置 maven-gpg-plugin 插件默认不执行
- 前端控制台相关搜索条件添加必填提示
- hippo4j 消息通知 & 报警抽象优化
- 配置中心未配置线程池启动报错
- 控制台线程池报警 UI 以及功能优化
- Web、框架线程池编辑弹框 UI 优化
- 线程池添加、编辑页面 UI 优化
- 线程池运行详情页前端 UI 优化
**Refactor**
- 删除自定义日志组件
- 线程池监控功能重构
- hippo4j core 配置中心生效判断重构
- 配置变更通知 & 报警通知重构
- Web 容器线程池适配迁移 hippo4j-adapter
## 1.3.1 (July 17, 2022)
注:这是一个兼容历史版本的小范围升级。
**Feature**
- 控制台新增线程池功能设置为 Admin 权限
- 添加 Hystrix 线程池监控及动态变更
- 添加 Netty 上传动态线程池监控数据方式
- 添加 GitHub Actions CI 流程
- 添加 Spring Kafka 示例项目
- Tomcat 版本号 >= 9.0.55 线程池适配
**Refactor**
- 更多线程池拆分子目录页面
**Optimize**
- hippo4j core 添加 banner 打印
- 优化可变更容量阻塞队列名称
**BUG**
- Apollo 配置修改延迟了一个版本
- Spring Boot 环境下使用 hippo4j-core 接入,配置中心使用 nacos启动时提示 ConfigService not found
查看 1.3.1 版本发布https://github.com/mabaiwan/hippo4j/milestone/9
## 1.3.0 (June 06, 2022)
1.3.0 发布 **适配三方框架的基础框架**。
目前已完成 **Dubbo、RabbitMQ、RocketMQ、RocketMQSpringCloudStream** 的线程池适配,后续还会接入 **Kafka、Hystrix** 等框架或中间件的线程池适配。
注:这是一个兼容历史版本的重大升级。
**Feature**
- 添加 RabbitMQ 线程池监控及动态变更
- 添加 RocketMQ 线程池监控及动态变更
- 添加 Dubbo 线程池监控及动态变更
- 添加 SpringCloud Stream RocketMQ 消费线程池监控及动态变更
**Refactor**
- 重构容器线程池查询及修改功能
- 优化配置中心触发监听后,所执行的数据变更逻辑
**Optimize**
- 前端控制台删除无用组件
- 服务端页面字段未显示中文
- 控制台 UI 优化
- 修改线程池实例后实时刷新列表参数
- 容器线程池编辑仅限 Admin 权限
- SpringBoot Starter 变更包路径
**BUG**
- 修复 SpringBoot Nacos 动态刷新不生效
- 报警配置 alarm=false 不配置通知报警平台和接收人报错
## 1.2.1 (May 07, 2022)
**BugFix**
- apollo 动态配置不生效
- 修复 hippo4j-core 后置处理器创建线程池问题
- 重构 hippo4j-core spring 后置处理器逻辑
- 优化ThreadPoolNotifyAlarmHandler下的空指针异常
- 修复线程池核心、最大线程数变更问题
- startup.cmd 未正常读取 conf 配置文件
**Optimize**
- 配置文件中字段歧义
- 修改代码中历史网址
- InstanceInfo 的 groupKey 参数重复设置
- ConfigFileTypeEnum 枚举字段添加注释
- 线程资源通过线程池创建,不允许自行显示创建线程
- Guava 版本升级至 30.0-jre 及以上版本
- SystemClock 替换 System.currentTimeMillis()
- 添加代码格式化插件 Spotless
- 修改线程池文案
## 1.2.0 (Mar 13, 2022)
**Feature**
- hippo4j-core线程池资源对接 Prometheus 监控
- hippo4j-core 支持 Zookeeper
- hippo4j-core 支持 Apollo
**Optimize**
- 适配非 Web SpringBoot 项目使用 Hippo4J
- 优化报警通知
- 修复在 JDK 小版本中的兼容性问题
**BugFix**
- server 端查看容器线程池,参数为 null
- 重构线程池查看及容器线程池查看等交互
- 修复引入 hippo4j-spring-boot-starter 后,运行单元测试报错
- 修复可能出现的空指针异常
## 1.1.0 (Mar 13, 2022)
Hippo4J 线程池框架 1.1.0 RELEASE 版本,添加了 Hippo4J-Core依赖配置中心的动态线程池.
**Feature**
- 删除 DynamicThreadPoolExecutor 内代码实现,仅通过线程池扩展点进行扩展
- 通过动态代理实现线程池拒绝策略执行次数统计
- 抽象通知报警消息模块
- 抽象 hippo4j 核心组件,不依赖 server 端即可完成动态调参、监控、报警等功能
- 前端删除线程池按钮添加 Admin 权限
- 添加线程池任务运行超长报警
- 容器线程池支持 Undertow
- 容器线程池支持 Jetty
- 重构服务端异常体系
**Optimize**
- 前端项目 Token 失效跳转登录页
- 优化 Server 启动脚本日志输出
- 优化前端按钮权限控制粒度
- 优化线程池报警推送文案
- 前端弹框样式优化
- 适配低版本 SpringBoot Bind
- 优化消息通知模块
**BugFix**
- Duplicate entry 'xxx' for key 'uk_configinfo_datagrouptenant'
## 1.0.0 (Feb 01, 2022)
**Feature**
- 线程池运行堆栈查看
- 扩展 Web 容器线程池动态调参、监控
**Optimize**
- 删除高版本 SpringBoot Api
- ListableBeanFactory#findAnnotationOnBean SpringBoot 低版本适配
- 优化客户端关闭时调用服务端钩子函数
- 线程池实例参数弹框添加实例 ID 和线程池状态
- 补充线程池替换 Hippo4J 文档
- 1.5.x springboot 引入hippo4j-spring-boot-starter配置项bean初始化失败
- 优化线程池参数编辑合理性校验
- BaseInstanceRegistry 读写锁重构
**BugFix**
- 本地项目线程池实例缓存无法精确清理
- 线程池实例页面多实例不同 Active 展示错误
- 创建动态线程池逻辑判断修复
- 创建动态线程池增强参数未设置
- 控制消息推送报警频率的方法有并发安全的问题
- tomcat线程池上下文获取失败

@ -60,7 +60,7 @@ const config = {
announcementBar: { announcementBar: {
id: 'announcementBar-1', // Increment on change id: 'announcementBar-1', // Increment on change
// content: `⭐️ If you like hippo4j, give it a star on <a target="_blank" rel="noopener noreferrer" href="https://gitee.com/mabaiwancn/hippo4j">Gitee</a>, thanks.`, // content: `⭐️ If you like hippo4j, give it a star on <a target="_blank" rel="noopener noreferrer" href="https://gitee.com/mabaiwancn/hippo4j">Gitee</a>, thanks.`,
content: `⭐️ 如果您喜欢 hippo4j请在 <a target="_blank" rel="noopener noreferrer" href="https://gitee.com/mabaiwancn/hippo4j">Gitee</a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/opengoofy/hippo4j">GitHub</a> 上给它一个 star谢谢`, content: `⭐️ 如果您喜欢 hippo4j请在 <a target="_blank" rel="noopener noreferrer" href="https://gitee.com/mabaiwancn/hippo4j">Gitee</a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/opengoofy/hippo4j">GitHub</a> 上给它一个 star谢谢`,
// content: `<a target="_blank" rel="noopener noreferrer" href="https://xiaomage.info/knowledge-planet/">👉 《小马哥的代码实战课》官方知识星球来啦!!!</a>`, // content: `<a target="_blank" rel="noopener noreferrer" href="https://xiaomage.info/knowledge-planet/">👉 《小马哥的代码实战课》官方知识星球来啦!!!</a>`,
}, },
navbar: { navbar: {
@ -146,7 +146,7 @@ const config = {
items: [ items: [
{ {
label: 'Gitee', label: 'Gitee',
href: 'https://gitee.com/mabaiwancn/hippo4j', href: 'https://gitee.com/itmachen/hippo4j',
}, },
{ {
label: 'GitHub', label: 'GitHub',

@ -18,42 +18,10 @@
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId> <artifactId>dubbo</artifactId>
<version>${alibaba-dubbo.version}</version> <version>${alibaba-dubbo.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -22,12 +22,12 @@ import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter;
import cn.hippo4j.adapter.base.ThreadPoolAdapterState; import cn.hippo4j.adapter.base.ThreadPoolAdapterState;
import com.alibaba.dubbo.common.extension.ExtensionLoader; import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.store.DataStore; import com.alibaba.dubbo.common.store.DataStore;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -41,7 +41,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMI
@Slf4j @Slf4j
public class AlibabaDubboThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> { public class AlibabaDubboThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> {
private final Map<String, ThreadPoolExecutor> DUBBO_PROTOCOL_EXECUTOR = Maps.newHashMap(); private final Map<String, ThreadPoolExecutor> DUBBO_PROTOCOL_EXECUTOR = new HashMap<>();
@Override @Override
public String mark() { public String mark() {

@ -8,7 +8,6 @@
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
<name>hippo4j-adapter-base</name>
<dependencies> <dependencies>
<dependency> <dependency>
@ -17,35 +16,4 @@
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -18,10 +18,10 @@
package cn.hippo4j.adapter.base; package cn.hippo4j.adapter.base;
import cn.hippo4j.common.config.ApplicationContextHolder; import cn.hippo4j.common.config.ApplicationContextHolder;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Thread-pool adapter bean container. * Thread-pool adapter bean container.
@ -31,7 +31,7 @@ public class ThreadPoolAdapterBeanContainer implements InitializingBean {
/** /**
* Store three-party thread pool framework bean instances. * Store three-party thread pool framework bean instances.
*/ */
public static final Map<String, ThreadPoolAdapter> THREAD_POOL_ADAPTER_BEAN_CONTAINER = Maps.newConcurrentMap(); public static final Map<String, ThreadPoolAdapter> THREAD_POOL_ADAPTER_BEAN_CONTAINER = new ConcurrentHashMap<>();
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {

@ -18,7 +18,6 @@
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId> <artifactId>dubbo</artifactId>
@ -26,35 +25,4 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -21,7 +21,6 @@ import cn.hippo4j.adapter.base.ThreadPoolAdapter;
import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter; import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter;
import cn.hippo4j.adapter.base.ThreadPoolAdapterState; import cn.hippo4j.adapter.base.ThreadPoolAdapterState;
import cn.hippo4j.common.toolkit.ReflectUtil; import cn.hippo4j.common.toolkit.ReflectUtil;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.Version; import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.extension.ExtensionLoader;
@ -31,6 +30,7 @@ import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -45,7 +45,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMI
@Slf4j @Slf4j
public class DubboThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> { public class DubboThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> {
private final Map<String, ThreadPoolExecutor> DUBBO_PROTOCOL_EXECUTOR = Maps.newHashMap(); private final Map<String, ThreadPoolExecutor> DUBBO_PROTOCOL_EXECUTOR = new HashMap<>();
@Override @Override
public String mark() { public String mark() {

@ -14,42 +14,10 @@
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>${spring-cloud-starter-netflix-hystrix.version}</version> <version>${spring-cloud-starter-netflix-hystrix.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -20,8 +20,6 @@ package cn.hippo4j.adapter.hystrix;
import cn.hippo4j.adapter.base.*; import cn.hippo4j.adapter.base.*;
import cn.hippo4j.common.config.ApplicationContextHolder; import cn.hippo4j.common.config.ApplicationContextHolder;
import cn.hippo4j.common.toolkit.CollectionUtil; import cn.hippo4j.common.toolkit.CollectionUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.netflix.hystrix.HystrixThreadPool; import com.netflix.hystrix.HystrixThreadPool;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
@ -29,6 +27,7 @@ import org.springframework.context.ApplicationListener;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -49,7 +48,7 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL
private static final String THREAD_POOLS_FIELD = "threadPools"; private static final String THREAD_POOLS_FIELD = "threadPools";
private final Map<String, ThreadPoolExecutor> HYSTRIX_CONSUME_EXECUTOR = Maps.newHashMap(); private final Map<String, ThreadPoolExecutor> HYSTRIX_CONSUME_EXECUTOR = new HashMap<>();
private ThreadPoolAdapterScheduler threadPoolAdapterScheduler; private ThreadPoolAdapterScheduler threadPoolAdapterScheduler;
@ -112,7 +111,7 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL
// Periodically refresh registration. // Periodically refresh registration.
ThreadPoolAdapterRegisterAction threadPoolAdapterRegisterAction = ApplicationContextHolder.getBean(ThreadPoolAdapterRegisterAction.class); ThreadPoolAdapterRegisterAction threadPoolAdapterRegisterAction = ApplicationContextHolder.getBean(ThreadPoolAdapterRegisterAction.class);
Map<String, ? extends HystrixThreadPoolAdapter> beansOfType = ApplicationContextHolder.getBeansOfType(this.getClass()); Map<String, ? extends HystrixThreadPoolAdapter> beansOfType = ApplicationContextHolder.getBeansOfType(this.getClass());
Map<String, ThreadPoolAdapter> map = Maps.newHashMap(beansOfType); Map<String, ThreadPoolAdapter> map = new HashMap<>(beansOfType);
ThreadPoolAdapterRegisterTask threadPoolAdapterRegisterTask = new ThreadPoolAdapterRegisterTask(scheduler, taskIntervalSeconds, map, threadPoolAdapterRegisterAction); ThreadPoolAdapterRegisterTask threadPoolAdapterRegisterTask = new ThreadPoolAdapterRegisterTask(scheduler, taskIntervalSeconds, map, threadPoolAdapterRegisterAction);
scheduler.schedule(threadPoolAdapterRegisterTask, threadPoolAdapterScheduler.getTaskIntervalSeconds(), TimeUnit.SECONDS); scheduler.schedule(threadPoolAdapterRegisterTask, threadPoolAdapterScheduler.getTaskIntervalSeconds(), TimeUnit.SECONDS);
} }
@ -207,7 +206,7 @@ public class HystrixThreadPoolAdapter implements ThreadPoolAdapter, ApplicationL
ThreadPoolAdapterRegisterAction threadPoolAdapterRegisterAction; ThreadPoolAdapterRegisterAction threadPoolAdapterRegisterAction;
private List<ThreadPoolAdapterCacheConfig> cacheConfigList = Lists.newArrayList(); private List<ThreadPoolAdapterCacheConfig> cacheConfigList = new ArrayList<>();
public ThreadPoolAdapterRegisterTask(ScheduledExecutorService scheduler, int taskIntervalSeconds, public ThreadPoolAdapterRegisterTask(ScheduledExecutorService scheduler, int taskIntervalSeconds,
Map<String, ThreadPoolAdapter> threadPoolAdapterMap, Map<String, ThreadPoolAdapter> threadPoolAdapterMap,

@ -17,7 +17,7 @@
package cn.hippo4j.adapter.hystrix; package cn.hippo4j.adapter.hystrix;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import cn.hippo4j.common.design.builder.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -36,8 +36,8 @@ public class ThreadPoolAdapterScheduler {
public ThreadPoolAdapterScheduler() { public ThreadPoolAdapterScheduler() {
scheduler = new ScheduledThreadPoolExecutor(2, scheduler = new ScheduledThreadPoolExecutor(2,
new ThreadFactoryBuilder() new ThreadFactoryBuilder()
.setNameFormat("threadPoolAdapter") .prefix("threadPoolAdapter")
.setDaemon(true) .daemon(true)
.build()); .build());
} }

@ -15,35 +15,4 @@
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -14,41 +14,9 @@
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId> <artifactId>spring-boot-starter-amqp</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -21,17 +21,13 @@ import cn.hippo4j.adapter.base.ThreadPoolAdapter;
import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter; import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter;
import cn.hippo4j.adapter.base.ThreadPoolAdapterState; import cn.hippo4j.adapter.base.ThreadPoolAdapterState;
import cn.hippo4j.common.toolkit.ReflectUtil; import cn.hippo4j.common.toolkit.ReflectUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory; import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import java.util.List; import java.util.*;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
@ -50,7 +46,7 @@ public class RabbitMQThreadPoolAdapter implements ThreadPoolAdapter, Application
private final Map<String, AbstractConnectionFactory> abstractConnectionFactoryMap; private final Map<String, AbstractConnectionFactory> abstractConnectionFactoryMap;
private final Map<String, ThreadPoolExecutor> RABBITMQ_THREAD_POOL_TASK_EXECUTOR = Maps.newHashMap(); private final Map<String, ThreadPoolExecutor> RABBITMQ_THREAD_POOL_TASK_EXECUTOR = new HashMap<>();
@Override @Override
public String mark() { public String mark() {
@ -71,7 +67,7 @@ public class RabbitMQThreadPoolAdapter implements ThreadPoolAdapter, Application
@Override @Override
public List<ThreadPoolAdapterState> getThreadPoolStates() { public List<ThreadPoolAdapterState> getThreadPoolStates() {
List<ThreadPoolAdapterState> adapterStateList = Lists.newArrayList(); List<ThreadPoolAdapterState> adapterStateList = new ArrayList<>();
RABBITMQ_THREAD_POOL_TASK_EXECUTOR.forEach( RABBITMQ_THREAD_POOL_TASK_EXECUTOR.forEach(
(key, val) -> adapterStateList.add(getThreadPoolState(key))); (key, val) -> adapterStateList.add(getThreadPoolState(key)));
return adapterStateList; return adapterStateList;

@ -14,42 +14,10 @@
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.rocketmq</groupId> <groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId> <artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq.version}</version> <version>${rocketmq.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -22,14 +22,15 @@ import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter;
import cn.hippo4j.adapter.base.ThreadPoolAdapterState; import cn.hippo4j.adapter.base.ThreadPoolAdapterState;
import cn.hippo4j.common.config.ApplicationContextHolder; import cn.hippo4j.common.config.ApplicationContextHolder;
import cn.hippo4j.common.toolkit.ReflectUtil; import cn.hippo4j.common.toolkit.ReflectUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.impl.consumer.ConsumeMessageService; import org.apache.rocketmq.client.impl.consumer.ConsumeMessageService;
import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
@ -42,7 +43,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMI
@Slf4j @Slf4j
public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> { public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> {
private final Map<String, ThreadPoolExecutor> ROCKET_MQ_CONSUME_EXECUTOR = Maps.newHashMap(); private final Map<String, ThreadPoolExecutor> ROCKET_MQ_CONSUME_EXECUTOR = new HashMap<>();
@Override @Override
public String mark() { public String mark() {
@ -65,7 +66,7 @@ public class RocketMQThreadPoolAdapter implements ThreadPoolAdapter, Application
@Override @Override
public List<ThreadPoolAdapterState> getThreadPoolStates() { public List<ThreadPoolAdapterState> getThreadPoolStates() {
List<ThreadPoolAdapterState> adapterStateList = Lists.newArrayList(); List<ThreadPoolAdapterState> adapterStateList = new ArrayList<>();
ROCKET_MQ_CONSUME_EXECUTOR.forEach( ROCKET_MQ_CONSUME_EXECUTOR.forEach(
(key, val) -> adapterStateList.add(getThreadPoolState(key))); (key, val) -> adapterStateList.add(getThreadPoolState(key)));
return adapterStateList; return adapterStateList;

@ -15,35 +15,4 @@
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -14,42 +14,10 @@
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
<version>${spring-cloud-starter-stream-rabbitmq.version}</version> <version>${spring-cloud-starter-stream-rabbitmq.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -23,8 +23,6 @@ import cn.hippo4j.adapter.base.ThreadPoolAdapterState;
import cn.hippo4j.common.config.ApplicationContextHolder; import cn.hippo4j.common.config.ApplicationContextHolder;
import cn.hippo4j.common.toolkit.CollectionUtil; import cn.hippo4j.common.toolkit.CollectionUtil;
import cn.hippo4j.common.toolkit.ReflectUtil; import cn.hippo4j.common.toolkit.ReflectUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer; import org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer; import org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer;
@ -36,10 +34,7 @@ import org.springframework.cloud.stream.binding.InputBindingLifecycle;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter; import org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter;
import java.util.Collection; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMITER; import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMITER;
@ -49,7 +44,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMI
@Slf4j @Slf4j
public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> { public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> {
private final Map<String, AbstractMessageListenerContainer> ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR = Maps.newHashMap(); private final Map<String, AbstractMessageListenerContainer> ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR = new HashMap<>();
@Override @Override
public String mark() { public String mark() {
@ -84,7 +79,7 @@ public class SpringCloudStreamRabbitMQThreadPoolAdapter implements ThreadPoolAda
@Override @Override
public List<ThreadPoolAdapterState> getThreadPoolStates() { public List<ThreadPoolAdapterState> getThreadPoolStates() {
List<ThreadPoolAdapterState> adapterStateList = Lists.newArrayList(); List<ThreadPoolAdapterState> adapterStateList = new ArrayList<>();
ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.forEach( ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.forEach(
(key, val) -> adapterStateList.add(getThreadPoolState(key))); (key, val) -> adapterStateList.add(getThreadPoolState(key)));
return adapterStateList; return adapterStateList;

@ -14,42 +14,10 @@
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-adapter-base</artifactId> <artifactId>hippo4j-adapter-base</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId> <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
<version>${spring-cloud-starter-stream-rocketmq.version}</version> <version>${spring-cloud-starter-stream-rocketmq.version}</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -25,8 +25,6 @@ import cn.hippo4j.common.toolkit.CollectionUtil;
import cn.hippo4j.common.toolkit.ReflectUtil; import cn.hippo4j.common.toolkit.ReflectUtil;
import com.alibaba.cloud.stream.binder.rocketmq.consuming.RocketMQListenerBindingContainer; import com.alibaba.cloud.stream.binder.rocketmq.consuming.RocketMQListenerBindingContainer;
import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQInboundChannelAdapter; import com.alibaba.cloud.stream.binder.rocketmq.integration.RocketMQInboundChannelAdapter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService; import org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService;
@ -37,10 +35,7 @@ import org.springframework.cloud.stream.binder.DefaultBinding;
import org.springframework.cloud.stream.binding.InputBindingLifecycle; import org.springframework.cloud.stream.binding.InputBindingLifecycle;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import java.util.Collection; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMITER; import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMITER;
@ -51,7 +46,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMI
@Slf4j @Slf4j
public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> { public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener<ApplicationStartedEvent> {
private final Map<String, ThreadPoolExecutor> ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR = Maps.newHashMap(); private final Map<String, ThreadPoolExecutor> ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR = new HashMap<>();
@Override @Override
public String mark() { public String mark() {
@ -74,7 +69,7 @@ public class SpringCloudStreamRocketMQThreadPoolAdapter implements ThreadPoolAda
@Override @Override
public List<ThreadPoolAdapterState> getThreadPoolStates() { public List<ThreadPoolAdapterState> getThreadPoolStates() {
List<ThreadPoolAdapterState> adapterStateList = Lists.newArrayList(); List<ThreadPoolAdapterState> adapterStateList = new ArrayList<>();
ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.forEach( ROCKET_MQ_SPRING_CLOUD_STREAM_CONSUME_EXECUTOR.forEach(
(key, val) -> adapterStateList.add(getThreadPoolState(key))); (key, val) -> adapterStateList.add(getThreadPoolState(key)));
return adapterStateList; return adapterStateList;

@ -16,69 +16,32 @@
<version>${tomcat-embed-core.version}</version> <version>${tomcat-embed-core.version}</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId> <artifactId>spring-boot-starter-tomcat</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId> <artifactId>spring-boot-starter-jetty</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId> <artifactId>spring-boot-starter-undertow</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-core</artifactId> <artifactId>hippo4j-core</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -24,12 +24,13 @@ import cn.hippo4j.common.model.ThreadPoolParameterInfo;
import cn.hippo4j.common.model.ThreadPoolRunStateInfo; import cn.hippo4j.common.model.ThreadPoolRunStateInfo;
import cn.hippo4j.common.toolkit.CalculateUtil; import cn.hippo4j.common.toolkit.CalculateUtil;
import cn.hippo4j.core.executor.state.AbstractThreadPoolRuntime; import cn.hippo4j.core.executor.state.AbstractThreadPoolRuntime;
import cn.hutool.core.date.DateUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.WebServer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date; import java.util.Date;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -138,7 +139,7 @@ public class TomcatWebThreadPoolHandler extends AbstractWebThreadPoolService {
runStateInfo.setQueueRemainingCapacity(remainingCapacity); runStateInfo.setQueueRemainingCapacity(remainingCapacity);
runStateInfo.setLargestPoolSize(largestPoolSize); runStateInfo.setLargestPoolSize(largestPoolSize);
runStateInfo.setCompletedTaskCount(completedTaskCount); runStateInfo.setCompletedTaskCount(completedTaskCount);
runStateInfo.setClientLastRefreshTime(DateUtil.formatDateTime(new Date())); runStateInfo.setClientLastRefreshTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
runStateInfo.setTimestamp(System.currentTimeMillis()); runStateInfo.setTimestamp(System.currentTimeMillis());
String rejectedExecutionHandlerName = executor instanceof ThreadPoolExecutor ? ((ThreadPoolExecutor) executor).getRejectedExecutionHandler().getClass().getSimpleName() String rejectedExecutionHandlerName = executor instanceof ThreadPoolExecutor ? ((ThreadPoolExecutor) executor).getRejectedExecutionHandler().getClass().getSimpleName()
: tomcatThreadPoolExecutor.getRejectedExecutionHandler().getClass().getSimpleName(); : tomcatThreadPoolExecutor.getRejectedExecutionHandler().getClass().getSimpleName();

@ -17,6 +17,13 @@
package cn.hippo4j.adapter.web; package cn.hippo4j.adapter.web;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.concurrent.Executor;
import cn.hippo4j.common.constant.ChangeThreadPoolConstants; import cn.hippo4j.common.constant.ChangeThreadPoolConstants;
import cn.hippo4j.common.model.ThreadPoolBaseInfo; import cn.hippo4j.common.model.ThreadPoolBaseInfo;
import cn.hippo4j.common.model.ThreadPoolParameter; import cn.hippo4j.common.model.ThreadPoolParameter;
@ -24,20 +31,14 @@ import cn.hippo4j.common.model.ThreadPoolParameterInfo;
import cn.hippo4j.common.model.ThreadPoolRunStateInfo; import cn.hippo4j.common.model.ThreadPoolRunStateInfo;
import cn.hippo4j.common.toolkit.CalculateUtil; import cn.hippo4j.common.toolkit.CalculateUtil;
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor; import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
import cn.hutool.core.date.DateUtil;
import io.undertow.Undertow; import io.undertow.Undertow;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServer;
import org.springframework.boot.web.server.WebServer;
import org.springframework.util.ReflectionUtils;
import org.xnio.Options; import org.xnio.Options;
import org.xnio.XnioWorker; import org.xnio.XnioWorker;
import java.lang.reflect.Field; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServer;
import java.lang.reflect.Method; import org.springframework.boot.web.server.WebServer;
import java.util.Date; import org.springframework.util.ReflectionUtils;
import java.util.Objects;
import java.util.concurrent.Executor;
/** /**
* Undertow web thread pool handler. * Undertow web thread pool handler.
@ -128,7 +129,7 @@ public class UndertowWebThreadPoolHandler extends AbstractWebThreadPoolService {
? ((DynamicThreadPoolExecutor) fieldObject).getRejectCountNum() ? ((DynamicThreadPoolExecutor) fieldObject).getRejectCountNum()
: -1L; : -1L;
stateInfo.setRejectCount(rejectCount); stateInfo.setRejectCount(rejectCount);
stateInfo.setClientLastRefreshTime(DateUtil.formatDateTime(new Date())); stateInfo.setClientLastRefreshTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
stateInfo.setTimestamp(System.currentTimeMillis()); stateInfo.setTimestamp(System.currentTimeMillis());
return stateInfo; return stateInfo;
} }

@ -22,6 +22,7 @@ import cn.hippo4j.common.model.WebIpAndPortInfo;
import cn.hippo4j.common.toolkit.Assert; import cn.hippo4j.common.toolkit.Assert;
import cn.hippo4j.common.toolkit.StringUtil; import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.core.toolkit.inet.InetUtils; import cn.hippo4j.core.toolkit.inet.InetUtils;
import lombok.NoArgsConstructor;
import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.WebServer;
import java.util.Arrays; import java.util.Arrays;
@ -29,25 +30,35 @@ import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
* Ip and port Holder * Ip and port holder.
*/ */
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public class WebIpAndPortHolder { public class WebIpAndPortHolder {
private static boolean SUPPORT_VERSION = false;
static {
try {
Class.forName("org.springframework.boot.web.server.WebServer");
SUPPORT_VERSION = true;
} catch (Exception ignored) {
}
}
/** /**
* Application ip and application post * Application ip and application post
*/ */
protected static AtomicReference<WebIpAndPortInfo> webIpAndPort = new AtomicReference<>(); protected static AtomicReference<WebIpAndPortInfo> WEB_IP_AND_PORT = new AtomicReference<>();
public static final String ALL = "*"; public static final String ALL = "*";
protected static final String SEPARATOR = ","; protected static final String SEPARATOR = ",";
private WebIpAndPortHolder() {
}
protected static void initIpAndPort() { protected static void initIpAndPort() {
webIpAndPort.compareAndSet(null, getWebIpAndPortInfo()); if (!SUPPORT_VERSION) {
return;
}
WEB_IP_AND_PORT.compareAndSet(null, getWebIpAndPortInfo());
} }
private static WebIpAndPortInfo getWebIpAndPortInfo() { private static WebIpAndPortInfo getWebIpAndPortInfo() {
@ -64,26 +75,26 @@ public class WebIpAndPortHolder {
} }
/** /**
* get WebIpAndPortInfo, If it is null, initialize it * get WebIpAndPortInfo, If it is null, initialize it.
* *
* @return WebIpAndPortInfo * @return Web ip and port info
*/ */
public static WebIpAndPortInfo getWebIpAndPort() { public static WebIpAndPortInfo getWebIpAndPort() {
if (webIpAndPort.get() == null) { if (WEB_IP_AND_PORT.get() == null) {
initIpAndPort(); initIpAndPort();
} }
return WebIpAndPortHolder.webIpAndPort.get(); return WebIpAndPortHolder.WEB_IP_AND_PORT.get();
} }
/** /**
* Check the new properties and instance IP and port * Check the new properties and instance IP and port.
* *
* @param nodes nodes in properties * @param nodes nodes in properties
* @return Whether it meets the conditions * @return Whether it meets the conditions
*/ */
public static boolean check(String nodes) { public static boolean check(String nodes) {
WebIpAndPortInfo webIpAndPort = WebIpAndPortHolder.getWebIpAndPort(); WebIpAndPortInfo webIpAndPort = WebIpAndPortHolder.getWebIpAndPort();
if (StringUtil.isEmpty(nodes) || ALL.equals(nodes)) { if (StringUtil.isEmpty(nodes) || ALL.equals(nodes) || webIpAndPort == null) {
return true; return true;
} }
String[] splitNodes = nodes.split(SEPARATOR); String[] splitNodes = nodes.split(SEPARATOR);
@ -93,5 +104,4 @@ public class WebIpAndPortHolder {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.anyMatch(each -> each.check(webIpAndPort.getIpSegment(), webIpAndPort.getPort())); .anyMatch(each -> each.check(webIpAndPort.getIpSegment(), webIpAndPort.getPort()));
} }
} }

@ -20,10 +20,12 @@ package cn.hippo4j.adapter.web;
import cn.hippo4j.common.model.ThreadPoolRunStateInfo; import cn.hippo4j.common.model.ThreadPoolRunStateInfo;
import cn.hippo4j.common.toolkit.ByteConvertUtil; import cn.hippo4j.common.toolkit.ByteConvertUtil;
import cn.hippo4j.core.executor.state.AbstractThreadPoolRuntime; import cn.hippo4j.core.executor.state.AbstractThreadPoolRuntime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.RuntimeInfo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
/** /**
* Web thread pool run state handler. * Web thread pool run state handler.
*/ */
@ -32,16 +34,20 @@ public class WebThreadPoolRunStateHandler extends AbstractThreadPoolRuntime {
@Override @Override
public ThreadPoolRunStateInfo supplement(ThreadPoolRunStateInfo poolRunStateInfo) { public ThreadPoolRunStateInfo supplement(ThreadPoolRunStateInfo poolRunStateInfo) {
RuntimeInfo runtimeInfo = new RuntimeInfo(); MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
String memoryProportion = StrUtil.builder( MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
"已分配: ", long used = heapMemoryUsage.getUsed();
ByteConvertUtil.getPrintSize(runtimeInfo.getTotalMemory()), long max = heapMemoryUsage.getMax();
" / 最大可用: ", String memoryProportion = new StringBuilder()
ByteConvertUtil.getPrintSize(runtimeInfo.getMaxMemory())).toString(); .append("已分配: ")
.append(ByteConvertUtil.getPrintSize(used))
.append(" / 最大可用: ")
.append(ByteConvertUtil.getPrintSize(max))
.toString();
poolRunStateInfo.setCurrentLoad(poolRunStateInfo.getCurrentLoad() + "%"); poolRunStateInfo.setCurrentLoad(poolRunStateInfo.getCurrentLoad() + "%");
poolRunStateInfo.setPeakLoad(poolRunStateInfo.getPeakLoad() + "%"); poolRunStateInfo.setPeakLoad(poolRunStateInfo.getPeakLoad() + "%");
poolRunStateInfo.setMemoryProportion(memoryProportion); poolRunStateInfo.setMemoryProportion(memoryProportion);
poolRunStateInfo.setFreeMemory(ByteConvertUtil.getPrintSize(runtimeInfo.getFreeMemory())); poolRunStateInfo.setFreeMemory(ByteConvertUtil.getPrintSize(Math.subtractExact(max, used)));
return poolRunStateInfo; return poolRunStateInfo;
} }
} }

@ -18,50 +18,37 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <artifactId>spring-boot-starter-security</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId> <artifactId>jjwt</artifactId>
<version>${jjwt.version}</version> <version>${jjwt.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.hippo4j</groupId> <groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-common</artifactId> <artifactId>hippo4j-common</artifactId>

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.auth.config;
import cn.hippo4j.auth.filter.RewriteUserInfoApiFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
*/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<RewriteUserInfoApiFilter> userInfoApiFilterRegistrationBean() {
FilterRegistrationBean<RewriteUserInfoApiFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new RewriteUserInfoApiFilter());
registration.addUrlPatterns("/*");
return registration;
}
}

@ -22,6 +22,7 @@ import cn.hippo4j.auth.filter.JWTAuthenticationFilter;
import cn.hippo4j.auth.filter.JWTAuthorizationFilter; import cn.hippo4j.auth.filter.JWTAuthorizationFilter;
import cn.hippo4j.auth.security.JwtTokenManager; import cn.hippo4j.auth.security.JwtTokenManager;
import cn.hippo4j.auth.service.impl.UserDetailsServiceImpl; import cn.hippo4j.auth.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
@ -50,6 +51,9 @@ import java.util.stream.Stream;
@EnableGlobalMethodSecurity(prePostEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter { public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${hippo4j.core.auth.enabled:true}")
private Boolean enableAuthentication;
@Resource @Resource
private UserDetailsService userDetailsService; private UserDetailsService userDetailsService;
@ -93,11 +97,12 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
.authorizeRequests() .authorizeRequests()
.antMatchers("/static/**", "/index.html", "/favicon.ico", "/avatar.jpg").permitAll() .antMatchers("/static/**", "/index.html", "/favicon.ico", "/avatar.jpg").permitAll()
.antMatchers("/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs").anonymous() .antMatchers("/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs").anonymous()
.anyRequest().authenticated()
.and() .and()
.addFilter(new JWTAuthenticationFilter(authenticationManager())) .addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(tokenManager, authenticationManager())) .addFilter(new JWTAuthorizationFilter(tokenManager, authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
disableAuthenticationIfNeeded(http);
http.authorizeRequests().anyRequest().authenticated();
} }
@Override @Override
@ -105,4 +110,10 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter {
String[] ignores = Stream.of("/hippo4j/v1/cs/auth/users/apply/token/**").toArray(String[]::new); String[] ignores = Stream.of("/hippo4j/v1/cs/auth/users/apply/token/**").toArray(String[]::new);
web.ignoring().antMatchers(ignores); web.ignoring().antMatchers(ignores);
} }
private void disableAuthenticationIfNeeded(HttpSecurity http) throws Exception {
if (Boolean.FALSE.equals(enableAuthentication)) {
http.authorizeRequests().antMatchers("/hippo4j/v1/cs/**").permitAll();
}
}
} }

@ -21,8 +21,8 @@ import cn.hippo4j.auth.model.biz.user.JwtUser;
import cn.hippo4j.auth.model.biz.user.LoginUser; import cn.hippo4j.auth.model.biz.user.LoginUser;
import cn.hippo4j.auth.toolkit.JwtTokenUtil; import cn.hippo4j.auth.toolkit.JwtTokenUtil;
import cn.hippo4j.auth.toolkit.ReturnT; import cn.hippo4j.auth.toolkit.ReturnT;
import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.common.web.base.Results; import cn.hippo4j.common.web.base.Results;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
@ -34,7 +34,6 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
@ -69,6 +68,7 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
Authentication authenticate = null; Authentication authenticate = null;
try { try {
LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class); LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
request.setAttribute("loginUser", loginUser);
rememberMe.set(loginUser.getRememberMe()); rememberMe.set(loginUser.getRememberMe());
authenticate = authenticationManager.authenticate( authenticate = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList())); new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList()));
@ -100,7 +100,7 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
Map<String, Object> maps = new HashMap(MAP_INITIAL_CAPACITY); Map<String, Object> maps = new HashMap(MAP_INITIAL_CAPACITY);
maps.put("data", JwtTokenUtil.TOKEN_PREFIX + token); maps.put("data", JwtTokenUtil.TOKEN_PREFIX + token);
maps.put("roles", role.split(SPLIT_COMMA)); maps.put("roles", role.split(SPLIT_COMMA));
response.getWriter().write(JSONUtil.toJsonStr(Results.success(maps))); response.getWriter().write(JSONUtil.toJSONString(Results.success(maps)));
} finally { } finally {
rememberMe.remove(); rememberMe.remove();
} }
@ -109,6 +109,6 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
@Override @Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException { protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8");
response.getWriter().write(JSONUtil.toJsonStr(new ReturnT(-1, "Server Error"))); response.getWriter().write(JSONUtil.toJSONString(new ReturnT(-1, "Server Error")));
} }
} }

@ -20,10 +20,10 @@ package cn.hippo4j.auth.filter;
import cn.hippo4j.auth.security.JwtTokenManager; import cn.hippo4j.auth.security.JwtTokenManager;
import cn.hippo4j.auth.toolkit.JwtTokenUtil; import cn.hippo4j.auth.toolkit.JwtTokenUtil;
import cn.hippo4j.common.toolkit.JSONUtil; import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.toolkit.UserContext; import cn.hippo4j.common.toolkit.UserContext;
import cn.hippo4j.common.web.base.Results; import cn.hippo4j.common.web.base.Results;
import cn.hippo4j.common.web.exception.ServiceException; import cn.hippo4j.common.web.exception.ServiceException;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -61,7 +61,7 @@ public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
FilterChain chain) throws IOException, ServletException { FilterChain chain) throws IOException, ServletException {
// Token when verifying client interaction. // Token when verifying client interaction.
String accessToken = request.getParameter(ACCESS_TOKEN); String accessToken = request.getParameter(ACCESS_TOKEN);
if (StrUtil.isNotBlank(accessToken)) { if (StringUtil.isNotBlank(accessToken)) {
tokenManager.validateToken(accessToken); tokenManager.validateToken(accessToken);
Authentication authentication = this.tokenManager.getAuthentication(accessToken); Authentication authentication = this.tokenManager.getAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
@ -85,7 +85,7 @@ public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
String resultStatus = "-1"; String resultStatus = "-1";
if (ex instanceof ServiceException) { if (ex instanceof ServiceException) {
ServiceException serviceException = (ServiceException) ex; ServiceException serviceException = (ServiceException) ex;
resultStatus = serviceException.errorCode.getCode(); resultStatus = serviceException.getErrorCode().getCode();
} }
response.getWriter().write(JSONUtil.toJSONString(Results.failure(resultStatus, ex.getMessage()))); response.getWriter().write(JSONUtil.toJSONString(Results.failure(resultStatus, ex.getMessage())));
response.getWriter().flush(); response.getWriter().flush();

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.auth.filter;
import cn.hippo4j.auth.toolkit.AuthUtil;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* When anonymous login is enabled, an error will be reported when viewing the current user information.
* Modify the URI to query the default administrator information.
*
* before:hippo4j/v1/cs/auth/users/info or hippo4j/v1/cs/auth/users/info/xxx
* after:hippo4j/v1/cs/auth/users/info/admin
*/
public class RewriteUserInfoApiFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
boolean enableAuthentication = AuthUtil.enableAuthentication;
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
String path = httpRequest.getRequestURI();
if (!enableAuthentication && path.contains("users/info")) {
httpRequest.getRequestDispatcher("/hippo4j/v1/cs/auth/users/info/admin").forward(servletRequest, servletResponse);
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}

@ -17,7 +17,7 @@
package cn.hippo4j.auth.security; package cn.hippo4j.auth.security;
import cn.hutool.core.util.StrUtil; import cn.hippo4j.common.toolkit.StringUtil;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
@ -64,7 +64,7 @@ public class JwtTokenManager {
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
List<GrantedAuthority> authorities = AuthorityUtils List<GrantedAuthority> authorities = AuthorityUtils
.commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY)); .commaSeparatedStringToAuthorityList((String) claims.get(AUTHORITIES_KEY));
User principal = new User(claims.getSubject(), StrUtil.EMPTY, authorities); User principal = new User(claims.getSubject(), StringUtil.EMPTY, authorities);
return new UsernamePasswordAuthenticationToken(principal, StrUtil.EMPTY, authorities); return new UsernamePasswordAuthenticationToken(principal, StringUtil.EMPTY, authorities);
} }
} }

@ -21,8 +21,8 @@ import cn.hippo4j.auth.mapper.PermissionMapper;
import cn.hippo4j.auth.model.biz.permission.PermissionQueryPageReqDTO; import cn.hippo4j.auth.model.biz.permission.PermissionQueryPageReqDTO;
import cn.hippo4j.auth.model.biz.permission.PermissionRespDTO; import cn.hippo4j.auth.model.biz.permission.PermissionRespDTO;
import cn.hippo4j.auth.service.PermissionService; import cn.hippo4j.auth.service.PermissionService;
import cn.hutool.core.bean.BeanUtil; import cn.hippo4j.common.toolkit.BeanUtil;
import cn.hutool.core.util.StrUtil; import cn.hippo4j.common.toolkit.StringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@ -44,8 +44,7 @@ public class PermissionServiceImpl implements PermissionService {
public IPage<PermissionRespDTO> listPermission(int pageNo, int pageSize) { public IPage<PermissionRespDTO> listPermission(int pageNo, int pageSize) {
PermissionQueryPageReqDTO queryPage = new PermissionQueryPageReqDTO(pageNo, pageSize); PermissionQueryPageReqDTO queryPage = new PermissionQueryPageReqDTO(pageNo, pageSize);
IPage<PermissionInfo> selectPage = permissionMapper.selectPage(queryPage, null); IPage<PermissionInfo> selectPage = permissionMapper.selectPage(queryPage, null);
return selectPage.convert(each -> BeanUtil.convert(each, PermissionRespDTO.class));
return selectPage.convert(each -> BeanUtil.toBean(each, PermissionRespDTO.class));
} }
@Override @Override
@ -68,9 +67,9 @@ public class PermissionServiceImpl implements PermissionService {
@Override @Override
public void deletePermission(String role, String resource, String action) { public void deletePermission(String role, String resource, String action) {
LambdaUpdateWrapper<PermissionInfo> updateWrapper = Wrappers.lambdaUpdate(PermissionInfo.class) LambdaUpdateWrapper<PermissionInfo> updateWrapper = Wrappers.lambdaUpdate(PermissionInfo.class)
.eq(StrUtil.isNotBlank(role), PermissionInfo::getRole, role) .eq(StringUtil.isNotBlank(role), PermissionInfo::getRole, role)
.eq(StrUtil.isNotBlank(resource), PermissionInfo::getResource, resource) .eq(StringUtil.isNotBlank(resource), PermissionInfo::getResource, resource)
.eq(StrUtil.isNotBlank(action), PermissionInfo::getAction, action); .eq(StringUtil.isNotBlank(action), PermissionInfo::getAction, action);
permissionMapper.delete(updateWrapper); permissionMapper.delete(updateWrapper);
} }
} }

@ -22,9 +22,9 @@ import cn.hippo4j.auth.model.biz.role.RoleQueryPageReqDTO;
import cn.hippo4j.auth.model.biz.role.RoleRespDTO; import cn.hippo4j.auth.model.biz.role.RoleRespDTO;
import cn.hippo4j.auth.service.PermissionService; import cn.hippo4j.auth.service.PermissionService;
import cn.hippo4j.auth.service.RoleService; import cn.hippo4j.auth.service.RoleService;
import cn.hutool.core.bean.BeanUtil; import cn.hippo4j.common.toolkit.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hippo4j.common.toolkit.CollectionUtil;
import cn.hutool.core.util.StrUtil; import cn.hippo4j.common.toolkit.StringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
@ -51,7 +51,7 @@ public class RoleServiceImpl implements RoleService {
public IPage<RoleRespDTO> listRole(int pageNo, int pageSize) { public IPage<RoleRespDTO> listRole(int pageNo, int pageSize) {
RoleQueryPageReqDTO queryPage = new RoleQueryPageReqDTO(pageNo, pageSize); RoleQueryPageReqDTO queryPage = new RoleQueryPageReqDTO(pageNo, pageSize);
IPage<RoleInfo> selectPage = roleMapper.selectPage(queryPage, null); IPage<RoleInfo> selectPage = roleMapper.selectPage(queryPage, null);
return selectPage.convert(each -> BeanUtil.toBean(each, RoleRespDTO.class)); return selectPage.convert(each -> BeanUtil.convert(each, RoleRespDTO.class));
} }
@Override @Override
@ -70,14 +70,14 @@ public class RoleServiceImpl implements RoleService {
@Override @Override
public void deleteRole(String role, String userName) { public void deleteRole(String role, String userName) {
List<String> roleStrList = CollUtil.toList(role); List<String> roleStrList = CollectionUtil.toList(role);
if (StrUtil.isBlank(role)) { if (StringUtil.isBlank(role)) {
LambdaQueryWrapper<RoleInfo> queryWrapper = Wrappers.lambdaQuery(RoleInfo.class).eq(RoleInfo::getUserName, userName); LambdaQueryWrapper<RoleInfo> queryWrapper = Wrappers.lambdaQuery(RoleInfo.class).eq(RoleInfo::getUserName, userName);
roleStrList = roleMapper.selectList(queryWrapper).stream().map(RoleInfo::getRole).collect(Collectors.toList()); roleStrList = roleMapper.selectList(queryWrapper).stream().map(RoleInfo::getRole).collect(Collectors.toList());
} }
LambdaUpdateWrapper<RoleInfo> updateWrapper = Wrappers.lambdaUpdate(RoleInfo.class) LambdaUpdateWrapper<RoleInfo> updateWrapper = Wrappers.lambdaUpdate(RoleInfo.class)
.eq(StrUtil.isNotBlank(role), RoleInfo::getRole, role) .eq(StringUtil.isNotBlank(role), RoleInfo::getRole, role)
.eq(StrUtil.isNotBlank(userName), RoleInfo::getUserName, userName); .eq(StringUtil.isNotBlank(userName), RoleInfo::getUserName, userName);
roleMapper.delete(updateWrapper); roleMapper.delete(updateWrapper);
roleStrList.forEach(each -> permissionService.deletePermission(each, "", "")); roleStrList.forEach(each -> permissionService.deletePermission(each, "", ""));
} }

@ -20,14 +20,21 @@ package cn.hippo4j.auth.service.impl;
import cn.hippo4j.auth.mapper.UserMapper; import cn.hippo4j.auth.mapper.UserMapper;
import cn.hippo4j.auth.model.UserInfo; import cn.hippo4j.auth.model.UserInfo;
import cn.hippo4j.auth.model.biz.user.JwtUser; import cn.hippo4j.auth.model.biz.user.JwtUser;
import cn.hippo4j.auth.model.biz.user.LoginUser;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections; import java.util.Collections;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -38,11 +45,18 @@ import java.util.Set;
@Slf4j @Slf4j
public class UserDetailsServiceImpl implements UserDetailsService { public class UserDetailsServiceImpl implements UserDetailsService {
@Value("${hippo4j.core.auth.enabled:true}")
private Boolean enableAuthentication;
@Resource @Resource
private UserMapper userMapper; private UserMapper userMapper;
@Override @Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
JwtUser anonymous = dealWithAnonymous();
if (!Objects.isNull(anonymous)) {
return anonymous;
}
UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, userName)); UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, userName));
if (Objects.isNull(userInfo)) { if (Objects.isNull(userInfo)) {
log.warn("User {} not found", userName); log.warn("User {} not found", userName);
@ -56,4 +70,27 @@ public class UserDetailsServiceImpl implements UserDetailsService {
jwtUser.setAuthorities(authorities); jwtUser.setAuthorities(authorities);
return jwtUser; return jwtUser;
} }
private JwtUser dealWithAnonymous() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return null;
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
LoginUser loginUser = (LoginUser) request.getAttribute("loginUser");
if (Objects.isNull(loginUser)) {
return null;
}
if (Boolean.FALSE.equals(enableAuthentication)) {
JwtUser jwtUser = new JwtUser();
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
jwtUser.setId(1L);
jwtUser.setUsername("anonymous");
jwtUser.setPassword(bCryptPasswordEncoder.encode(loginUser.getPassword()));
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"));
jwtUser.setAuthorities(authorities);
return jwtUser;
}
return null;
}
} }

@ -24,10 +24,9 @@ import cn.hippo4j.auth.model.biz.user.UserReqDTO;
import cn.hippo4j.auth.model.biz.user.UserRespDTO; import cn.hippo4j.auth.model.biz.user.UserRespDTO;
import cn.hippo4j.auth.service.RoleService; import cn.hippo4j.auth.service.RoleService;
import cn.hippo4j.auth.service.UserService; import cn.hippo4j.auth.service.UserService;
import cn.hippo4j.common.toolkit.BeanUtil;
import cn.hippo4j.common.toolkit.StringUtil; import cn.hippo4j.common.toolkit.StringUtil;
import cn.hippo4j.common.web.exception.ServiceException; import cn.hippo4j.common.web.exception.ServiceException;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -59,7 +58,7 @@ public class UserServiceImpl implements UserService {
LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery(UserInfo.class) LambdaQueryWrapper<UserInfo> queryWrapper = Wrappers.lambdaQuery(UserInfo.class)
.eq(StringUtil.isNotBlank(reqDTO.getUserName()), UserInfo::getUserName, reqDTO.getUserName()); .eq(StringUtil.isNotBlank(reqDTO.getUserName()), UserInfo::getUserName, reqDTO.getUserName());
IPage<UserInfo> selectPage = userMapper.selectPage(reqDTO, queryWrapper); IPage<UserInfo> selectPage = userMapper.selectPage(reqDTO, queryWrapper);
return selectPage.convert(each -> BeanUtil.toBean(each, UserRespDTO.class)); return selectPage.convert(each -> BeanUtil.convert(each, UserRespDTO.class));
} }
@Override @Override
@ -71,16 +70,16 @@ public class UserServiceImpl implements UserService {
throw new RuntimeException("用户名重复"); throw new RuntimeException("用户名重复");
} }
reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword())); reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword()));
UserInfo insertUser = BeanUtil.toBean(reqDTO, UserInfo.class); UserInfo insertUser = BeanUtil.convert(reqDTO, UserInfo.class);
userMapper.insert(insertUser); userMapper.insert(insertUser);
} }
@Override @Override
public void updateUser(UserReqDTO reqDTO) { public void updateUser(UserReqDTO reqDTO) {
if (StrUtil.isNotBlank(reqDTO.getPassword())) { if (StringUtil.isNotBlank(reqDTO.getPassword())) {
reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword())); reqDTO.setPassword(bCryptPasswordEncoder.encode(reqDTO.getPassword()));
} }
UserInfo updateUser = BeanUtil.toBean(reqDTO, UserInfo.class); UserInfo updateUser = BeanUtil.convert(reqDTO, UserInfo.class);
LambdaUpdateWrapper<UserInfo> updateWrapper = Wrappers.lambdaUpdate(UserInfo.class) LambdaUpdateWrapper<UserInfo> updateWrapper = Wrappers.lambdaUpdate(UserInfo.class)
.eq(UserInfo::getUserName, reqDTO.getUserName()); .eq(UserInfo::getUserName, reqDTO.getUserName());
userMapper.update(updateUser, updateWrapper); userMapper.update(updateUser, updateWrapper);
@ -108,7 +107,7 @@ public class UserServiceImpl implements UserService {
Wrapper queryWrapper = Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, reqDTO.getUserName()); Wrapper queryWrapper = Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, reqDTO.getUserName());
UserInfo userInfo = userMapper.selectOne(queryWrapper); UserInfo userInfo = userMapper.selectOne(queryWrapper);
UserRespDTO respUser = Optional.ofNullable(userInfo) UserRespDTO respUser = Optional.ofNullable(userInfo)
.map(each -> BeanUtil.toBean(each, UserRespDTO.class)) .map(each -> BeanUtil.convert(each, UserRespDTO.class))
.orElseThrow(() -> new ServiceException("查询无此用户, 可以尝试清空缓存或退出登录.")); .orElseThrow(() -> new ServiceException("查询无此用户, 可以尝试清空缓存或退出登录."));
return respUser; return respUser;
} }

@ -0,0 +1,32 @@
/*
* 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.auth.toolkit;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class AuthUtil {
public static boolean enableAuthentication;
@Value("${hippo4j.core.auth.enabled:true}")
public void setEnableAuthentication(boolean enabled) {
AuthUtil.enableAuthentication = enabled;
}
}

@ -22,90 +22,45 @@
<artifactId>spring-context</artifactId> <artifactId>spring-context</artifactId>
<version>${spring-context.version}</version> <version>${spring-context.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
<version>${spring-web.version}</version> <version>${spring-web.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId> <artifactId>spring-beans</artifactId>
<version>${spring-beans.version}</version> <version>${spring-beans.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>
<version>${spring-core.version}</version> <version>${spring-core.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId> <artifactId>spring-boot-starter-test</artifactId>
</dependency> <scope>test</scope>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>hutool-all</artifactId> <artifactId>caffeine</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>com.github.dozermapper</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>dozer-core</artifactId>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
<Built-By>chen.ma</Built-By>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> </project>

@ -32,6 +32,9 @@ public interface ClientCloseHookExecute {
*/ */
void closeHook(ClientCloseHookReq req); void closeHook(ClientCloseHookReq req);
/**
* Client close hook req.
*/
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
class ClientCloseHookReq { class ClientCloseHookReq {

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

@ -25,14 +25,14 @@ public class ChangeThreadPoolConstants {
/** /**
* Dynamic thread pool parameter change text * Dynamic thread pool parameter change text
*/ */
public static final String CHANGE_THREAD_POOL_TEXT = "[{}] Dynamic thread pool change parameter." + public static final String CHANGE_THREAD_POOL_TEXT = "[{}] Dynamic thread pool change parameter."
"\n corePoolSize: {}" + + "\n corePoolSize: {}"
"\n maximumPoolSize: {}" + + "\n maximumPoolSize: {}"
"\n capacity: {}" + + "\n capacity: {}"
"\n keepAliveTime: {}" + + "\n keepAliveTime: {}"
"\n executeTimeOut: {}" + + "\n executeTimeOut: {}"
"\n rejectedType: {}" + + "\n rejectedType: {}"
"\n allowCoreThreadTimeOut: {}"; + "\n allowCoreThreadTimeOut: {}";
/** /**
* Dynamic thread pool parameter change separator * Dynamic thread pool parameter change separator

@ -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 cn.hippo4j.common.constant;
public class ConfigModifyTypeConstants {
/**
* Thread pool manager change
*/
public static final int THREAD_POOL_MANAGER = 1;
/**
* Thread pool instance change
*/
public static final int THREAD_POOL_INSTANCE = 2;
/**
* Web thread pool change
*/
public static final int WEB_THREAD_POOL = 3;
/**
* Adapter thread pool change
*/
public static final int ADAPTER_THREAD_POOL = 4;
}

@ -38,11 +38,13 @@ public class Constants {
public static final String DEFAULT_NAMESPACE_ID = "public"; public static final String DEFAULT_NAMESPACE_ID = "public";
public static final String ENCODE = "UTF-8";
public static final String NULL = ""; public static final String NULL = "";
public static final String UP = "UP"; 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; public static final int CONFIG_LONG_POLL_TIMEOUT = 30000;
@ -62,6 +64,8 @@ public class Constants {
public static final String LISTENER_PATH = CONFIG_CONTROLLER_PATH + "/listener"; public static final String LISTENER_PATH = CONFIG_CONTROLLER_PATH + "/listener";
public static final String VERIFY_PATH = CONFIG_CONTROLLER_PATH + "/verify";
public static final String MONITOR_PATH = BASE_PATH + "/monitor"; public static final String MONITOR_PATH = BASE_PATH + "/monitor";
public static final String REGISTER_ADAPTER_BASE_PATH = BASE_PATH + "/adapter/thread-pool"; public static final String REGISTER_ADAPTER_BASE_PATH = BASE_PATH + "/adapter/thread-pool";

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.common.constant;
/**
* Http header constants.
*/
public interface HttpHeaderConstants {
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 type.
*
* @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,108 @@
/*
* 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 response code.
*
* @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;
}

@ -15,9 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package cn.hippo4j.core.executor.support; package cn.hippo4j.common.design.builder;
import cn.hippo4j.common.design.builder.Builder;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;

@ -64,12 +64,10 @@ public class AbstractSubjectCenter {
log.warn("Register observer. A string whose subject or observer is empty or empty."); log.warn("Register observer. A string whose subject or observer is empty or empty.");
return; return;
} }
List<Observer> observers = OBSERVERS_MAP.get(subject); List<Observer> observers = OBSERVERS_MAP.get(subject);
if (CollectionUtil.isEmpty(observers)) { if (CollectionUtil.isEmpty(observers)) {
observers = new ArrayList(); observers = new ArrayList();
} }
observers.add(observer); observers.add(observer);
OBSERVERS_MAP.put(subject, observers); OBSERVERS_MAP.put(subject, observers);
} }
@ -90,12 +88,11 @@ public class AbstractSubjectCenter {
* @param observer * @param observer
*/ */
public static void remove(String subject, Observer observer) { public static void remove(String subject, Observer observer) {
List<Observer> observers; List<Observer> observers = OBSERVERS_MAP.get(subject);
if (StringUtil.isBlank(subject) || CollectionUtil.isEmpty((observers = OBSERVERS_MAP.get(subject))) || observer == null) { if (StringUtil.isBlank(subject) || CollectionUtil.isEmpty(observers) || observer == null) {
log.warn("Remove observer. A string whose subject or observer is empty or empty."); log.warn("Remove observer. A string whose subject or observer is empty or empty.");
return; return;
} }
observers.remove(observer); observers.remove(observer);
} }
@ -121,7 +118,6 @@ public class AbstractSubjectCenter {
log.warn("Under the subject, there is no observer group."); log.warn("Under the subject, there is no observer group.");
return; return;
} }
observers.parallelStream().forEach(each -> { observers.parallelStream().forEach(each -> {
try { try {
each.accept(observerMessage); each.accept(observerMessage);
@ -131,6 +127,9 @@ public class AbstractSubjectCenter {
}); });
} }
/**
* Subject type.
*/
public enum SubjectType { public enum SubjectType {
/** /**

@ -0,0 +1,58 @@
/*
* 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.enums;
public enum VerifyEnum {
/**
* unVerify
*/
TO_VERIFY(0, "待审核"),
/**
* accept
*/
VERIFY_ACCEPT(1, "审核通过"),
/**
* reject
*/
VERIFY_REJECT(2, "审核拒绝"),
/**
* invalid
*/
VERIFY_INVALID(3, "失效");
private final Integer verifyStatus;
private final String desc;
VerifyEnum(Integer verifyStatus, String desc) {
this.verifyStatus = verifyStatus;
this.desc = desc;
}
public String getDesc() {
return this.desc;
}
public Integer getVerifyStatus() {
return this.verifyStatus;
}
}

@ -26,6 +26,9 @@ import java.util.concurrent.ThreadFactory;
*/ */
public class ExecutorFactory { public class ExecutorFactory {
/**
* Thread pool management tool.
*/
public static final class Managed { public static final class Managed {
private static final String DEFAULT_NAMESPACE = "dynamic.thread-pool"; private static final String DEFAULT_NAMESPACE = "dynamic.thread-pool";

@ -32,7 +32,7 @@ public class ThreadPoolManager {
private Map<String, Map<String, Set<ExecutorService>>> resourcesManager; private Map<String, Map<String, Set<ExecutorService>>> resourcesManager;
private Map<String, Object> lockers = new ConcurrentHashMap(8); private Map<String, Object> lockers = new ConcurrentHashMap();
private static final ThreadPoolManager INSTANCE = new ThreadPoolManager(); private static final ThreadPoolManager INSTANCE = new ThreadPoolManager();
@ -47,7 +47,7 @@ public class ThreadPoolManager {
} }
private void init() { private void init() {
resourcesManager = new ConcurrentHashMap(8); resourcesManager = new ConcurrentHashMap();
} }
public void register(String tenantId, String group, ExecutorService executor) { public void register(String tenantId, String group, ExecutorService executor) {
@ -60,7 +60,7 @@ public class ThreadPoolManager {
synchronized (monitor) { synchronized (monitor) {
Map<String, Set<ExecutorService>> map = resourcesManager.get(tenantId); Map<String, Set<ExecutorService>> map = resourcesManager.get(tenantId);
if (map == null) { if (map == null) {
map = new HashMap(8); map = new HashMap();
map.put(group, new HashSet()); map.put(group, new HashSet());
map.get(group).add(executor); map.get(group).add(executor);
resourcesManager.put(tenantId, map); resourcesManager.put(tenantId, map);

@ -24,7 +24,13 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.*; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -78,6 +84,8 @@ public enum BlockingQueueTypeEnum {
this.name = name; this.name = name;
} }
private static final int DEFAULT_CAPACITY = 1024;
static { static {
DynamicThreadPoolServiceLoader.register(CustomBlockingQueue.class); DynamicThreadPoolServiceLoader.register(CustomBlockingQueue.class);
} }
@ -105,7 +113,7 @@ public enum BlockingQueueTypeEnum {
.orElseGet(() -> { .orElseGet(() -> {
int temCapacity = capacity; int temCapacity = capacity;
if (capacity == null || capacity <= 0) { if (capacity == null || capacity <= 0) {
temCapacity = 1024; temCapacity = DEFAULT_CAPACITY;
} }
return new LinkedBlockingQueue(temCapacity); return new LinkedBlockingQueue(temCapacity);
})); }));

@ -50,7 +50,7 @@ public enum RejectedPolicyTypeEnum {
@Getter @Getter
private String name; private String name;
public RejectedExecutionHandler rejectedHandler; private RejectedExecutionHandler rejectedHandler;
RejectedPolicyTypeEnum(Integer type, String name, RejectedExecutionHandler rejectedHandler) { RejectedPolicyTypeEnum(Integer type, String name, RejectedExecutionHandler rejectedHandler) {
this.type = type; this.type = type;

@ -17,11 +17,15 @@
package cn.hippo4j.common.executor.support; package cn.hippo4j.common.executor.support;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.*; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.*; import java.util.concurrent.locks.Condition;
import java.util.*; import java.util.concurrent.locks.ReentrantLock;
/** /**
* A clone of {@linkplain java.util.concurrent.LinkedBlockingQueue} * A clone of {@linkplain java.util.concurrent.LinkedBlockingQueue}
@ -229,9 +233,10 @@ public class ResizableCapacityLinkedBlockingQueue<E> extends AbstractQueue<E>
*/ */
public ResizableCapacityLinkedBlockingQueue(Collection<? extends E> c) { public ResizableCapacityLinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE); this(Integer.MAX_VALUE);
for (Iterator<? extends E> it = c.iterator(); it.hasNext();) for (Iterator<? extends E> it = c.iterator(); it.hasNext();) {
add(it.next()); add(it.next());
} }
}
// this doc comment is overridden to remove the reference to collections // this doc comment is overridden to remove the reference to collections
// greater in size than Integer.MAX_VALUE // greater in size than Integer.MAX_VALUE
@ -698,6 +703,9 @@ public class ResizableCapacityLinkedBlockingQueue<E> extends AbstractQueue<E>
return new Itr(); return new Itr();
} }
/**
* Itr.
*/
private class Itr implements Iterator<E> { private class Itr implements Iterator<E> {
/* /*

@ -113,6 +113,9 @@ public class InstanceInfo {
this.actionType = actionType; this.actionType = actionType;
} }
/**
* Instance status.
*/
public enum InstanceStatus { public enum InstanceStatus {
/** /**
@ -153,6 +156,9 @@ public class InstanceInfo {
} }
} }
/**
* Action type.
*/
public enum ActionType { public enum ActionType {
/** /**
* ADDED * ADDED
@ -170,6 +176,9 @@ public class InstanceInfo {
DELETED DELETED
} }
/**
* Instance renew.
*/
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public static class InstanceRenew { public static class InstanceRenew {

@ -17,7 +17,10 @@
package cn.hippo4j.common.model.register.notify; package cn.hippo4j.common.model.register.notify;
import lombok.*; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/** /**
* Dynamic thread-pool register core notify parameter. * Dynamic thread-pool register core notify parameter.

@ -18,7 +18,11 @@
package cn.hippo4j.common.spi; package cn.hippo4j.common.spi;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -86,7 +90,8 @@ public class DynamicThreadPoolServiceLoader {
private static Object newServiceInstance(final Class<?> clazz) { private static Object newServiceInstance(final Class<?> clazz) {
try { try {
return clazz.getDeclaredConstructor().newInstance(); return clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { } catch (InstantiationException | IllegalAccessException | InvocationTargetException
| NoSuchMethodException e) {
throw new ServiceLoaderInstantiationException(clazz, e); throw new ServiceLoaderInstantiationException(clazz, e);
} }
} }

@ -91,6 +91,16 @@ public class Assert {
} }
} }
public static void notBlank(String str, String message) {
if (StringUtil.isBlank(str)) {
throw new IllegalArgumentException(message);
}
}
public static void notBlank(String str) {
notBlank(str, "[Assertion failed] - this string must not be blank");
}
public static void hasText(String text, String message) { public static void hasText(String text, String message) {
if (!StringUtils.hasText(text)) { if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException(message); throw new IllegalArgumentException(message);

@ -0,0 +1,152 @@
/*
* 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.IllegalException;
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.*;
/**
* Bean util.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanUtil {
protected static Mapper BEAN_MAPPER_BUILDER;
static {
BEAN_MAPPER_BUILDER = DozerBeanMapperBuilder.buildDefault();
}
public static <T, S> T convert(S source, Class<T> clazz) {
return Optional.ofNullable(source)
.map(each -> BEAN_MAPPER_BUILDER.map(each, clazz))
.orElse(null);
}
public static <T, S> T convert(S source, T target) {
Optional.ofNullable(source)
.ifPresent(each -> BEAN_MAPPER_BUILDER.map(each, target));
return target;
}
public static <T, S> List<T> convert(List<S> sources, Class<T> clazz) {
return Optional.ofNullable(sources)
.map(each -> {
List<T> targetList = new ArrayList<T>(each.size());
each.forEach(item -> targetList.add(BEAN_MAPPER_BUILDER.map(item, clazz)));
return targetList;
})
.orElse(null);
}
public static <T, S> Set<T> convert(Set<S> sources, Class<T> clazz) {
return Optional.ofNullable(sources)
.map(each -> {
Set<T> targetSize = new HashSet<T>(each.size());
each.forEach(item -> targetSize.add(BEAN_MAPPER_BUILDER.map(item, clazz)));
return targetSize;
})
.orElse(null);
}
public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz, boolean toCamelCase) {
if (clazz == null) {
return null;
}
try {
T newInstance = clazz.newInstance();
return mapToBean(map, newInstance, toCamelCase);
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalException("do not create instance for " + clazz.getName(), e);
}
}
/**
* map to bean
*
* @param map map
* @param bean obj bean
* @param toCamelCase format to camel case
* @param <T> bean type
* @return T
*/
public static <T> T mapToBean(Map<String, Object> map, T bean, boolean toCamelCase) {
if (bean == null) {
return null;
}
if (map.isEmpty()) {
return bean;
}
Class<?> clazz = bean.getClass();
map.forEach((s, o) -> {
String name = toCamelCase ? StringUtil.toCamelCase(s, StringUtil.UNDERLINE) : s;
Method method = setter(clazz, name);
if (method != null) {
ReflectUtil.invoke(bean, method, o);
}
});
return bean;
}
/**
* getter for properties
*
* @param o obj
* @param propertiesName name
* @return Method for get
*/
public static Method getter(Class<?> o, String propertiesName) {
if (o == null) {
return null;
}
try {
PropertyDescriptor descriptor = new PropertyDescriptor(propertiesName, o);
return descriptor.getReadMethod();
} catch (IntrospectionException e) {
throw new IllegalException("not find getter for" + propertiesName + "in" + o.getName(), e);
}
}
/**
* setter for properties
*
* @param o obj
* @param propertiesName name
* @return Method for set
*/
public static Method setter(Class<?> o, String propertiesName) {
if (o == null) {
return null;
}
try {
PropertyDescriptor descriptor = new PropertyDescriptor(propertiesName, o);
return descriptor.getWriteMethod();
} catch (IntrospectionException e) {
throw new IllegalException("not find setter for" + propertiesName + "in" + o.getName(), e);
}
}
}

@ -17,8 +17,6 @@
package cn.hippo4j.common.toolkit; package cn.hippo4j.common.toolkit;
import cn.hutool.core.util.StrUtil;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -42,7 +40,7 @@ public class BooleanUtil {
* @return * @return
*/ */
public static boolean toBoolean(String valueStr) { public static boolean toBoolean(String valueStr) {
if (StrUtil.isNotBlank(valueStr)) { if (StringUtil.isNotBlank(valueStr)) {
valueStr = valueStr.trim().toLowerCase(); valueStr = valueStr.trim().toLowerCase();
return TREE_SET.contains(valueStr); return TREE_SET.contains(valueStr);
} }

@ -24,9 +24,11 @@ import java.text.DecimalFormat;
*/ */
public class ByteConvertUtil { public class ByteConvertUtil {
public static final Integer KB_SIZE = 2 << 9; public static final int KB_SIZE = 2 << 9;
public static final Integer MB_SIZE = 2 << 19;
public static final Integer GB_SIZE = 2 << 29; public static final int MB_SIZE = 2 << 19;
public static final int GB_SIZE = 2 << 29;
public static String getPrintSize(long size) { public static String getPrintSize(long size) {
DecimalFormat df = new DecimalFormat("#.00"); DecimalFormat df = new DecimalFormat("#.00");

@ -25,7 +25,9 @@ package cn.hippo4j.common.toolkit;
*/ */
public class CalculateUtil { public class CalculateUtil {
private static final int PERCENTAGE = 100;
public static int divide(int num1, int num2) { public static int divide(int num1, int num2) {
return ((int) (Double.parseDouble(num1 + "") / Double.parseDouble(num2 + "") * 100)); return ((int) (Double.parseDouble(num1 + "") / Double.parseDouble(num2 + "") * PERCENTAGE));
} }
} }

@ -17,10 +17,8 @@
package cn.hippo4j.common.toolkit; package cn.hippo4j.common.toolkit;
import java.util.Collection; import java.util.*;
import java.util.Iterator; import java.util.stream.Collectors;
import java.util.List;
import java.util.Map;
/** /**
* Collection util. * Collection util.
@ -35,11 +33,12 @@ public class CollectionUtil {
* @return * @return
*/ */
public static <T> T getFirst(Iterable<T> iterable) { public static <T> T getFirst(Iterable<T> iterable) {
Iterator<T> iterator; if (iterable != null) {
if (iterable != null && (iterator = iterable.iterator()) != null && iterator.hasNext()) { Iterator<T> iterator = iterable.iterator();
if (iterator != null && iterator.hasNext()) {
return iterator.next(); return iterator.next();
} }
}
return null; return null;
} }
@ -90,7 +89,7 @@ public class CollectionUtil {
* @return * @return
*/ */
public static boolean isEmpty(Iterator<?> iterator) { public static boolean isEmpty(Iterator<?> iterator) {
return null == iterator || false == iterator.hasNext(); return null == iterator || !iterator.hasNext();
} }
/** /**
@ -122,4 +121,57 @@ public class CollectionUtil {
public static boolean isNotEmpty(Collection<?> collection) { public static boolean isNotEmpty(Collection<?> collection) {
return !isEmpty(collection); return !isEmpty(collection);
} }
/**
* to list
*
* @param ts elements
* @param <T> type
* @return List
*/
public static <T> List<T> toList(T... ts) {
if (ts == null || ts.length == 0) {
return new ArrayList<>();
}
return Arrays.stream(ts)
.collect(Collectors.toList());
}
/**
* reference google guava
*
* @param elements
* @return
*/
@SafeVarargs
public static <E> ArrayList<E> newArrayList(E... elements) {
Objects.requireNonNull(elements);
// Avoid integer overflow when a large array is passed in
int capacity = computeArrayListCapacity(elements.length);
ArrayList<E> list = new ArrayList<>(capacity);
Collections.addAll(list, elements);
return list;
}
private static int computeArrayListCapacity(int arraySize) {
checkNonnegative(arraySize);
// TODO(kevinb): Figure out the right behavior, and document it
return saturatedCast(5L + arraySize + (arraySize / 10));
}
private static void checkNonnegative(int value) {
if (value < 0) {
throw new IllegalArgumentException("arraySize cannot be negative but was: " + value);
}
}
private static int saturatedCast(long value) {
if (value > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
if (value < Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
}
return (int) value;
}
} }

@ -0,0 +1,76 @@
/*
* 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 lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
/**
* date and time util
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DateUtil {
/**
* get time zone for this JVM
*/
private static final TimeZone TIME_ZONE = TimeZone.getDefault();
public static final String NORM_DATE_PATTERN = "yyyy-MM-dd";
public static final String NORM_TIME_PATTERN = "HH:mm:ss";
public static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
/**
* Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
* represented by this <tt>Date</tt> object.
*
* @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
* represented by this date.
*/
public static long getTime(LocalDateTime date) {
return getTime(date, TIME_ZONE.toZoneId());
}
public static long getTime(LocalDateTime date, ZoneId zoneId) {
return date.atZone(zoneId).toInstant().toEpochMilli();
}
/**
* modify format to date
*
* @param date date
* @param normTimePattern PATTERN
* @return String
*/
public static String format(Date date, String normTimePattern) {
SimpleDateFormat zoneFormat = new SimpleDateFormat(normTimePattern);
return zoneFormat.format(date);
}
}

@ -17,18 +17,23 @@
package cn.hippo4j.common.toolkit; package cn.hippo4j.common.toolkit;
import cn.hippo4j.common.web.exception.IllegalException;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import java.io.BufferedInputStream; import java.io.*;
import java.io.ByteArrayOutputStream; import java.nio.charset.Charset;
import java.io.InputStream; import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/** /**
* File util; * File util;
*/ */
public class FileUtil { public class FileUtil {
private static final int ERROR_CODE = -1;
@SneakyThrows @SneakyThrows
public static String readUtf8String(String path) { public static String readUtf8String(String path) {
String resultReadStr; String resultReadStr;
@ -38,7 +43,7 @@ public class FileUtil {
BufferedInputStream bis = new BufferedInputStream(inputStream); BufferedInputStream bis = new BufferedInputStream(inputStream);
ByteArrayOutputStream buf = new ByteArrayOutputStream()) { ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
int result = bis.read(); int result = bis.read();
while (result != -1) { while (result != ERROR_CODE) {
buf.write((byte) result); buf.write((byte) result);
result = bis.read(); result = bis.read();
} }
@ -46,4 +51,38 @@ public class FileUtil {
} }
return resultReadStr; return resultReadStr;
} }
public static List<String> readLines(String path, Charset charset) {
List<String> strList = new ArrayList<>();
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
ClassPathResource classPathResource = new ClassPathResource(path);
try {
inputStreamReader = new InputStreamReader(classPathResource.getInputStream(), charset);
bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
strList.add(line);
}
} catch (IOException e) {
e.printStackTrace();
throw new IllegalException("file read error");
} finally {
if (inputStreamReader != null) {
try {
inputStreamReader.close();
} catch (IOException e) {
throw new IllegalException("file read error");
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
throw new IllegalException("file read error");
}
}
}
return strList;
}
} }

@ -0,0 +1,85 @@
/*
* 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 lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.UUID;
/**
* id and uuid util{@link UUID}
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IdUtil {
/**
* get a random UUID
*
* @return UUID
*/
public static String randomUUID() {
return toString(UUID.randomUUID(), false);
}
/**
* get a simple random UUID
*
* @return a simple UUID
*/
public static String simpleUUID() {
return toString(UUID.randomUUID(), true);
}
/**
* toString
*
* @param uuid UUID
* @return UUID String
*/
public static String toString(UUID uuid, boolean isSimple) {
long mostSigBits = uuid.getMostSignificantBits();
long leastSigBits = uuid.getLeastSignificantBits();
if (isSimple) {
return (digits(mostSigBits >> 32, 8) +
digits(mostSigBits >> 16, 4) +
digits(mostSigBits, 4) +
digits(leastSigBits >> 48, 4) +
digits(leastSigBits, 12));
} else {
return (digits(mostSigBits >> 32, 8) + "-" +
digits(mostSigBits >> 16, 4) + "-" +
digits(mostSigBits, 4) + "-" +
digits(leastSigBits >> 48, 4) + "-" +
digits(leastSigBits, 12));
}
}
/**
* Returns val represented by the specified number of hex digits. <br>
* {@link UUID#digits(long, int)}
*
* @param val value
* @param digits position
* @return hex value
*/
private static String digits(long val, int digits) {
long hi = 1L << (digits * 4);
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
}
}

@ -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 IoUtil {
/**
* 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(IoUtil::closeQuietly);
}
}

@ -56,4 +56,11 @@ public class JSONUtil {
} }
return JSON_FACADE.parseArray(text, clazz); 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 cn.hippo4j.common.api.JsonFacade;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.databind.type.CollectionType;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@ -74,4 +72,14 @@ public class JacksonHandler implements JsonFacade {
CollectionType collectionType = MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, clazz); CollectionType collectionType = MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, clazz);
return MAPPER.readValue(text, collectionType); return MAPPER.readValue(text, collectionType);
} }
@Override
public boolean isJson(String text) {
try {
MAPPER.readTree(text);
return true;
} catch (JsonProcessingException ignored) {
}
return false;
}
} }

@ -0,0 +1,88 @@
/*
* 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.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Objects;
/**
* reference google guava
*/
public class Joiner {
private final String separator;
private Joiner(String separator) {
this.separator = Objects.requireNonNull(separator);
}
/**
* Returns a joiner which automatically places {@code separator} between consecutive elements.
*/
public static Joiner on(String separator) {
return new Joiner(separator);
}
/**
* Returns a string containing the string representation of each of {@code parts}, using the
* previously configured separator between each.
*/
public final String join(Object[] parts) {
return join(Arrays.asList(parts));
}
public final String join(Iterable<?> parts) {
return join(parts.iterator());
}
/**
* Returns a string containing the string representation of each of {@code parts}, using the
* previously configured separator between each.
*/
public final String join(Iterator<?> parts) {
return appendTo(new StringBuilder(), parts).toString();
}
public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
try {
appendTo((Appendable) builder, parts);
} catch (IOException impossible) {
throw new AssertionError(impossible);
}
return builder;
}
public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
Objects.requireNonNull(appendable);
if (parts.hasNext()) {
appendable.append(toString(parts.next()));
while (parts.hasNext()) {
appendable.append(separator);
appendable.append(toString(parts.next()));
}
}
return appendable;
}
CharSequence toString(Object part) {
Objects.requireNonNull(part);
return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
}
}

@ -0,0 +1,167 @@
/*
* 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 util.
*/
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);
}
}

@ -33,6 +33,16 @@ public class Md5Util {
private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static final char XF0 = 0xF0;
private static final char XF = 0x0F;
private static final int DATA_ID_GROUP_ID_THREE_LEN = 3;
private static final int DATA_ID_GROUP_ID_FOUR_LEN = 4;
private static final int DISPLACEMENT = 4;
private static final ThreadLocal<MessageDigest> MESSAGE_DIGEST_LOCAL = ThreadLocal.withInitial(() -> { private static final ThreadLocal<MessageDigest> MESSAGE_DIGEST_LOCAL = ThreadLocal.withInitial(() -> {
try { try {
return MessageDigest.getInstance("MD5"); return MessageDigest.getInstance("MD5");
@ -65,8 +75,8 @@ public class Md5Util {
int l = bytes.length; int l = bytes.length;
char[] out = new char[l << 1]; char[] out = new char[l << 1];
for (int i = 0, j = 0; i < l; i++) { for (int i = 0, j = 0; i < l; i++) {
out[j++] = DIGITS_LOWER[(0xF0 & bytes[i]) >>> 4]; out[j++] = DIGITS_LOWER[(XF0 & bytes[i]) >>> DISPLACEMENT];
out[j++] = DIGITS_LOWER[0x0F & bytes[i]]; out[j++] = DIGITS_LOWER[XF & bytes[i]];
} }
return new String(out); return new String(out);
} }
@ -86,12 +96,12 @@ public class Md5Util {
sb.append(Constants.WORD_SEPARATOR); sb.append(Constants.WORD_SEPARATOR);
sb.append(dataIdGroupId[1]); sb.append(dataIdGroupId[1]);
// if have tenant, then set it // if have tenant, then set it
if (dataIdGroupId.length == 3) { if (dataIdGroupId.length == DATA_ID_GROUP_ID_THREE_LEN) {
if (StringUtil.isNotBlank(dataIdGroupId[2])) { if (StringUtil.isNotBlank(dataIdGroupId[2])) {
sb.append(Constants.WORD_SEPARATOR); sb.append(Constants.WORD_SEPARATOR);
sb.append(dataIdGroupId[2]); sb.append(dataIdGroupId[2]);
} }
} else if (dataIdGroupId.length == 4) { } else if (dataIdGroupId.length == DATA_ID_GROUP_ID_FOUR_LEN) {
if (StringUtil.isNotBlank(dataIdGroupId[2])) { if (StringUtil.isNotBlank(dataIdGroupId[2])) {
sb.append(Constants.WORD_SEPARATOR); sb.append(Constants.WORD_SEPARATOR);
sb.append(dataIdGroupId[2]); sb.append(dataIdGroupId[2]);

@ -41,7 +41,7 @@ public class MessageConvert {
MessageWrapper wrapper = new MessageWrapper(); MessageWrapper wrapper = new MessageWrapper();
wrapper.setResponseClass(message.getClass()); wrapper.setResponseClass(message.getClass());
wrapper.setMessageType(message.getMessageType()); wrapper.setMessageType(message.getMessageType());
List<Map<String, Object>> messageMapList = new ArrayList(); List<Map<String, Object>> messageMapList = new ArrayList<>();
List<Message> messages = message.getMessages(); List<Message> messages = message.getMessages();
messages.forEach(each -> { messages.forEach(each -> {
String eachVal = JSONUtil.toJSONString(each); String eachVal = JSONUtil.toJSONString(each);

@ -17,22 +17,27 @@
package cn.hippo4j.common.toolkit; package cn.hippo4j.common.toolkit;
import cn.hutool.core.convert.Convert; import cn.hippo4j.common.web.exception.IllegalException;
import cn.hutool.core.exceptions.UtilException; import lombok.AccessLevel;
import cn.hutool.core.util.ClassUtil; import lombok.NoArgsConstructor;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
/** /**
* Reflect util. * Reflect util.
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReflectUtil { public class ReflectUtil {
private static final Map<Class<?>, Field[]> FIELDS_CACHE = new ConcurrentHashMap(); private static final Map<Class<?>, Field[]> FIELDS_CACHE = new ConcurrentHashMap<>();
public static Object getFieldValue(Object obj, String fieldName) { public static Object getFieldValue(Object obj, String fieldName) {
if (null == obj || StringUtil.isBlank(fieldName)) { if (null == obj || StringUtil.isBlank(fieldName)) {
@ -55,13 +60,13 @@ public class ReflectUtil {
result = field.get(obj); result = field.get(obj);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
String exceptionMsg = String.format("IllegalAccess for %s.%s", field.getDeclaringClass(), field.getName()); String exceptionMsg = String.format("IllegalAccess for %s.%s", field.getDeclaringClass(), field.getName());
throw new RuntimeException(exceptionMsg, e); throw new IllegalException(exceptionMsg, e);
} }
return result; return result;
} }
public static <T extends AccessibleObject> T setAccessible(T accessibleObject) { public static <T extends AccessibleObject> T setAccessible(T accessibleObject) {
if (null != accessibleObject && false == accessibleObject.isAccessible()) { if (null != accessibleObject && !accessibleObject.isAccessible()) {
accessibleObject.setAccessible(true); accessibleObject.setAccessible(true);
} }
return accessibleObject; return accessibleObject;
@ -69,7 +74,7 @@ public class ReflectUtil {
public static Field getField(Class<?> beanClass, String name) throws SecurityException { public static Field getField(Class<?> beanClass, String name) throws SecurityException {
final Field[] fields = getFields(beanClass); final Field[] fields = getFields(beanClass);
return ArrayUtil.firstMatch((field) -> name.equals(getFieldName(field)), fields); return ArrayUtil.firstMatch(field -> name.equals(getFieldName(field)), fields);
} }
public static Field[] getFields(Class<?> beanClass) throws SecurityException { public static Field[] getFields(Class<?> beanClass) throws SecurityException {
@ -109,32 +114,137 @@ public class ReflectUtil {
return field.getName(); return field.getName();
} }
public static void setFieldValue(Object obj, String fieldName, Object value) throws UtilException { public static void setFieldValue(Object obj, String fieldName, Object value) throws IllegalException {
cn.hutool.core.lang.Assert.notNull(obj); Assert.notNull(obj);
cn.hutool.core.lang.Assert.notBlank(fieldName); Assert.notBlank(fieldName);
final Field field = getField((obj instanceof Class) ? (Class<?>) obj : obj.getClass(), fieldName); final Field field = getField((obj instanceof Class) ? (Class<?>) obj : obj.getClass(), fieldName);
cn.hutool.core.lang.Assert.notNull(field, "Field [{}] is not exist in [{}]", fieldName, obj.getClass().getName()); Assert.notNull(field, "Field [" + fieldName + "] is not exist in [" + obj.getClass().getName() + "]");
setFieldValue(obj, field, value); setFieldValue(obj, field, value);
} }
public static void setFieldValue(Object obj, Field field, Object value) throws UtilException { public static void setFieldValue(Object obj, Field field, Object value) throws IllegalException {
cn.hutool.core.lang.Assert.notNull(field, "Field in [{}] not exist !", obj); Assert.notNull(field, "Field in [" + obj + "] not exist !");
final Class<?> fieldType = field.getType(); final Class<?> fieldType = field.getType();
if (null != value) { if (null != value) {
if (false == fieldType.isAssignableFrom(value.getClass())) { if (!fieldType.isAssignableFrom(value.getClass())) {
final Object targetValue = Convert.convert(fieldType, value); final Object targetValue = cast(fieldType, value);
if (null != targetValue) { if (null != targetValue) {
value = targetValue; value = targetValue;
} }
} }
} else { } else {
value = ClassUtil.getDefaultValue(fieldType); value = getDefaultValue(fieldType);
} }
setAccessible(field); setAccessible(field);
try { try {
field.set(obj instanceof Class ? null : obj, value); field.set(obj instanceof Class ? null : obj, value);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new UtilException(e, "IllegalAccess for {}.{}", obj, field.getName()); throw new IllegalException("IllegalAccess for " + obj + "." + field.getName(), e);
}
}
/**
* find the method associated with the method name<br>
* if find multiple, return the first, parameter is equivocal
*
* @param clazz the class
* @param methodName retrieves the method name
* @return find method
*/
public static Method getMethodByName(Class<?> clazz, String methodName) {
if (Objects.nonNull(clazz) && Objects.nonNull(methodName)) {
Method method = Arrays.stream(clazz.getMethods())
.filter(m -> Objects.equals(m.getName(), methodName))
.findFirst().orElse(null);
if (method != null) {
return method;
}
return Arrays.stream(clazz.getDeclaredMethods())
.filter(m -> Objects.equals(m.getName(), methodName))
.findFirst().orElse(null);
}
return null;
}
/**
* find the method associated with the method name
*
* @param clazz the class
* @param methodName retrieves the method name
* @param arguments matched parameters class
* @return find method
*/
public static Method getMethodByName(Class<?> clazz, String methodName, Class<?>... arguments) {
try {
if (Objects.nonNull(clazz) && Objects.nonNull(methodName)) {
return clazz.getMethod(methodName, arguments);
}
} catch (NoSuchMethodException e) {
throw new IllegalException(e);
}
return null;
}
/**
* Cast the value to the type <br>
* If a ClassCastException occurs, return null
*
* @param clazz Cast class
* @param value The cast value
* @return The value after the cast is completed
*/
public static Object cast(Class<?> clazz, Object value) {
try {
return clazz.cast(value);
} catch (ClassCastException e) {
return null;
}
}
/**
* the default value is obtained if it is a primitive type, and NULL if it is not
*
* @param clazz clazz
* @return default value
*/
public static Object getDefaultValue(Class<?> clazz) {
if (Objects.isNull(clazz) || !clazz.isPrimitive()) {
return null;
}
if (long.class.isAssignableFrom(clazz)) {
return 0L;
} else if (int.class.isAssignableFrom(clazz)) {
return 0;
} else if (short.class.isAssignableFrom(clazz)) {
return (short) 0;
} else if (char.class.isAssignableFrom(clazz)) {
return (char) 0;
} else if (byte.class.isAssignableFrom(clazz)) {
return (byte) 0;
} else if (double.class.isAssignableFrom(clazz)) {
return 0D;
} else if (float.class.isAssignableFrom(clazz)) {
return 0f;
} else if (boolean.class.isAssignableFrom(clazz)) {
return false;
}
return null;
}
/**
* invoke
*
* @param obj the obj
* @param method the method
* @param arguments parameters
* @return result for zhe method
*/
@SuppressWarnings("unchecked")
public static <T> T invoke(Object obj, Method method, Object... arguments) {
try {
return (T) method.invoke(obj, arguments);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalException(e);
} }
} }
} }

@ -29,7 +29,7 @@ import java.util.function.Supplier;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class Singleton { public final class Singleton {
private static final ConcurrentHashMap<String, Object> SINGLE_OBJECT_POOL = new ConcurrentHashMap(); private static final ConcurrentHashMap<String, Object> SINGLE_OBJECT_POOL = new ConcurrentHashMap<>();
/** /**
* Get a singleton object by key. * Get a singleton object by key.
@ -55,9 +55,12 @@ public final class Singleton {
*/ */
public static <T> T get(String key, Supplier<T> supplier) { public static <T> T get(String key, Supplier<T> supplier) {
Object result = SINGLE_OBJECT_POOL.get(key); Object result = SINGLE_OBJECT_POOL.get(key);
if (result == null && (result = supplier.get()) != null) { if (result == null) {
result = supplier.get();
if (result != null) {
SINGLE_OBJECT_POOL.put(key, result); SINGLE_OBJECT_POOL.put(key, result);
} }
}
return result != null ? (T) result : null; return result != null ? (T) result : null;
} }

@ -17,6 +17,9 @@
package cn.hippo4j.common.toolkit; package cn.hippo4j.common.toolkit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* String util. * String util.
*/ */
@ -26,6 +29,8 @@ public class StringUtil {
public static final char UNDERLINE = '_'; public static final char UNDERLINE = '_';
public static final String[] EMPTY_ARRAY = new String[0];
/** /**
* Returns the given string if it is nonempty; {@code null} otherwise. * Returns the given string if it is nonempty; {@code null} otherwise.
* *
@ -49,9 +54,11 @@ public class StringUtil {
/** /**
* Returns {@code true} if the given string is null or is the empty string. * Returns {@code true} if the given string is null or is the empty string.
* *
* this method has been deprecated, use isEmpty() instead.
* @param str * @param str
* @return * @return
*/ */
@Deprecated
public static boolean isNullOrEmpty(String str) { public static boolean isNullOrEmpty(String str) {
return str == null || str.isEmpty(); return str == null || str.isEmpty();
} }
@ -63,14 +70,17 @@ public class StringUtil {
* @return * @return
*/ */
public static boolean isBlank(CharSequence str) { public static boolean isBlank(CharSequence str) {
int length; if ((str == null)) {
if ((str == null) || ((length = str.length()) == 0)) { return true;
}
int length = str.length();
if (length == 0) {
return true; return true;
} }
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
char c = str.charAt(i); char c = str.charAt(i);
boolean charNotBlank = Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\ufeff' || c == '\u202a'; boolean charNotBlank = Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\ufeff' || c == '\u202a';
if (charNotBlank == false) { if (!charNotBlank) {
return false; return false;
} }
} }
@ -104,7 +114,7 @@ public class StringUtil {
* @return * @return
*/ */
public static boolean isNotBlank(CharSequence str) { public static boolean isNotBlank(CharSequence str) {
return isBlank(str) == false; return !isBlank(str);
} }
/** /**
@ -114,7 +124,7 @@ public class StringUtil {
* @return * @return
*/ */
public static boolean isAllNotEmpty(CharSequence... args) { public static boolean isAllNotEmpty(CharSequence... args) {
return false == hasEmpty(args); return !hasEmpty(args);
} }
/** /**
@ -188,4 +198,162 @@ public class StringUtil {
} }
return sb.toString(); return sb.toString();
} }
/**
* to camel case
*
* @param str CharSequence
* @param symbol symbol
* @return toCamelCase String
*/
public static String toCamelCase(CharSequence str, char symbol) {
if (null == str || str.length() == 0) {
return null;
}
int length = str.length();
StringBuilder sb = new StringBuilder(length);
boolean upperCase = false;
for (int i = 0; i < length; ++i) {
char c = str.charAt(i);
if (c == symbol) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* combination CharSequence, get a String
*
* @param charSequences CharSequence, if null or empty, get {@link StringUtil#EMPTY}
* @return String
*/
public static String newBuilder(CharSequence... charSequences) {
if (charSequences == null || charSequences.length == 0) {
return StringUtil.EMPTY;
}
return createBuilder(charSequences).toString();
}
/**
* combination CharSequence, get a StringBuilder
*
* @param charSequences CharSequence
* @return StringBuilder
*/
public static StringBuilder createBuilder(CharSequence... charSequences) {
StringBuilder builder = new StringBuilder();
if (charSequences == null || charSequences.length == 0) {
return builder;
}
for (CharSequence sequence : charSequences) {
builder.append(sequence);
}
return builder;
}
/**
* combination CharSequence, to StringBuilder
*
* @param builder StringBuilder, if null create a new
* @param charSequences CharSequence
* @return StringBuilder
*/
public static StringBuilder appends(StringBuilder builder, CharSequence... charSequences) {
if (builder == null) {
return createBuilder(charSequences);
}
if (charSequences == null || charSequences.length == 0) {
return builder;
}
for (CharSequence sequence : charSequences) {
builder.append(sequence);
}
return builder;
}
/**
* Replace a portion of the string, replacing all found
*
* @param str A string to operate on
* @param searchStr The replaced string
* @param replaceStr The replaced string
* @return Replace the result
*/
public static String replace(String str, String searchStr, String replaceStr) {
return Pattern
.compile(searchStr, Pattern.LITERAL)
.matcher(str)
.replaceAll(Matcher.quoteReplacement(replaceStr));
}
/**
* <p>Splits the provided text into an array, separators specified.
*
* <pre>
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("abc def", null) = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
* </pre>
* @param str the String to parse, may be null
* @param separatorChars the characters used as the delimiters,
* @return an array of parsed Strings
*/
public static String[] split(final String str, final String separatorChars) {
if (str == null) {
return null;
}
if (isBlank(str)) {
return EMPTY_ARRAY;
}
if (isBlank(separatorChars)) {
return str.split(" ");
}
return str.split(separatorChars);
}
/**
* Tests if this string starts with the specified prefix.
*
* @param str this str
* @param prefix the suffix
* @return Whether the prefix exists
*/
public static boolean startWith(String str, String prefix) {
if (isEmpty(str)) {
return false;
}
return str.startsWith(prefix);
}
/**
* get the string before the delimiter
*
* @param str string
* @param symbol separator
* @return String
*/
public static String subBefore(String str, String symbol) {
if (isEmpty(str) || symbol == null) {
return str;
}
if (symbol.isEmpty()) {
return EMPTY;
}
int pos = str.indexOf(symbol);
if (-1 == pos) {
return str;
}
if (0 == pos) {
return EMPTY;
}
return str.substring(0, pos);
}
} }

@ -28,7 +28,7 @@ import java.util.Optional;
*/ */
public class UserContext { public class UserContext {
private static final ThreadLocal<User> USER_THREAD_LOCAL = new ThreadLocal(); private static final ThreadLocal<User> USER_THREAD_LOCAL = new ThreadLocal<>();
public static void setUserInfo(String username, String userRole) { public static void setUserInfo(String username, String userRole) {
USER_THREAD_LOCAL.set(new User(username, userRole)); USER_THREAD_LOCAL.set(new User(username, userRole));
@ -46,6 +46,9 @@ public class UserContext {
USER_THREAD_LOCAL.remove(); USER_THREAD_LOCAL.remove();
} }
/**
* User info.
*/
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor

@ -0,0 +1,199 @@
/*
* 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.HttpHeaderConstants;
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(HttpHeaderConstants.CONTENT_TYPE, HttpMediaType.APPLICATION_JSON);
addParam(HttpHeaderConstants.ACCEPT_CHARSET, DEFAULT_CHARSET);
}
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(HttpHeaderConstants.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(HttpHeaderConstants.ACCEPT_CHARSET);
if (acceptCharset == null) {
String contentType = getValue(HttpHeaderConstants.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,70 @@
/*
* 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,360 @@
/*
* 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.CollectionUtil;
import cn.hippo4j.common.toolkit.IoUtil;
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.AccessLevel;
import lombok.NoArgsConstructor;
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 java.util.Optional;
import static cn.hippo4j.common.constant.HttpHeaderConstants.CONTENT_LENGTH;
/**
* Http request utilities.
*
* @author Rongzhen Yan
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class HttpUtil {
/**
* Default connect timeout
*/
private static final int DEFAULT_CONNECT_TIMEOUT = 10000;
/**
* Default read timeout
*/
private static final int DEFAULT_READ_TIMEOUT = 30000;
/**
* Send a get network request.
*
* @param url target url
* @param headers headers
* @param params form data
* @param timeout request timeout
* @param clazz return the target data type
* @param <T> return the target data type
* @return
*/
public static <T> T get(String url, Map<String, String> headers, Map<String, String> params, long timeout, Class<T> clazz) {
return execute(buildUrl(url, params), HttpMethod.GET, null, headers, timeout, clazz);
}
/**
* Send a get network request.
*
* @param url target url
* @param params form data
* @return
*/
public static String get(String url, Map<String, String> params) {
return execute(buildUrl(url, params), HttpMethod.GET, null, null);
}
/**
* Send a get network request.
*
* @param url target url
* @return
*/
public static String get(String url) {
return execute(url, HttpMethod.GET, null, null);
}
/**
* Send a get network request.
*
* @param url target url
* @param clazz return the target data type
* @param <T> return the target data type
* @return
*/
public static <T> T get(String url, Class<T> clazz) {
return JSONUtil.parseObject(get(url), clazz);
}
/**
* Send a post network request.
*
* @param url target url
* @param body request body
* @param clazz return the target data type
* @param <T> return the target data type
* @return
*/
public static <T> T post(String url, Object body, Class<T> clazz) {
String result = post(url, body);
return JSONUtil.parseObject(result, clazz);
}
/**
* Send a post network request.
*
* @param url target url
* @param body request body
* @param timeout request timeout
* @param clazz return the target data type
* @param <T> return the target data type
* @return
*/
public static <T> T post(String url, Object body, long timeout, Class<T> clazz) {
String result = post(url, body, timeout);
return JSONUtil.parseObject(result, clazz);
}
/**
* Send a post network request.
*
* @param url target url
* @param headers headers
* @param params form data
* @param timeout request timeout
* @param clazz return the target data type
* @param <T> return the target data type
* @return
*/
public static <T> T post(String url, Map<String, String> headers, Map<String, String> params, long timeout, Class<T> clazz) {
return execute(buildUrl(url, params), HttpMethod.POST, null, headers, timeout, clazz);
}
/**
* Send a post network request.
*
* @param url target url
* @param headers headers
* @param body request body
* @param timeout request timeout
* @param clazz return the target data type
* @param <T> return the target data type
* @return
*/
public static <T> T post(String url, Map<String, String> headers, Object body, long timeout, Class<T> clazz) {
return execute(url, HttpMethod.POST, body, headers, timeout, clazz);
}
/**
* Send a post network request.
*
* @param url target url
* @param body request body
* @return
*/
public static String post(String url, Object body) {
return execute(url, HttpMethod.POST, body, null);
}
/**
* Send a post network request.
*
* @param url target url
* @param body request body
* @param timeout request timeout
* @return
*/
public static String post(String url, Object body, long timeout) {
return execute(url, HttpMethod.POST, body, null, timeout, String.class);
}
/**
* Send a post network request.
*
* @param url target url
* @param json json data
* @return
*/
public static String postJson(String url, String json) {
return executeJson(url, HttpMethod.POST, json, null);
}
/**
* Send a put network request.
*
* @param url target url
* @param body request body
* @return
*/
public static String put(String url, Object body) {
return execute(url, HttpMethod.PUT, body, null);
}
/**
* Send a put network request.
*
* @param url target url
* @param body request body
* @param headers headers
* @return
*/
public static String put(String url, Object body, Map<String, String> headers) {
return execute(url, HttpMethod.PUT, body, headers);
}
/**
* Constructs a complete Url from the query string.
*
* @param url target url
* @param queryParams query params
* @return
*/
@SneakyThrows
public static String buildUrl(String url, Map<String, String> queryParams) {
if (CollectionUtil.isEmpty(queryParams)) {
return url;
}
boolean isFirst = true;
StringBuilder builder = new StringBuilder(url);
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
String key = entry.getKey();
if (key != null && entry.getValue() != null) {
if (isFirst) {
isFirst = false;
builder.append("?");
} else {
builder.append("&");
}
String value = URLEncoder.encode(queryParams.get(key), Constants.ENCODE)
.replaceAll("\\+", "%20");
builder.append(key)
.append("=")
.append(value);
}
}
return builder.toString();
}
private static String 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);
}
private static String execute(String url, String method, Object param, Map<String, String> headers) {
HttpURLConnection connection = createConnection(url, method);
HttpClientResponse response = null;
try {
response = doExecute(connection, param, headers);
return response.getBodyString();
} finally {
Optional.ofNullable(response).ifPresent(each -> each.close());
}
}
private static <T> T execute(String url, String method, Object body, Map<String, String> headers, long timeout, Class<T> clazz) {
HttpURLConnection connection = createConnection(url, method, timeout);
HttpClientResponse response = null;
try {
response = doExecute(connection, body, headers);
if (clazz == String.class) {
return (T) response.getBodyString();
}
return JSONUtil.parseObject(response.getBodyString(), clazz);
} finally {
Optional.ofNullable(response).ifPresent(each -> each.close());
}
}
@SneakyThrows
private static HttpClientResponse doExecute(HttpURLConnection connection, Object body, Map<String, String> headers) {
try {
if (headers != null && headers.size() > 0) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
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();
IoUtil.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 ex) {
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)), ex);
throw ex;
}
}
@SneakyThrows
private static HttpURLConnection createConnection(String url, String method) {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
connection.setReadTimeout(DEFAULT_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;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save