Showing preview only (314K chars total). Download the full file or copy to clipboard to get everything.
Repository: all4you/redant
Branch: master
Commit: 38f32e1bd310
Files: 118
Total size: 254.7 KB
Directory structure:
gitextract_2l6xrivj/
├── .gitignore
├── LICENSE
├── README.md
├── md/
│ ├── interceptor.md
│ ├── overview.md
│ └── processor.md
├── pom.xml
├── redant-cluster/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── redant/
│ └── cluster/
│ ├── bootstrap/
│ │ ├── MasterServerBootstrap.java
│ │ ├── SlaveServerBootstrap.java
│ │ └── ZkBootstrap.java
│ ├── master/
│ │ ├── MasterServer.java
│ │ ├── MasterServerBackendHandler.java
│ │ └── MasterServerHandler.java
│ ├── node/
│ │ └── Node.java
│ ├── service/
│ │ ├── discover/
│ │ │ ├── ServiceDiscover.java
│ │ │ └── ZkServiceDiscover.java
│ │ └── register/
│ │ ├── ServiceRegister.java
│ │ └── ZkServiceRegister.java
│ ├── slave/
│ │ └── SlaveServer.java
│ └── zk/
│ ├── ZkClient.java
│ ├── ZkConfig.java
│ ├── ZkNode.java
│ └── ZkServer.java
├── redant-core/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── redant/
│ │ │ └── core/
│ │ │ ├── ServerBootstrap.java
│ │ │ ├── anno/
│ │ │ │ └── Order.java
│ │ │ ├── aware/
│ │ │ │ ├── Aware.java
│ │ │ │ └── BeanContextAware.java
│ │ │ ├── bean/
│ │ │ │ ├── BaseBean.java
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── Autowired.java
│ │ │ │ │ └── Bean.java
│ │ │ │ └── context/
│ │ │ │ ├── BeanContext.java
│ │ │ │ └── DefaultBeanContext.java
│ │ │ ├── common/
│ │ │ │ ├── constants/
│ │ │ │ │ └── CommonConstants.java
│ │ │ │ ├── enums/
│ │ │ │ │ ├── ContentType.java
│ │ │ │ │ └── RequestMethod.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── InvalidSessionException.java
│ │ │ │ │ ├── InvocationException.java
│ │ │ │ │ └── ValidationException.java
│ │ │ │ ├── html/
│ │ │ │ │ ├── DefaultHtmlMaker.java
│ │ │ │ │ ├── HtmlMaker.java
│ │ │ │ │ ├── HtmlMakerEnum.java
│ │ │ │ │ └── HtmlMakerFactory.java
│ │ │ │ ├── util/
│ │ │ │ │ ├── GenericsUtil.java
│ │ │ │ │ ├── HtmlContentUtil.java
│ │ │ │ │ ├── HttpRenderUtil.java
│ │ │ │ │ ├── HttpRequestUtil.java
│ │ │ │ │ ├── PropertiesUtil.java
│ │ │ │ │ ├── TagUtil.java
│ │ │ │ │ └── ThreadUtil.java
│ │ │ │ └── view/
│ │ │ │ ├── HtmlKeyHolder.java
│ │ │ │ ├── Page404.java
│ │ │ │ ├── Page500.java
│ │ │ │ ├── PageError.java
│ │ │ │ └── PageIndex.java
│ │ │ ├── context/
│ │ │ │ └── RedantContext.java
│ │ │ ├── controller/
│ │ │ │ ├── ControllerProxy.java
│ │ │ │ ├── ProxyInvocation.java
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── Controller.java
│ │ │ │ │ ├── Mapping.java
│ │ │ │ │ └── Param.java
│ │ │ │ └── context/
│ │ │ │ ├── ControllerContext.java
│ │ │ │ └── DefaultControllerContext.java
│ │ │ ├── converter/
│ │ │ │ ├── AbstractConverter.java
│ │ │ │ ├── Converter.java
│ │ │ │ ├── PrimitiveConverter.java
│ │ │ │ └── PrimitiveTypeUtil.java
│ │ │ ├── cookie/
│ │ │ │ ├── CookieManager.java
│ │ │ │ └── DefaultCookieManager.java
│ │ │ ├── executor/
│ │ │ │ ├── AbstractExecutor.java
│ │ │ │ ├── Executor.java
│ │ │ │ └── HttpResponseExecutor.java
│ │ │ ├── handler/
│ │ │ │ ├── ControllerDispatcher.java
│ │ │ │ └── ssl/
│ │ │ │ └── SslContextHelper.java
│ │ │ ├── init/
│ │ │ │ ├── InitExecutor.java
│ │ │ │ ├── InitFunc.java
│ │ │ │ └── InitOrder.java
│ │ │ ├── interceptor/
│ │ │ │ ├── Interceptor.java
│ │ │ │ ├── InterceptorBuilder.java
│ │ │ │ ├── InterceptorHandler.java
│ │ │ │ └── InterceptorProvider.java
│ │ │ ├── render/
│ │ │ │ └── RenderType.java
│ │ │ ├── router/
│ │ │ │ ├── BadClientSilencer.java
│ │ │ │ ├── MethodlessRouter.java
│ │ │ │ ├── OrderlessRouter.java
│ │ │ │ ├── PathPattern.java
│ │ │ │ ├── RouteResult.java
│ │ │ │ ├── Router.java
│ │ │ │ └── context/
│ │ │ │ ├── DefaultRouterContext.java
│ │ │ │ └── RouterContext.java
│ │ │ ├── server/
│ │ │ │ ├── NettyHttpServer.java
│ │ │ │ ├── NettyHttpServerInitializer.java
│ │ │ │ └── Server.java
│ │ │ └── session/
│ │ │ ├── HttpSession.java
│ │ │ ├── SessionConfig.java
│ │ │ ├── SessionHelper.java
│ │ │ └── SessionManager.java
│ │ └── resources/
│ │ ├── logback.xml
│ │ ├── redant.properties
│ │ └── zk.cfg
│ └── test/
│ └── java/
│ └── com/
│ └── redant/
│ └── core/
│ └── context/
│ └── RedantContextTest.java
└── redant-example/
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── redant/
│ │ └── example/
│ │ ├── bootstrap/
│ │ │ ├── cluster/
│ │ │ │ ├── MasterServerBootstrap.java
│ │ │ │ ├── SlaveServerBootstrap.java
│ │ │ │ └── ZkBootstrap.java
│ │ │ └── standalone/
│ │ │ └── ServerBootstrap.java
│ │ ├── controller/
│ │ │ ├── BaseController.java
│ │ │ ├── CookieController.java
│ │ │ └── UserController.java
│ │ ├── interceptor/
│ │ │ ├── BlockInterceptor.java
│ │ │ ├── CustomInterceptorBuilder.java
│ │ │ └── PerformanceInterceptor.java
│ │ └── service/
│ │ ├── UserBean.java
│ │ ├── UserService.java
│ │ └── UserServiceImpl.java
│ └── resources/
│ └── logback.xml
└── test/
└── java/
└── com/
└── lememo/
└── core/
└── interceptor/
└── InterceptorProviderTest.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
# OS generated files #
######################
.DS_Store*
ehthumbs.db
Icon?
Thumbs.db
# Editor Files #
################
*~
*.swp
# Gradle Files #
################
.gradle
.m2
# Build output directies
target/
build/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
# IntelliJ specific files/directories
out
.idea
*.ipr
*.iws
*.iml
atlassian-ide-plugin.xml
# Eclipse specific files/directories
.classpath
.project
.settings
.metadata
# NetBeans specific files/directories
.nbattrs
nbproject/private/
build/
nbbuild/
dist/
nbdist/
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "{}" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright 2017 all4you
Licensed 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.
================================================
FILE: README.md
================================================
# RedAnt 项目
**RedAnt** 是一个基于 Netty 的轻量级 Web 容器
**特性:**
- [x] **IOC容器** : 通过 @Bean 注解可以管理所有对象,通过 @Autowired 注解进行对象注入
- [x] **自定义路由** : 通过 @Controller @Mapping @Param 注解可以自定义路由
- [x] **自动参数转换** : 通过 TypeConverter 接口,http 参数会被转成对象(支持基本类型,Map,List,JavaBean)
- [x] **结果渲染** : 支持对结果进行渲染,支持 html, xml, plain, json 格式
- [x] **Cookie管理** : 内置一个 Cookie 管理器
- [x] **前置后置拦截器** :支持前置拦截器与后置拦截器
- [x] **单机模式** : 支持单机模式
- [x] **集群模式** : 支持集群模式
- [x] **服务注册与发现** :实现了一个基于 Zk 的服务注册与发现,来支持多节点模式
- [ ] **Session管理** : 因为涉及到多节点模式,分布式 session 暂未实现
## 快速启动
### 1.单机模式
Redant 是一个基于 Netty 的 Web 容器,类似 Tomcat 和 WebLogic 等容器
只需要启动一个 Server,默认的实现类是 NettyHttpServer 就能快速启动一个 web 容器了,如下所示:
``` java
public final class ServerBootstrap {
public static void main(String[] args) {
Server nettyServer = new NettyHttpServer();
// 各种初始化工作
nettyServer.preStart();
// 启动服务器
nettyServer.start();
}
}
```
### 2.集群模式
到目前为止,我描述的都是单节点模式,如果哪一天单节点的性能无法满足了,那就需要使用集群了,所以我也实现了集群模式。
集群模式是由一个主节点和若干个从节点构成的。主节点接收到请求后,将请求转发给从节点来处理,从节点把处理好的结果返回给主节点,由主节点把结果响应给请求。
要想实现集群模式需要有一个服务注册和发现的功能,目前是借助于 Zk 来做的服务注册与发现。
#### 准备一个 Zk 服务端
因为主节点需要把请求转发给从节点,所以主节点需要知道目前有哪些从节点,我通过 ZooKeeper 来实现服务注册与发现。
如果你没有可用的 Zk 服务端的话,那你可以通过运行下面的 Main 方法来启动一个 ZooKeeper 服务端:
``` java
public final class ZkBootstrap {
private static final Logger LOGGER = LoggerFactory.getLogger(ZkBootstrap.class);
public static void main(String[] args) {
try {
ZkServer zkServer = new ZkServer();
zkServer.startStandalone(ZkConfig.DEFAULT);
}catch (Exception e){
LOGGER.error("ZkBootstrap start failed,cause:",e);
System.exit(1);
}
}
}
```
这样你就可以在后面启动主从节点的时候使用这个 Zk 了。但是这并不是必须的,如果你已经有一个正在运行的 Zk 的服务端,那么你可以在启动主从节点的时候直接使用它,通过在 main 方法的参数中指定 Zk 的地址即可。
#### 启动主节点
只需要运行下面的代码,就可以启动一个主节点了:
``` java
public class MasterServerBootstrap {
public static void main(String[] args) {
String zkAddress = ZkServer.getZkAddressArgs(args,ZkConfig.DEFAULT);
// 启动MasterServer
Server masterServer = new MasterServer(zkAddress);
masterServer.preStart();
masterServer.start();
}
}
```
如果在 main 方法的参数中指定了 Zk 的地址,就通过该地址去进行服务发现,否则会使用默认的 Zk 地址
#### 启动从节点
只需要运行下面的代码,就可以启动一个从节点了:
``` java
public class SlaveServerBootstrap {
public static void main(String[] args) {
String zkAddress = ZkServer.getZkAddressArgs(args,ZkConfig.DEFAULT);
Node node = Node.getNodeWithArgs(args);
// 启动SlaveServer
Server slaveServer = new SlaveServer(zkAddress,node);
slaveServer.preStart();
slaveServer.start();
}
}
```
如果在 main 方法的参数中指定了 Zk 的地址,就通过该地址去进行服务注册,否则会使用默认的 Zk 地址
## 例子
你可以运行 redant-example 模块中提供的例子来体验一下,example 模块中内置了两个 Controller 。
启动完之后,你可以在浏览器中访问 http://127.0.0.1:8888 来查看具体的效果 (默认的端口可以在 redant.properties 中修改)
如果你看到了这样的消息:"Welcome to redant!" 这就意味着你已经启动成功了。
在 redant-example 模块中,内置了以下几个默认的路由:
| 方法类型 | URL | 响应类型 |
| ----------------- | ---------------------------- | ----------------------------- |
| GET | / | HTML |
| \* | \* | HTML |
| GET | /user/count | JSON |
| GET | /user/list | JSON |
| GET | /user/info | JSON |
## Bean 管理器
跟 Spring 一样,你可以通过 @Bean 注解来管理所有的对象,通过 @Autowired 来自动注入
**Tips:** 更多信息请查看wiki: [Bean][1]
## 自定义路由
跟 Spring 一样,你可以通过 @Controller 来自定义一个 Controller.
@Mapping 注解用在方法级别,@Controller + @Mapping 唯一定义一个 http 请求。
@Param 注解用在方法的参数上。通过该注解可以自动将基本类型转成 POJO 对象。
**Tips:** 更多信息请查看wiki: [Router][2]
## Cookie 管理器
Cookie 管理器可以管理用户自定义的 Cookie。
**Tips:** 更多信息请查看wiki: [Cookie][4]
## 联系我
> wh_all4you#hotmail.com

[1]: https://github.com/all4you/redant/wiki/1:Bean
[2]: https://github.com/all4you/redant/wiki/2:Router
[3]: https://github.com/all4you/redant/wiki/3:Session
[4]: https://github.com/all4you/redant/wiki/4:Cookie
================================================
FILE: md/interceptor.md
================================================
# 用责任链模式设计拦截器
我在 Redant(https://github.com/all4you/redant) 中通过继承 ChannelHandler 实现了拦截器的功能,并且 pipeline 就是一种责任链模式的应用。但是我后面对原本的拦截器进行了重新设计,为什么这样做呢,因为原本的方式是在 ChannelHandler 的基础上操作的,而我们知道 Netty 的数据处理都是基于 ByteBuf 的,这就涉及到引用计数释放的问题,前面的 ChannelHandler 在处理时可以不关心引用计数的问题,而交给最后一个 ChannelHandler 去释放。
但是拦截器的一大特性就是当某个条件不满足时需要中断后面的操作直接返回,所以这就造成了在 pipeline 中某个节点需要释放引用计数,另外一个方面就是原先的设计使用了很多自定义的 ChannelHandler,有的只做了一些简单的工作,所以完全可以对他们进行合并,使代码变得更加精简紧凑。
合并多个 ChannelHandler 是比较简单的,重新设计拦截器相对就复杂一些了。
## 重新设计拦截器
首先我把原本的前置拦截器和后置拦截器统一成一个拦截器,然后抽象出两个方法,分别表示:前置处理,后置处理,如下图所示:

默认前置处理的方法返回 true,用户可以根据他们的业务进行覆盖。
这里是定义了一个抽象类,也可以用接口,java 8 开始接口中可以有默认方法实现。
拦截器定义好之后,现在就可以在 ChannelHandler 中加入拦截器的方法调用了,如下图所示:

当前置方法返回 false 时,直接返回,中断后面的业务逻辑处理,最终会到 finally 中将结果写入 response 中返回给前端。
现在只要实现 InterceptorHandler 中的两个方法就可以了,其实这也很简单,只要获取到所有的 Interceptor 的实现类,然后依次调用这些实现类的前置方法和后置方法就好了,如下图所示:

## 获取拦截器
现在的重点就是怎样获取到所有的拦截器,首先可以想到的是通过扫描的方法,找到所有 Interceptor 的实现类,然后将这些实现类加入到一个 List 中即可。
那怎么保证拦截器的执行顺序呢,很简单,只要在加入 List 之前对他们进行排序就可以了。再定义一个 @Order 注解来表示排序的顺序,然后用一个 Wrapper 包装类将 Interceptor 和 Order 包装起来,排序到包装类的 List 中,最后再从包装类的 List 中依次取出所有的 Interceptor 就完成了 Interceptor 的排序了。
知道了大致的原理之后,实现起来就很简单了,如下图所示:

但是我们不能每次都通过调用 scanInterceptors() 方法来获取所有的拦截器,如果这样每次都扫描一次的话性能会有影响,所以我们只需要第一次调用一下该方法,然后把结果保存在一个私有的变量中,获取的时候直接读取该变量的值即可,如下图所示:

## 自定义拦截器实现类
下面让我们来自定义两个拦截器实现类,来验证下具体的效果。
第一个拦截器,在前置方法中对请求参数进行判断,如果请求参数中有 block=true 的参数,则进行拦截,如下图所示:

第二个拦截器,在后置方法中打印出每次请求的耗时,如下图所示:

通过 @Order 注解来指定执行的顺序,先执行 BlockInterceptor 再执行 PerformanceInterceptor。
## 查看效果
现在我们请求 /user/info 这个接口,查看下效果。
首先我们只提交正常的参数,如下图所示:

打印的结果如下图所示:

从打印的结果中可以看到依次执行了:
- BlockInterceptor 的 preHandle 方法
- PerformanceInterceptor 的 preHandle方法
- BlockInterceptor 的 postHandle 方法
- PerformanceInterceptor 的 postHandle方法
这说明拦截器是按照 @Order 注解进行了排序,然后依次执行的。
然后我们再提交一个 block=true 的参数,再次请求该接口,如下图所示:

可以看到该请求已经被拦截器的前置方法给拦截了,再看下打印的日志,如下图所示:

只打印了 BlockInterceptor 的 preHandler 方法中的部分日志,后面的方法都没有执行,因为被拦截了直接返回了。
## 存在的问题
到这里已经对拦截器完成了改造,并且也验证了效果,看上去效果还可以。但是有没有什么问题呢?
还真有一个问题:所有的 Interceptor 实现类只要被扫描到了,就会被加入到 List 中去,如果不想应用某一个拦截器这时就做不到了,因为无法对 list 中的值进行动态的更改。
如果我们可以动态的获取一个保存了 Interceptor 的 list ,如果该 list 中没有获取到值,再通过扫描的方式去拿到所有的 Interceptor 这样就完美了。
动态获取 Interceptor 的 list 的方法,可以由用户自定义实现,根据某些规则来确定要不要将某个 Interceptor 加入到 list 中去,这样就把 Interceptor 的实现和使用进行了解耦了。用户可以实现任意多的 Interceptor,但是只根据规则去使用其中的某些 Interceptor。
理清楚了原理之后,就很好实现了,首先定义一个接口,用来构造 Interceptor 的 List,如下图所示:

有了 InterceptorBuilder 之后,在获取 Interceptor 的时候,就可以先根据 InterceptorBuilder 来获取了,如下图所示:

以下是一个示例的 InterceptorBuilder,具体的可以用户自行扩展设计,如下图所示:

这样用户只要实现一个 InterceptorBuilder 接口,即可按照自己的意图去组装所有的拦截器。
================================================
FILE: md/overview.md
================================================
**RedAnt** 是一个基于 Netty 的轻量级 Web 容器,创建这个项目的目的主要是学习使用 Netty,俗话说不要轻易的造轮子,但是通过造轮子我们可以学到很多优秀开源框架的设计思路,编写优美的代码,更好的提升自己。

## 快速启动
Redant 是一个基于 Netty 的 Web 容器,类似 Tomcat 和 WebLogic 等容器
只需要启动一个 Server,默认的实现类是 NettyHttpServer 就能快速启动一个 web 容器了,如下所示:
```java
public final class ServerBootstrap {
public static void main(String[] args) {
Server nettyServer = new NettyHttpServer();
// 各种初始化工作
nettyServer.preStart();
// 启动服务器
nettyServer.start();
}
}
```
我们可以直接启动 redant-example 模块中的 ServerBootstrap 类,因为 redant-example 中有很多示例的 Controller,我们直接运行 example 中的 ServerBootstrap,启动后你会看到如下的日志信息:

在 redant-example 模块中,内置了以下几个默认的路由:
| 方法类型 | URL | 响应类型 |
| -------- | ----------- | -------- |
| GET | / | HTML |
| \* | \* | HTML |
| GET | /user/count | JSON |
| GET | /user/list | JSON |
| GET | /user/info | JSON |
启动成功后,可以访问 http://127.0.0.1:8888/ 查看效果,如下图所示:

如果你可以看到 "Welcome to redant!" 这样的消息,那就说明你启动成功了。
## 自定义路由
框架实现了自定义路由,通过 @Controller @Mapping 注解就可以唯一确定一个自定义路由。如下列的 UserController 所示:

和 Spring 的使用方式一样,访问 /user/list 来看下效果,如下图所示:

## 结果渲染
目前支持 json、html、xml、text 等类型的结果渲染,用户只需要在 方法的 @Mapping 注解上通过 renderType 来指定具体的渲染类型即可,如果不指定的话,默认以 json 类型范围。
如下图所示,首页就是通过指定 renderType 为 html 来返回一个 html 页面的:

## IOC容器
从 UserController 的代码中,我们看到 userServerce 对象是通过 @Autowired 注解自动注入的,这个功能是任何一个 IOC 容器基本的能力,下面我们来看看如何实现一个简单的 IOC 容器。
首先定义一个 BeanContext 接口,如下所示:
``` java
public interface BeanContext {
/**
* 获得Bean
* @param name Bean的名称
* @return Bean
*/
Object getBean(String name);
/**
* 获得Bean
* @param name Bean的名称
* @param clazz Bean的类
* @param <T> 泛型
* @return Bean
*/
<T> T getBean(String name,Class<T> clazz);
}
```
然后我们需要在系统启动的时候,扫描出所有被 @Bean 注解修饰的类,然后对这些类进行实例化,然后把实例化后的对象保存在一个 Map 中即可,如下图所示:

代码很简单,通过在指定路径下扫描出所有的类之后,把实例对象加入map中,但是对于已经加入的 bean 不能继续加入了,加入之后要获取一个 Bean 也很简单了,直接通过 name 到 map 中去获取就可以了。
现在我们已经把所有 @Bean 的对象管理起来了,那对于依赖到的其他的 bean 该如何注入呢,换句话说就是将我们实例化好的对象赋值给 @Autowired 注解修饰的变量。
简单点的做法就是遍历 beanMap,然后对每个 bean 进行检查,看这个 bean 里面的每个 setter 方法和属性,如果有 @Autowired 注解,那就找到具体的 bean 实例之后将值塞进去。
### setter注入

### field注入

### 通过Aware获取BeanContext
BeanContext 已经实现了,那怎么获取 BeanContext 的实例呢?想到 Spring 中有很多的 Aware 接口,每种接口负责一种实例的回调,比如我们想要获取一个 BeanFactory 那只要将我们的类实现 BeanFactoryAware 接口就可以了,接口中的 setBeanFactory(BeanFactory factory) 方法参数中的 BeanFactory 实例就是我们所需要的,我们只要实现该方法,然后将参数中的实例保存在我们的类中,后续就可以直接使用了。
那现在我就来实现这样的功能,首先定义一个 Aware 接口,所有其他需要回调塞值的接口都继承自该接口,如下所示:
``` java
public interface Aware {
}
public interface BeanContextAware extends Aware{
/**
* 设置BeanContext
* @param beanContext BeanContext对象
*/
void setBeanContext(BeanContext beanContext);
}
```
接下来需要将 BeanContext 的实例注入到所有 BeanContextAware 的实现类中去。BeanContext 的实例很好得到,BeanContext 的实现类本身就是一个 BeanContext 的实例,并且可以将该实例设置为单例,这样的话所有需要获取 BeanContext 的地方都可以获取到同一个实例。
拿到 BeanContext 的实例后,我们就需要扫描出所有实现了 BeanContextAware 接口的类,并实例化这些类,然后调用这些类的 setBeanContext 方法,参数就传我们拿到的 BeanContext 实例。
逻辑理清楚之后,实现起来就很简单了,如下图所示:

## Cookie管理
基本上所有的 web 容器都会有 cookie 管理的能力,那我们的 redant 也不能落后。首先定义一个 CookieManager 的接口,核心的操作 cookie 的方法如下:
``` java
public interface CookieManager {
Set<Cookie> getCookies();
Cookie getCookie(String name);
void addCookie(String name,String value);
void setCookie(Cookie cookie);
boolean deleteCookie(String name);
}
```
其中我只列举了几个核心的方法,另外有一些不同参数的重载方法,这里就不详细介绍了。最关键的是两个方法,一个是读 Cookie 一个是写 Cookie 。
### 读 Cookie
Netty 中是通过 HttpRequest 的 Header 来保存请求中所携带的 Cookie的,所以要读取 Cookie 的话,最关键的是获取到 HttpRequest。而 HttpRequest 可以在 ChannelHandler 中拿到,通过 HttpServerCodec 编解码器,Netty 已经帮我们把请求的数据转换成 HttpRequest 了。但是这个 HttpRequest 只在 ChannelHandler 中才能访问到,而处理 Cookie 通常是用户自定义的操作,并且对用户来说他是不关心 HttpRequest 的,他只需要通过 CookieManager 去获取一个 Cookie 就行了。
这种情况下,最适合的就是将 HttpRequest 对象保存在一个 ThreadLocal 中,在 CookieManager 中需要获取的时候,直接到 ThreadLocal 中去取出来就可以了,如下列代码所示:
``` java
@Override
public Set<Cookie> getCookies() {
HttpRequest request = TemporaryDataHolder.loadHttpRequest();
Set<Cookie> cookies = new HashSet<>();
if(request != null) {
String value = request.headers().get(HttpHeaderNames.COOKIE);
if (value != null) {
cookies = ServerCookieDecoder.STRICT.decode(value);
}
}
return cookies;
}
```
TemporaryDataHolder 就是那个通过 ThreadLocal 保存了 HttpRequest 的类。
### 写 Cookie
写 Cookie 和读 Cookie 面临着一样的问题,就是写的时候需要借助于 HttpResponse,将 Cookie 写入 HttpResponse 的 Header 中去,但是用户执行写 Cookie 操作的时候,根本就不关心 HttpResponse,甚至他在写的时候,还没有 HttpResponse。
这时的做法也是将需要写到 HttpResponse 中的 Cookie 保存在 ThreadLocal 中,然后在最后通过 channel 写响应之前,将 Cookie 拿出来塞到 HttpResponse 中去即可,如下列代码所示:
``` java
@Override
public void setCookie(Cookie cookie) {
TemporaryDataHolder.storeCookie(cookie);
}
/**
* 响应消息
*/
private void writeResponse(){
boolean close = isClose();
response.headers().add(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(response.content().readableBytes()));
// 从ThreadLocal中取出待写入的cookie
Set<Cookie> cookies = TemporaryDataHolder.loadCookies();
if(!CollectionUtil.isEmpty(cookies)){
for(Cookie cookie : cookies){
// 将cookie写入response中
response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
}
}
ChannelFuture future = channel.write(response);
if(close){
future.addListener(ChannelFutureListener.CLOSE);
}
}
```
## 拦截器
拦截器是一个框架很重要的功能,通过拦截器可以实现一些通用的工作,比如登录鉴权,事务处理等等。记得在 Servlet 的年代,拦截器是非常重要的一个功能,基本上每个系统都会在 web.xml 中配置很多的拦截器。
拦截器的基本思想是,通过一连串的类去执行某个拦截的操作,一旦某个类中的拦截操作返回了 false,那就终止后面的所有流程,直接返回。
这种场景非常适合用责任链模式去实现,而 Netty 的 pipeline 本身就是一个责任链模式的应用,所以我们就可以通过 pipeline 来实现我们的拦截器。这里我定义了两种类型的拦截器:前置拦截器和后置拦截器。
前置拦截器是在处理用户的业务逻辑之前的一个拦截操作,如果该操作返回了 false 则直接 return,不会继续执行用户的业务逻辑。
后置拦截器就有点不同了,后置拦截器主要就是处理一些后续的操作,因为后置拦截器再跟前置拦截器一样,当操作返回了 false 直接 return 的话,已经没有意义了,因为业务逻辑已经执行完了。
理解清楚了具体的逻辑之后,实现起来就很简单了,如下列代码所示:
### 前置拦截器

### 后置拦截器

有了实现之后,我们需要把他们加到 pipeline 中合适的位置,让他们在整个责任链中生效,如下图所示:

### 指定拦截器的执行顺序
目前拦截器还没有实现指定顺序执行的功能,其实也很简单,可以定义一个 @InterceptorOrder 的注解应用在所有的拦截器的实现类上,扫描到拦截器的结果之后,根据该注解进行排序,然后把拍完序之后的结果添加到 pipeline 中即可。
## 集群模式
到目前为止,我描述的都是单节点模式,如果哪一天单节点的性能无法满足了,那就需要使用集群了,所以我也实现了集群模式。
集群模式是由一个主节点和若干个从节点构成的。主节点接收到请求后,将请求转发给从节点来处理,从节点把处理好的结果返回给主节点,由主节点把结果响应给请求。
要想实现集群模式需要有一个服务注册和发现的功能,目前是借助于 Zk 来做的服务注册与发现。
### 准备一个 Zk 服务端
因为主节点需要把请求转发给从节点,所以主节点需要知道目前有哪些从节点,我通过 ZooKeeper 来实现服务注册与发现。
如果你没有可用的 Zk 服务端的话,那你可以通过运行下面的 Main 方法来启动一个 ZooKeeper 服务端:
```java
public final class ZkBootstrap {
private static final Logger LOGGER = LoggerFactory.getLogger(ZkBootstrap.class);
public static void main(String[] args) {
try {
ZkServer zkServer = new ZkServer();
zkServer.startStandalone(ZkConfig.DEFAULT);
}catch (Exception e){
LOGGER.error("ZkBootstrap start failed,cause:",e);
System.exit(1);
}
}
}
```
这样你就可以在后面启动主从节点的时候使用这个 Zk 了。但是这并不是必须的,如果你已经有一个正在运行的 Zk 的服务端,那么你可以在启动主从节点的时候直接使用它,通过在 main 方法的参数中指定 Zk 的地址即可。
### 启动主节点
只需要运行下面的代码,就可以启动一个主节点了:
```java
public class MasterServerBootstrap {
public static void main(String[] args) {
String zkAddress = ZkServer.getZkAddressArgs(args,ZkConfig.DEFAULT);
// 启动MasterServer
Server masterServer = new MasterServer(zkAddress);
masterServer.preStart();
masterServer.start();
}
}
```
如果在 main 方法的参数中指定了 Zk 的地址,就通过该地址去进行服务发现,否则会使用默认的 Zk 地址。
### 启动从节点
只需要运行下面的代码,就可以启动一个从节点了:
```java
public class SlaveServerBootstrap {
public static void main(String[] args) {
String zkAddress = ZkServer.getZkAddressArgs(args,ZkConfig.DEFAULT);
Node node = Node.getNodeWithArgs(args);
// 启动SlaveServer
Server slaveServer = new SlaveServer(zkAddress,node);
slaveServer.preStart();
slaveServer.start();
}
}
```
如果在 main 方法的参数中指定了 Zk 的地址,就通过该地址去进行服务注册,否则会使用默认的 Zk 地址。
实际上多节点模式具体的处理逻辑还是复用了单节点模式的核心功能,只是把原本一台实例扩展到多台实例而已。
## 总结
本文通过介绍一个基于 Netty 的 web 容器,让我们了解了一个 http 服务端的大概的构成,当然实现中可能有更加好的方法。但是主要的还是要了解内在的思想,包括 Netty 的一些基本的使用方法。
我会继续优化该项目,加入更多的特性,例如服务发现与注册当前是通过 Zk 来实现的,未来可能会引入其他的组件去实现服务注册与发现。
除此之外,Session 的管理还未完全实现,后续也需要对这一块进行完善。
================================================
FILE: md/processor.md
================================================
# 责任链
我在 Redant 中实现的拦截器所使用的责任链,其实是通过了一个 List 来保存了所有的 Interceptor,那我们通常所说的责任链除了使用 List 来实现外,还可以通过真正的链表结构来实现,Netty 和 Sentinel 中都有这样的实现,下面我来实现一个简单的链式结构的责任链。
责任链的应用已经有很多了,这里不再赘述,假设我们需要对前端提交的请求做以下操作:鉴权,登录,日志记录,通过责任链来做这些处理是非常合适的。
首先定义一个处理接口,如下图所示:

通过 List 方式的实现很简单,只需要把每个 Processor 的实现类添加到一个 List 中即可,处理的时候遍历该 List 依次处理,这里不再做具体的描述,感兴趣的可以自行实现。
## 定义节点
如果是通过链表的形式来实现的话,首先我们需要有一个类表示链表中的某个节点,并且该节点需要有一个同类型的私有变量表示该节点的下个节点,这样就可以实现一个链表了,如下图所示:

## 定义容器
接着我们需要定义一个容器,在容器中有头,尾两个节点,头结点作为一个空节点,真正的节点将添加到头结点的 next 节点上去,尾节点作为一个指针,用来指向当前添加的节点,下一次添加新节点时,将从尾节点处添加。有了具体的处理逻辑之后,实现起来就很简单了,这个容器的实现如下图所示:

## 定义实现类
下面我们可以实现具体的 Processor 来处理业务逻辑了,只要继承 AbstractLinkedProcessor 即可,如下图所示:

其他两个实现类: LoginProcessor ,LogProcessor 类似,这里就不贴出来了。
然后就可以根据规则来组装所需要的 Processor 了,假设我们的规则是需要对请求依次进行:鉴权,登录,日志记录,那组装的代码如下图所示:

执行该代码,结果如下图所示:

## 存在的问题
看的仔细的同学可以发现了,在 AuthProcessor 的业务逻辑实现中,除了执行了具体的逻辑代码之外,还调用了一行 super.process(content) 代码,这行代码的作用是调用链表中的下一个节点的 process 方法。但是如果有一天我们在写自己的 Processor 实现类时,忘记调用这行代码的话,会是怎样的结果呢?
结果就是当前节点后面的节点不会被调用,整个链表就像断掉一样,那怎样来避免这种问题的发生呢?其实我们在 AbstractProcessor 中已经实现了 process 方法,该方法就是调用下个节点的 process 方法的。那我们在这个方法触发调用下个节点之前,再抽象出一个用以具体的业务逻辑处理的方法 doProcess ,先执行 doProcess 方法,执行完之后再触发下个节点的 process ,这样就不会出现链表断掉的情况了,具体的实现如下图所示:

相应的 LinkedProcessorChain 和具体的实现类也要做响应的调整,如下图所示:


重新执行刚刚的测试类,发现结果和之前的一样,至此一个简单的链式责任链完成了。
================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.redant</groupId>
<artifactId>redant</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>redant</name>
<properties>
<java.version>1.8</java.version>
<java.encoding>UTF-8</java.encoding>
<project-name>redant</project-name>
<project-version>1.0.0-SNAPSHOT</project-version>
<common-beans.version>1.9.2</common-beans.version>
<slf4j.version>1.7.7</slf4j.version>
<logback.version>1.1.7</logback.version>
<fastjson.version>1.2.54</fastjson.version>
<netty.version>4.1.9.Final</netty.version>
<cglib.version>3.2.4</cglib.version>
<hutool-all.version>4.2.1</hutool-all.version>
<zookeeper.version>3.4.13</zookeeper.version>
<curator.version>4.0.1</curator.version>
</properties>
<modules>
<module>redant-core</module>
<module>redant-cluster</module>
<module>redant-example</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${common-beans.version}</version>
</dependency>
<!-- 日志文件管理包 -->
<!-- 格式化对象,方便输出日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
<dependency>
<groupId>com.redant</groupId>
<artifactId>redant-core</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>com.redant</groupId>
<artifactId>redant-cluster</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>com.redant</groupId>
<artifactId>redant-example</artifactId>
<version>${project-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${java.encoding}</encoding>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.vm</include>
</includes>
</resource>
</resources>
</build>
</project>
================================================
FILE: redant-cluster/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>redant</artifactId>
<groupId>com.redant</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redant-cluster</artifactId>
<packaging>jar</packaging>
<name>redant-cluster</name>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.redant</groupId>
<artifactId>redant-core</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/bootstrap/MasterServerBootstrap.java
================================================
package com.redant.cluster.bootstrap;
import com.redant.cluster.master.MasterServer;
import com.redant.cluster.zk.ZkConfig;
import com.redant.cluster.zk.ZkServer;
import com.redant.core.server.Server;
/**
* MasterServerBootstrap
* @author houyi.wh
* @date 2017/11/20
**/
public class MasterServerBootstrap {
public static void main(String[] args) {
String zkAddress = ZkServer.getZkAddressArgs(args,ZkConfig.DEFAULT);
// 启动MasterServer
Server masterServer = new MasterServer(zkAddress);
masterServer.preStart();
masterServer.start();
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/bootstrap/SlaveServerBootstrap.java
================================================
package com.redant.cluster.bootstrap;
import com.redant.cluster.node.Node;
import com.redant.cluster.slave.SlaveServer;
import com.redant.cluster.zk.ZkConfig;
import com.redant.cluster.zk.ZkServer;
import com.redant.core.server.Server;
/**
* SlaveServerBootstrap
* @author houyi.wh
* @date 2017/11/20
**/
public class SlaveServerBootstrap {
public static void main(String[] args) {
String zkAddress = ZkServer.getZkAddressArgs(args,ZkConfig.DEFAULT);
Node node = Node.getNodeWithArgs(args);
// 启动SlaveServer
Server slaveServer = new SlaveServer(zkAddress,node);
slaveServer.preStart();
slaveServer.start();
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/bootstrap/ZkBootstrap.java
================================================
package com.redant.cluster.bootstrap;
import com.redant.cluster.zk.ZkConfig;
import com.redant.cluster.zk.ZkServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ZK启动入口
* @author houyi.wh
* @date 2017/11/21
**/
public class ZkBootstrap {
private static final Logger LOGGER = LoggerFactory.getLogger(ZkBootstrap.class);
public static void main(String[] args) {
try {
ZkServer zkServer = new ZkServer();
zkServer.startStandalone(ZkConfig.DEFAULT);
}catch (Exception e){
LOGGER.error("ZkBootstrap start failed,cause:",e);
System.exit(1);
}
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/master/MasterServer.java
================================================
package com.redant.cluster.master;
import com.redant.cluster.service.discover.ZkServiceDiscover;
import com.redant.core.common.constants.CommonConstants;
import com.redant.core.server.Server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* MasterServer
* @author houyi.wh
* @date 2017/11/20
*/
public final class MasterServer implements Server {
private static final Logger LOGGER = LoggerFactory.getLogger(MasterServer.class);
private String zkAddress;
public MasterServer(String zkAddress){
this.zkAddress = zkAddress;
}
@Override
public void preStart() {
// 监听SlaveNode的变化
ZkServiceDiscover.getInstance(zkAddress).watch();
}
@Override
public void start() {
EventLoopGroup bossGroup = new NioEventLoopGroup(CommonConstants.BOSS_GROUP_SIZE, new DefaultThreadFactory("boss", true));
EventLoopGroup workerGroup = new NioEventLoopGroup(CommonConstants.WORKER_GROUP_SIZE, new DefaultThreadFactory("worker", true));
try {
long start = System.currentTimeMillis();
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
// .handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new MasterServerInitializer(zkAddress));
ChannelFuture future = b.bind(CommonConstants.SERVER_PORT).sync();
long cost = System.currentTimeMillis()-start;
LOGGER.info("[MasterServer] Startup at port:{} cost:{}[ms]",CommonConstants.SERVER_PORT,cost);
// 等待服务端Socket关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
LOGGER.error("InterruptedException:",e);
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private static class MasterServerInitializer extends ChannelInitializer<SocketChannel> {
private String zkAddress;
MasterServerInitializer(String zkAddress){
this.zkAddress = zkAddress;
}
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
addAdvanced(pipeline);
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new MasterServerHandler(zkAddress));
}
/**
* 可以在 HttpServerCodec 之后添加这些 ChannelHandler 进行开启高级特性
*/
private void addAdvanced(ChannelPipeline pipeline){
if(CommonConstants.USE_COMPRESS) {
// 对 http 响应结果开启 gizp 压缩
pipeline.addLast(new HttpContentCompressor());
}
if(CommonConstants.USE_AGGREGATOR) {
// 将多个HttpRequest组合成一个FullHttpRequest
pipeline.addLast(new HttpObjectAggregator(CommonConstants.MAX_CONTENT_LENGTH));
}
}
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/master/MasterServerBackendHandler.java
================================================
package com.redant.cluster.master;
import io.netty.channel.*;
/**
* @author houyi.wh
* @date 2018/1/18
**/
public class MasterServerBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
public MasterServerBackendHandler(Channel inboundChannel){
this.inboundChannel = inboundChannel;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.read();
}
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
inboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ctx.channel().read();
} else {
future.channel().close();
}
}
});
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
MasterServerHandler.closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
MasterServerHandler.closeOnFlush(ctx.channel());
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/master/MasterServerHandler.java
================================================
package com.redant.cluster.master;
import com.redant.cluster.service.discover.ServiceDiscover;
import com.redant.cluster.service.discover.ZkServiceDiscover;
import com.redant.cluster.node.Node;
import com.redant.core.common.constants.CommonConstants;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* MasterServerHandler is a http master which
* will transfer http request to node server
* @author houyi.wh
* @date 2017/11/20
*/
public class MasterServerHandler extends ChannelInboundHandlerAdapter {
private final static Logger LOGGER = LoggerFactory.getLogger(MasterServerHandler.class);
private Node node;
/**
* Client--->Master Channel
*/
private Channel inboundChannel;
/**
* Master--->Slave Channel
*/
private Channel outboundChannel;
private ChannelFuture outboundConnectFuture;
private ServiceDiscover serviceDiscover;
public MasterServerHandler(String zkAddress){
this.serviceDiscover = ZkServiceDiscover.getInstance(zkAddress);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
node = serviceDiscover.discover();
inboundChannel = ctx.channel();
// Start the connection attempt.
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(inboundChannel.eventLoop())
.channel(ctx.channel().getClass())
// use master inboundChannel to write back the response get from remote server
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpClientCodec());
pipeline.addLast(new HttpObjectAggregator(CommonConstants.MAX_CONTENT_LENGTH));
pipeline.addLast(new MasterServerBackendHandler(inboundChannel));
}
});
bootstrap.option(ChannelOption.AUTO_READ, false);
// connect to node
outboundConnectFuture = bootstrap.connect(node.getHost(), node.getPort());
// get outboundChannel to remote server
outboundChannel = outboundConnectFuture.channel();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
outboundConnectFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
// connection complete start to read first data
inboundChannel.read();
if(outboundChannel.isActive()) {
if(msg instanceof HttpRequest){
HttpRequest request = (HttpRequest)msg;
if(request.uri().equals(CommonConstants.FAVICON_ICO)){
return;
}
outboundChannel.writeAndFlush(request).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
// was able to flush out data, start to read the next chunk
ctx.channel().read();
} else {
LOGGER.error("write to backend {}:{} error,cause:{}", node.getHost(), node.getPort(),future.cause());
future.channel().close();
}
}
});
}else{
closeOnFlush(ctx.channel());
}
}
} else {
LOGGER.error("connect to backend {}:{} error,cause:{}", node.getHost(), node.getPort(),future.cause());
// Close the connection if the connection attempt has failed.
inboundChannel.close();
}
}
});
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
closeOnFlush(ctx.channel());
}
/**
* Closes the specified channel after all queued write requests are flushed.
*/
static void closeOnFlush(Channel channel) {
if (channel.isActive()) {
channel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/node/Node.java
================================================
package com.redant.cluster.node;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.redant.core.common.util.GenericsUtil;
/**
* Node
* @author houyi.wh
* @date 2017/11/20
**/
public class Node {
private static final String DEFAULT_HOST = GenericsUtil.getLocalIpV4();
private static final int DEFAULT_PORT = 8088;
public static final Node DEFAULT_PORT_NODE = new Node(DEFAULT_HOST,DEFAULT_PORT);
private String id;
private String host;
private int port;
public Node(int port){
this(DEFAULT_HOST,port);
}
public Node(String host, int port){
this(SecureUtil.md5(host+"&"+port),host,port);
}
public Node(String id, String host, int port){
this.id = id;
this.host = host;
this.port = port;
}
public static Node getNodeWithArgs(String[] args){
Node node = Node.DEFAULT_PORT_NODE;
if(args.length>1 && NumberUtil.isInteger(args[1])){
node = new Node(Integer.parseInt(args[1]));
}
return node;
}
/**
* 从JsonObject中解析出SlaveNode
* @param object 对象
* @return 节点
*/
public static Node parse(JSONObject object){
if(object==null){
return null;
}
String host = object.getString("host");
int port = object.getIntValue("port");
String id = object.getString("id");
return new Node(id,host,port);
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getId(){
return id;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/service/discover/ServiceDiscover.java
================================================
package com.redant.cluster.service.discover;
import com.redant.cluster.node.Node;
/**
* 服务发现-应用级别
* @author houyi.wh
* @date 2017/11/20
**/
public interface ServiceDiscover {
/**
* 监听Slave节点
*/
void watch();
/**
* 发现可用的Slave节点
* @return 可用节点
*/
Node discover();
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/service/discover/ZkServiceDiscover.java
================================================
package com.redant.cluster.service.discover;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.redant.cluster.node.Node;
import com.redant.cluster.zk.ZkClient;
import com.redant.cluster.zk.ZkNode;
import io.netty.util.CharsetUtil;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 服务发现
* @author houyi.wh
* @date 2017/11/20
**/
public class ZkServiceDiscover implements ServiceDiscover {
private static final Logger LOGGER = LoggerFactory.getLogger(ZkServiceDiscover.class);
private CuratorFramework client;
private Map<String,Node> nodeMap;
private Lock lock;
private int slaveIndex = 0;
private static ServiceDiscover discover;
private ZkServiceDiscover(){
}
private ZkServiceDiscover(String zkAddress){
client = ZkClient.getClient(zkAddress);
nodeMap = new HashMap<>();
lock = new ReentrantLock();
}
public static ServiceDiscover getInstance(String zkAddress){
if(discover==null) {
synchronized (ZkServiceDiscover.class) {
if(discover==null) {
discover = new ZkServiceDiscover(zkAddress);
}
}
}
return discover;
}
@Override
public void watch() {
if(client==null){
throw new IllegalArgumentException("param illegal with client=null");
}
initNodeOnFirst();
doWatch();
}
@Override
public Node discover() {
if(client==null){
throw new IllegalArgumentException("param illegal with client=null");
}
lock.lock();
try {
if (nodeMap.size() == 0) {
LOGGER.error("No available Node!");
return null;
}
Node[] nodes = new Node[]{};
nodes = nodeMap.values().toArray(nodes);
// 通过CAS循环获取下一个可用服务
if (slaveIndex>=nodes.length) {
slaveIndex = 0;
}
return nodes[slaveIndex++];
}finally {
lock.unlock();
}
}
private void initNodeOnFirst(){
try {
if(client.checkExists().forPath(ZkNode.ROOT_NODE_PATH)!=null){
List<String> children = client.getChildren().forPath(ZkNode.ROOT_NODE_PATH);
for(String child : children){
String childPath = ZkNode.ROOT_NODE_PATH+"/"+child;
byte[] data = client.getData().forPath(childPath);
Node node = Node.parse(JSON.parseObject(data,JSONObject.class));
if(node !=null){
LOGGER.info("add slave={} to nodeMap when init", node);
nodeMap.put(node.getId(), node);
}
}
}
} catch (Exception e) {
LOGGER.error("initNodeOnFirst error cause:",e);
}
}
private void doWatch(){
try {
PathChildrenCache watcher = new PathChildrenCache(
client,
ZkNode.ROOT_NODE_PATH,
true
);
watcher.getListenable().addListener(new SlaveNodeWatcher());
watcher.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
}catch(Exception e){
LOGGER.error("doWatch error cause:",e);
}
}
private class SlaveNodeWatcher implements PathChildrenCacheListener {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
ChildData data = event.getData();
if(data==null || data.getData()==null){
return;
}
Node node = Node.parse(JSON.parseObject(data.getData(),JSONObject.class));
if(node ==null){
LOGGER.error("get a null slave with eventType={},path={},data={}",event.getType(),data.getPath(),data.getData());
}else {
switch (event.getType()) {
case CHILD_ADDED:
nodeMap.put(node.getId(), node);
LOGGER.info("CHILD_ADDED with path={},data={},current slave size={}", data.getPath(), new String(data.getData(),CharsetUtil.UTF_8), nodeMap.size());
break;
case CHILD_REMOVED:
nodeMap.remove(node.getId());
LOGGER.info("CHILD_REMOVED with path={},data={},current slave size={}", data.getPath(), new String(data.getData(),CharsetUtil.UTF_8), nodeMap.size());
break;
case CHILD_UPDATED:
nodeMap.replace(node.getId(), node);
LOGGER.info("CHILD_UPDATED with path={},data={},current slave size={}", data.getPath(), new String(data.getData(),CharsetUtil.UTF_8), nodeMap.size());
break;
default:
break;
}
}
}
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/service/register/ServiceRegister.java
================================================
package com.redant.cluster.service.register;
import com.redant.cluster.node.Node;
/**
* 服务注册-应用级别
* @author houyi.wh
* @date 2017/11/21
**/
public interface ServiceRegister {
/**
* 注册节点
* @param node 节点
*/
void register(Node node);
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/service/register/ZkServiceRegister.java
================================================
package com.redant.cluster.service.register;
import cn.hutool.core.util.StrUtil;
import com.redant.cluster.node.Node;
import com.redant.cluster.zk.ZkClient;
import com.redant.cluster.zk.ZkNode;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author houyi.wh
* @date 2017/11/21
**/
public class ZkServiceRegister implements ServiceRegister {
private static final Logger LOGGER = LoggerFactory.getLogger(ZkServiceRegister.class);
private CuratorFramework client;
private static ZkServiceRegister register;
private ZkServiceRegister(){
}
private ZkServiceRegister(String zkAddress){
client = ZkClient.getClient(zkAddress);
}
public static ServiceRegister getInstance(String zkAddress){
if(register==null) {
synchronized (ZkServiceRegister.class) {
if(register==null) {
register = new ZkServiceRegister(zkAddress);
}
}
}
return register;
}
@Override
public void register(Node node) {
if(client==null || node ==null){
throw new IllegalArgumentException(String.format("param illegal with client={%s},slave={%s}",client==null?null:client.toString(), node ==null?null: node.toString()));
}
try {
if(client.checkExists().forPath(ZkNode.SLAVE_NODE_PATH)==null) {
// 创建临时节点
client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(ZkNode.SLAVE_NODE_PATH, StrUtil.utf8Bytes(node.toString()));
}
} catch (Exception e) {
LOGGER.error("register slave error with slave={},cause:", node,e);
}
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/slave/SlaveServer.java
================================================
package com.redant.cluster.slave;
import com.redant.cluster.node.Node;
import com.redant.cluster.service.register.ZkServiceRegister;
import com.redant.core.common.constants.CommonConstants;
import com.redant.core.init.InitExecutor;
import com.redant.core.server.NettyHttpServerInitializer;
import com.redant.core.server.Server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* SlaveServer
* @author houyi.wh
* @date 2017/11/20
*/
public final class SlaveServer implements Server {
private static final Logger LOGGER = LoggerFactory.getLogger(SlaveServer.class);
private String zkAddress;
private Node node;
public SlaveServer(String zkAddress, Node node){
this.zkAddress = zkAddress;
this.node = node;
}
@Override
public void preStart() {
InitExecutor.init();
// 注册Slave到ZK
ZkServiceRegister.getInstance(zkAddress).register(node);
}
@Override
public void start() {
if(node ==null){
throw new IllegalArgumentException("slave is null");
}
EventLoopGroup bossGroup = new NioEventLoopGroup(CommonConstants.BOSS_GROUP_SIZE, new DefaultThreadFactory("boss", true));
EventLoopGroup workerGroup = new NioEventLoopGroup(CommonConstants.WORKER_GROUP_SIZE, new DefaultThreadFactory("worker", true));
try {
long start = System.currentTimeMillis();
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
// .handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new NettyHttpServerInitializer());
ChannelFuture future = b.bind(node.getPort()).sync();
long cost = System.currentTimeMillis()-start;
LOGGER.info("SlaveServer Startup at port:{} cost:{}[ms]", node.getPort(),cost);
// 等待服务端Socket关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
LOGGER.error("InterruptedException:",e);
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkClient.java
================================================
package com.redant.cluster.zk;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 操作ZK的客户端
* @author houyi.wh
* @date 2017/11/21
**/
public class ZkClient {
/**
* 节点的session超时时间,当Slave服务停掉后,
* curator客户端需要等待该节点超时后才会触发CHILD_REMOVED事件
*/
private static final int DEFAULT_SESSION_TIMEOUT_MS = 5000;
private static final int DEFAULT_CONNECTION_TIMEOUT_MS = 15000;
/**
* 操作ZK的客户端
*/
private static Map<String,CuratorFramework> clients;
private static Lock lock;
static{
clients = new ConcurrentHashMap<>();
lock = new ReentrantLock();
}
/**
* 获取ZK客户端
* @param zkAddress zk服务端地址
* @return zk客户端
*/
public static CuratorFramework getClient(String zkAddress){
if(zkAddress == null || zkAddress.trim().length() == 0){
return null;
}
CuratorFramework client = clients.get(zkAddress);
if(client==null){
lock.lock();
try {
if(!clients.containsKey(zkAddress)) {
client = CuratorFrameworkFactory.newClient(
zkAddress,
DEFAULT_SESSION_TIMEOUT_MS,
DEFAULT_CONNECTION_TIMEOUT_MS,
new RetryNTimes(10, 5000)
);
client.start();
clients.putIfAbsent(zkAddress,client);
}else{
client = clients.get(zkAddress);
}
}finally {
lock.unlock();
}
}
return client;
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkConfig.java
================================================
package com.redant.cluster.zk;
import com.redant.core.common.util.GenericsUtil;
import java.util.Properties;
/**
* 启动zk所需要的配置信息
* @author houyi.wh
* @date 2017/11/21
*/
public class ZkConfig {
private interface ZkConstant {
int CLIENT_PORT = 2181;
String DATA_DIR = "/Users/houyi/zookeeper/data";
String DATA_LOG_DIR = "/Users/houyi/zookeeper/log";
}
/**
* 客户端连接的端口
*/
private int clientPort;
private int tickTime = 2000;
private int initLimit = 10;
private int syncLimit = 5;
/**
* 数据存储目录,格式为:
* /home/zookeeper/1/data
*/
private String dataDir;
/**
* 日志存储目录,格式为:
* /home/zookeeper/1/log
*/
private String dataLogDir;
/**
* 客户端连接数上限
*/
private int maxClientCnxns = 60;
public ZkConfig(int clientPort,String dataDir,String dataLogDir){
this.clientPort = clientPort;
this.dataDir = dataDir;
this.dataLogDir = dataLogDir;
}
public static ZkConfig DEFAULT = new ZkConfig(ZkConstant.CLIENT_PORT,ZkConstant.DATA_DIR,ZkConstant.DATA_LOG_DIR);
public String generateZkAddress(){
return GenericsUtil.getLocalIpV4()+":"+this.clientPort;
}
public Properties toProp(){
Properties properties = new Properties();
properties.put("clientPort",this.clientPort);
properties.put("clientPortAddress",GenericsUtil.getLocalIpV4());
properties.put("tickTime",this.tickTime);
properties.put("initLimit",this.initLimit);
properties.put("syncLimit",this.syncLimit);
properties.put("dataDir",this.dataDir);
properties.put("dataLogDir",this.dataLogDir);
properties.put("maxClientCnxns",this.maxClientCnxns);
return properties;
}
public int getClientPort() {
return clientPort;
}
public void setClientPort(int clientPort) {
this.clientPort = clientPort;
}
public int getTickTime() {
return tickTime;
}
public void setTickTime(int tickTime) {
this.tickTime = tickTime;
}
public int getInitLimit() {
return initLimit;
}
public void setInitLimit(int initLimit) {
this.initLimit = initLimit;
}
public int getSyncLimit() {
return syncLimit;
}
public void setSyncLimit(int syncLimit) {
this.syncLimit = syncLimit;
}
public String getDataDir() {
return dataDir;
}
public void setDataDir(String dataDir) {
this.dataDir = dataDir;
}
public String getDataLogDir() {
return dataLogDir;
}
public void setDataLogDir(String dataLogDir) {
this.dataLogDir = dataLogDir;
}
public int getMaxClientCnxns() {
return maxClientCnxns;
}
public void setMaxClientCnxns(int maxClientCnxns) {
this.maxClientCnxns = maxClientCnxns;
}
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkNode.java
================================================
package com.redant.cluster.zk;
/**
* @author houyi.wh
* @date 2017/11/21
**/
public class ZkNode {
/**
* 根节点
*/
public static final String ROOT_NODE_PATH = "/redant";
/**
* SlaveNode注册的节点
*/
public static final String SLAVE_NODE_PATH = ROOT_NODE_PATH+"/slave";
}
================================================
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkServer.java
================================================
package com.redant.cluster.zk;
import cn.hutool.core.util.StrUtil;
import org.apache.zookeeper.server.ServerConfig;
import org.apache.zookeeper.server.ZooKeeperServerMain;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
import org.apache.zookeeper.server.quorum.QuorumPeerMain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Properties;
/**
* ZooKeeper服务端
* @author houyi.wh
* @date 2017/11/21
*
*/
public class ZkServer {
private static final Logger LOGGER = LoggerFactory.getLogger(ZkServer.class);
public static String getZkAddressArgs(String[] args, ZkConfig zkConfig){
String zkAddress = ZkServer.getZkAddress(zkConfig);
if(args.length>0 && StrUtil.isNotBlank(args[0])){
LOGGER.info("zkAddress is read from args");
zkAddress = args[0];
}
if(StrUtil.isBlank(zkAddress)){
System.exit(1);
}
return zkAddress;
}
public static String getZkAddress(ZkConfig zkConfig){
return zkConfig!=null ? zkConfig.generateZkAddress() : null;
}
/**
* 通过官方的ZooKeeperServerMain启动类启动单机模式
* @param zkConfig 配置对象
* @throws ConfigException 配置异常
* @throws IOException IO异常
*/
public void startStandalone(ZkConfig zkConfig) throws ConfigException, IOException {
Properties zkProp = zkConfig.toProp();
QuorumPeerConfig config = new QuorumPeerConfig();
config.parseProperties(zkProp);
ServerConfig serverConfig = new ServerConfig();
serverConfig.readFrom(config);
ZooKeeperServerMain zkServer = new ZooKeeperServerMain();
zkServer.runFromConfig(serverConfig);
}
/**
* 通过官方的QuorumPeerMain启动类启动真集群模式
* 会执行quorumPeer.join();
* 需要在不同的服务器上执行
* @param zkConfig 配置对象
* @throws ConfigException 配置异常
* @throws IOException IO异常
*/
public void startCluster(ZkConfig zkConfig) throws ConfigException, IOException {
Properties zkProp = zkConfig.toProp();
QuorumPeerConfig config = new QuorumPeerConfig();
config.parseProperties(zkProp);
QuorumPeerMain main = new QuorumPeerMain();
main.runFromConfig(config);
}
}
================================================
FILE: redant-core/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>redant</artifactId>
<groupId>com.redant</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redant-core</artifactId>
<packaging>jar</packaging>
<name>redant-core</name>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<!-- 日志文件管理包 -->
<!-- 格式化对象,方便输出日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
</project>
================================================
FILE: redant-core/src/main/java/com/redant/core/ServerBootstrap.java
================================================
package com.redant.core;
import com.redant.core.server.NettyHttpServer;
import com.redant.core.server.Server;
/**
* 服务端启动入口
* @author houyi.wh
* @date 2017-10-20
*/
public final class ServerBootstrap {
public static void main(String[] args) {
Server nettyServer = new NettyHttpServer();
// 各种初始化工作
nettyServer.preStart();
// 启动服务器
nettyServer.start();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/anno/Order.java
================================================
package com.redant.core.anno;
import java.lang.annotation.*;
/**
* 排序规则,升序排序
* @author houyi.wh
* @date 2019-01-14
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface Order {
/**
* 最低优先级
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* 最高优先级
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* The order value. Lowest precedence by default.
*
* @return the order value
*/
int value() default LOWEST_PRECEDENCE;
}
================================================
FILE: redant-core/src/main/java/com/redant/core/aware/Aware.java
================================================
package com.redant.core.aware;
/**
* @author houyi.wh
* @date 2019-01-14
*/
public interface Aware {
}
================================================
FILE: redant-core/src/main/java/com/redant/core/aware/BeanContextAware.java
================================================
package com.redant.core.aware;
import com.redant.core.bean.context.BeanContext;
/**
* @author houyi.wh
* @date 2019-01-14
*/
public interface BeanContextAware extends Aware{
/**
* 设置BeanContext
* @param beanContext BeanContext对象
*/
void setBeanContext(BeanContext beanContext);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/bean/BaseBean.java
================================================
package com.redant.core.bean;
import com.alibaba.fastjson.JSON;
import java.io.Serializable;
/**
* BaseBean 所有bean都继承该类
* @author houyi.wh
* @date 2017-10-20
*/
public class BaseBean implements Serializable {
private static final long serialVersionUID = -4976516540408695147L;
public BaseBean() {
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/bean/annotation/Autowired.java
================================================
package com.redant.core.bean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
String name() default "";
}
================================================
FILE: redant-core/src/main/java/com/redant/core/bean/annotation/Bean.java
================================================
package com.redant.core.bean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
String name() default "";
}
================================================
FILE: redant-core/src/main/java/com/redant/core/bean/context/BeanContext.java
================================================
package com.redant.core.bean.context;
/**
* Bean上下文
* @author houyi.wh
* @date 2017-10-20
*/
public interface BeanContext {
/**
* 获得Bean
* @param name Bean的名称
* @return Bean
*/
Object getBean(String name);
/**
* 获得Bean
* @param name Bean的名称
* @param clazz Bean的类
* @param <T> 泛型
* @return Bean
*/
<T> T getBean(String name,Class<T> clazz);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/bean/context/DefaultBeanContext.java
================================================
package com.redant.core.bean.context;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.ClassScaner;
import cn.hutool.core.util.StrUtil;
import com.redant.core.aware.BeanContextAware;
import com.redant.core.bean.annotation.Autowired;
import com.redant.core.bean.annotation.Bean;
import com.redant.core.common.constants.CommonConstants;
import com.redant.core.init.InitFunc;
import com.redant.core.init.InitOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* DefaultBeanContext
*
* @author houyi.wh
* @date 2017-10-20
*/
@InitOrder(1)
public class DefaultBeanContext implements BeanContext, InitFunc {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBeanContext.class);
/**
* 保存所有的bean,初始化过一次后不会改变
*/
private static Map<String, Object> beanMap;
/**
* bean加载完毕的标志
*/
private static volatile boolean inited;
private static final class DefaultBeanContextHolder {
private static DefaultBeanContext context = new DefaultBeanContext();
}
private DefaultBeanContext() {
}
public static BeanContext getInstance() {
return DefaultBeanContextHolder.context;
}
@Override
public void init() {
// 初始化
doInit();
}
/**
* 根据bean的name获取具体的bean对象
*/
@Override
public Object getBean(String name) {
return inited() ? beanMap.get(name) : null;
}
/**
* 根据bean的name获取具体的bean对象
*/
@Override
public <T> T getBean(String name, Class<T> clazz) {
Object bean = getBean(name);
return bean == null ? null : (T) bean;
}
/**
* bean是否加载完毕
*/
private boolean inited() {
while (!inited) {
doInit();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return inited;
}
/**
* 执行初始化工作
*/
private void doInit() {
// 初始化时需要同步
synchronized (DefaultBeanContext.class) {
if (!inited) {
LOGGER.info("[DefaultBeanContext] doInit");
// 初始化bean
initBean();
// 对象注入
injectAnnotation();
// 注入BeanContext到BeanContextAware
processBeanContextAware();
inited = true;
LOGGER.info("[DefaultBeanContext] doInit success!");
}
}
}
/**
* 初始化Bean
*/
private void initBean() {
LOGGER.info("[DefaultBeanContext] start initBean");
try {
/*
* 扫描指定package下指定的类,并返回set
*/
Set<Class<?>> classSet = ClassScaner.scanPackageByAnnotation(CommonConstants.BEAN_SCAN_PACKAGE, Bean.class);
beanMap = new LinkedHashMap<>(classSet.size() + 1);
if (CollectionUtil.isNotEmpty(classSet)) {
/*
* 遍历所有类,找出有beanClass注解的类,并封装到linkedHashMap里
*/
for (Class<?> cls : classSet) {
Bean bean = cls.getAnnotation(Bean.class);
if (bean != null) {
String beanName = StrUtil.isNotBlank(bean.name()) ? bean.name() : cls.getName();
if (beanMap.containsKey(beanName)) {
LOGGER.warn("[DefaultBeanContext] duplicate bean with name={}", beanName);
continue;
}
beanMap.put(beanName, cls.newInstance());
}
}
LOGGER.info("[DefaultBeanContext] initBean success!");
} else {
LOGGER.warn("[DefaultBeanContext] no bean classes scanned!");
}
} catch (Exception e) {
LOGGER.error("[DefaultBeanContext] initBean error,cause:{}", e.getMessage(), e);
}
}
/**
* 注解处理器
* 如果注解Autowired配置了name属性,则根据name所指定的名称获取要注入的实例引用,
* 否则根据属性所属类型来扫描配置文件获取要注入的实例引用
*/
private void injectAnnotation() {
LOGGER.info("[DefaultBeanContext] start injectAnnotation");
for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
Object bean = entry.getValue();
if (bean != null) {
propertyAnnotation(bean);
fieldAnnotation(bean);
}
}
LOGGER.info("[DefaultBeanContext] injectAnnotation success!");
}
/**
* 处理BeanContextAware
* 让那些实现了BeanContextAware接口的类能注入BeanContext
*/
private void processBeanContextAware() {
LOGGER.info("[DefaultBeanContext] start processBeanContextAware");
try {
/*
* 扫描指定package下指定的类,并返回set
*/
Set<Class<?>> classSet = ClassScaner.scanPackageBySuper(CommonConstants.BEAN_SCAN_PACKAGE, BeanContextAware.class);
if (CollectionUtil.isNotEmpty(classSet)) {
for (Class<?> cls : classSet) {
// 如果cls是BeanContextAware的实现类
if (!cls.isInterface() && BeanContextAware.class.isAssignableFrom(cls)) {
Constructor<?> constructor = cls.getDeclaredConstructor();
constructor.setAccessible(true);
BeanContextAware aware = (BeanContextAware) constructor.newInstance();
aware.setBeanContext(getInstance());
}
}
}
LOGGER.info("[DefaultBeanContext] processBeanContextAware success!");
} catch (Exception e) {
LOGGER.error("[DefaultBeanContext] processBeanContextAware error,cause:{}", e.getMessage(), e);
}
}
/**
* 处理在set方法加入的注解
*
* @param bean 处理的bean
*/
private void propertyAnnotation(Object bean) {
LOGGER.info("[DefaultBeanContext] start propertyAnnotation");
try {
// 获取其属性的描述
PropertyDescriptor[] descriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
// 获取所有set方法
Method setter = descriptor.getWriteMethod();
// 判断set方法是否定义了注解
if (setter != null && setter.isAnnotationPresent(Autowired.class)) {
// 获取当前注解,并判断name属性是否为空
Autowired resource = setter.getAnnotation(Autowired.class);
String name;
Object value = null;
if (StrUtil.isNotBlank(resource.name())) {
// 获取注解的name属性的内容
name = resource.name();
value = beanMap.get(name);
} else { // 如果当前注解没有指定name属性,则根据类型进行匹配
for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
// 判断当前属性所属的类型是否在beanHolderMap中存在
if (descriptor.getPropertyType().isAssignableFrom(entry.getValue().getClass())) {
// 获取类型匹配的实例对象
value = entry.getValue();
break;
}
}
}
// 允许访问private方法
setter.setAccessible(true);
// 把引用对象注入属性
setter.invoke(bean, value);
}
}
LOGGER.info("[DefaultBeanContext] propertyAnnotation success!");
} catch (Exception e) {
LOGGER.info("[DefaultBeanContext] propertyAnnotation error,cause:{}", e.getMessage(), e);
}
}
/**
* 处理在字段上的注解
*
* @param bean 处理的bean
*/
private void fieldAnnotation(Object bean) {
LOGGER.info("[DefaultBeanContext] start fieldAnnotation");
try {
// 获取其全部的字段描述
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field != null && field.isAnnotationPresent(Autowired.class)) {
Autowired resource = field.getAnnotation(Autowired.class);
String name;
Object value = null;
if (StrUtil.isNotBlank(resource.name())) {
name = resource.name();
value = beanMap.get(name);
} else {
for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
// 判断当前属性所属的类型是否在配置文件中存在
if (field.getType().isAssignableFrom(entry.getValue().getClass())) {
// 获取类型匹配的实例对象
value = entry.getValue();
break;
}
}
}
// 允许访问private字段
field.setAccessible(true);
// 把引用对象注入属性
field.set(bean, value);
}
}
LOGGER.info("[DefaultBeanContext] fieldAnnotation success!");
} catch (Exception e) {
LOGGER.info("[DefaultBeanContext] fieldAnnotation error,cause:{}", e.getMessage(), e);
}
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/constants/CommonConstants.java
================================================
package com.redant.core.common.constants;
import com.redant.core.common.util.PropertiesUtil;
/**
* 公共常量
* @author houyi.wh
* @date 2017-10-20
*/
public class CommonConstants {
private static final String REDANT_PROPERTIES_PATH = "/redant.properties";
private static PropertiesUtil propertiesUtil = PropertiesUtil.getInstance(REDANT_PROPERTIES_PATH);
/**
* 服务端口号
*/
public static final int SERVER_PORT = propertiesUtil.getInt("netty.server.port",8888);
/**
* BossGroup Size
* 先从启动参数中获取:-Dnetty.server.bossGroup.size=2
* 如果获取不到从配置文件中获取
* 如果再获取不到则取默认值
*/
public static final int BOSS_GROUP_SIZE = null!=Integer.getInteger("netty.server.bossGroup.size")?Integer.getInteger("netty.server.bossGroup.size"):propertiesUtil.getInt("netty.server.bossGroup.size",2);
/**
* WorkerGroup Size
* 先从启动参数中获取:-Dnetty.server.workerGroup.size=4
* 如果获取不到从配置文件中获取
* 如果再获取不到则取默认值
*/
public static final int WORKER_GROUP_SIZE = null!=Integer.getInteger("netty.server.workerGroup.size")?Integer.getInteger("netty.server.workerGroup.size"):propertiesUtil.getInt("netty.server.workerGroup.size",4);
/**
* 能处理的最大数据的字节数
*/
public static final int MAX_CONTENT_LENGTH = propertiesUtil.getInt("netty.maxContentLength",10485760);
/**
* 是否开启ssl
*/
public static final boolean USE_SSL = propertiesUtil.getBoolean("netty.server.use.ssl");
/**
* 是否开启压缩
*/
public static final boolean USE_COMPRESS = propertiesUtil.getBoolean("netty.server.use.compress");
/**
* 是否开启http对象聚合
*/
public static final boolean USE_AGGREGATOR = propertiesUtil.getBoolean("netty.server.use.aggregator");
/**
* KeyStore path
*/
public static final String KEY_STORE_PATH = propertiesUtil.getString("ssl.keyStore.path");
/**
* KeyStore password
*/
public static final String KEY_STORE_PASSWORD = propertiesUtil.getString("ssl.keyStore.password");
/**
* 扫描bean的包路径
*/
public static final String BEAN_SCAN_PACKAGE = propertiesUtil.getString("bean.scan.package");
/**
* 扫描interceptor的包路径
*/
public static final String INTERCEPTOR_SCAN_PACKAGE = propertiesUtil.getString("interceptor.scan.package");
/**
* 服务端出错时的错误描述
*/
public static final String SERVER_INTERNAL_ERROR_DESC = propertiesUtil.getString("server.internal.error.desc");
public static final String FAVICON_ICO = "/favicon.ico";
public static final String CONNECTION_KEEP_ALIVE = "keep-alive";
public static final String CONNECTION_CLOSE = "close";
/**
* 是否异步处理业务逻辑
*/
public static final boolean ASYNC_EXECUTE_EVENT = propertiesUtil.getBoolean("async.execute.event");
/**
* 业务线程池核心线程数
*/
public static final int EVENT_EXECUTOR_POOL_CORE_SIZE = propertiesUtil.getInt("async.executor.pool.core.size",10);
/**
* 业务线程池最大线程数
*/
public static final int EVENT_EXECUTOR_POOL_MAX_SIZE = propertiesUtil.getInt("async.executor.pool.max.size",20);
/**
* 业务线程池临时线程存活时间,单位:s
*/
public static final int EVENT_EXECUTOR_POOL_KEEP_ALIVE_SECONDS = propertiesUtil.getInt("async.executor.pool.keep.alive.seconds",10);
/**
* 业务线程池阻塞队列大学
*/
public static final int EVENT_EXECUTOR_POOL_BLOCKING_QUEUE_SIZE = propertiesUtil.getInt("async.executor.pool.blocking.queue.size",10);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/enums/ContentType.java
================================================
package com.redant.core.common.enums;
public enum ContentType {
APPLICATION_ATOM_XML("application/atom+xml"),
APPLICATION_FORM_URLENCODED("application/x-www-form-urlencoded"),
APPLICATION_JSON("application/json"),
APPLICATION_OCTET_STREAM("application/octet-stream"),
APPLICATION_SVG_XML("application/svg+xml"),
APPLICATION_XHTML_XML("application/xhtml+xml"),
APPLICATION_XML("application/xml")
;
private String content;
ContentType(String content){
this.content = content;
}
@Override
public String toString() {
return content;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/enums/RequestMethod.java
================================================
package com.redant.core.common.enums;
import io.netty.handler.codec.http.HttpMethod;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public enum RequestMethod {
/**
* GET
*/
GET(HttpMethod.GET),
/**
* HEAD
*/
HEAD(HttpMethod.HEAD),
/**
* POST
*/
POST(HttpMethod.POST),
/**
* PUT
*/
PUT(HttpMethod.PUT),
/**
* PATCH
*/
PATCH(HttpMethod.PATCH),
/**
* DELETE
*/
DELETE(HttpMethod.DELETE),
/**
* OPTIONS
*/
OPTIONS(HttpMethod.OPTIONS),
/**
* TRACE
*/
TRACE(HttpMethod.TRACE);
HttpMethod httpMethod;
RequestMethod(HttpMethod httpMethod) {
this.httpMethod = httpMethod;
}
public static HttpMethod getHttpMethod(RequestMethod requestMethod){
for(RequestMethod method : values()){
if(requestMethod==method){
return method.httpMethod;
}
}
return null;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/exception/InvalidSessionException.java
================================================
package com.redant.core.common.exception;
/**
* 非法session
* @author houyi.wh
* @date 2017-10-20
*/
public class InvalidSessionException extends RuntimeException{
private static final long serialVersionUID = 1L;
public InvalidSessionException(String s) {
super(s);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/exception/InvocationException.java
================================================
package com.redant.core.common.exception;
public class InvocationException extends Exception{
private static final long serialVersionUID = 1L;
public InvocationException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/exception/ValidationException.java
================================================
package com.redant.core.common.exception;
/**
* ValidationException
* @author houyi.wh
* @date 2017-10-20
*/
public class ValidationException extends RuntimeException{
private static final long serialVersionUID = 1L;
public ValidationException(String s) {
super(s);
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/html/DefaultHtmlMaker.java
================================================
package com.redant.core.common.html;
import cn.hutool.core.collection.CollectionUtil;
import com.redant.core.common.view.HtmlKeyHolder;
import java.util.Map;
/**
* 默认的HtmlMaker,只处理字符串
* @author houyi.wh
* @date 2017/12/1
**/
public class DefaultHtmlMaker implements HtmlMaker {
@Override
public String make(String htmlTemplate, Map<String, Object> contentMap) {
String html = htmlTemplate;
if(CollectionUtil.isNotEmpty(contentMap)){
for(Map.Entry<String,Object> entry : contentMap.entrySet()){
String key = entry.getKey();
Object val = entry.getValue();
if(val instanceof String){
html = html.replaceAll(HtmlKeyHolder.START_ESCAPE+key+HtmlKeyHolder.END,val.toString());
}
}
}
return html;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/html/HtmlMaker.java
================================================
package com.redant.core.common.html;
import java.util.Map;
/**
* html生成器
* @author houyi.wh
* @date 2017/12/1
**/
public interface HtmlMaker {
/**
* 根据html模板生成html内容
* @param htmlTemplate html模板
* @param contentMap 参数
* @return html内容
*/
String make(String htmlTemplate,Map<String, Object> contentMap);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/html/HtmlMakerEnum.java
================================================
package com.redant.core.common.html;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public enum HtmlMakerEnum {
/**
* 字符串
*/
STRING
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/html/HtmlMakerFactory.java
================================================
package com.redant.core.common.html;
import cn.hutool.core.util.ReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public class HtmlMakerFactory {
private volatile static HtmlMakerFactory factory;
private Map<HtmlMakerEnum,HtmlMaker> htmlMakerMap;
private final Lock lock;
private HtmlMakerFactory(){
htmlMakerMap = new ConcurrentHashMap<>();
lock = new ReentrantLock();
}
/**
* 获取工厂实例
*/
public static HtmlMakerFactory instance(){
if(factory==null){
synchronized (HtmlMakerFactory.class) {
if (factory==null) {
factory = new HtmlMakerFactory();
}
}
}
return factory;
}
/**
* 创建HtmlMaker实例
*/
public HtmlMaker build(HtmlMakerEnum type,Class<? extends HtmlMaker> clazz){
if(type==null){
return null;
}else{
HtmlMaker htmlMaker = htmlMakerMap.get(type);
if(htmlMaker==null){
lock.lock();
try {
if(!htmlMakerMap.containsKey(type)) {
htmlMaker = ReflectUtil.newInstance(clazz);
htmlMakerMap.putIfAbsent(type,htmlMaker);
}else{
htmlMaker = htmlMakerMap.get(type);
}
}finally {
lock.unlock();
}
}
return htmlMaker;
}
}
public static void main(String[] args) {
int loopTimes = 200;
class Runner implements Runnable{
private Logger logger = LoggerFactory.getLogger(Runner.class);
@Override
public void run() {
HtmlMakerFactory factory = HtmlMakerFactory.instance();
logger.info("factory={},currentThread={}",(factory!=null?factory.getClass().getName():"null"),Thread.currentThread().getName());
}
}
for(int i=0;i<loopTimes;i++){
new Thread(new Runner()).start();
}
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/util/GenericsUtil.java
================================================
package com.redant.core.common.util;
import cn.hutool.core.util.NetUtil;
import cn.hutool.core.util.StrUtil;
import com.redant.core.common.exception.ValidationException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
* GenericsUtil
* @author houyi.wh
* @date 2017-10-20
*/
public class GenericsUtil {
/**
* 通过反射获得Class声明的范型Class.
* 通过反射,获得方法输入参数第index个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String, Buyer> maps, List<String> names){}
* @param method 方法
* @param index 第几个输入参数
* @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回空集合
*/
@SuppressWarnings("rawtypes")
public static List<Class> getMethodGenericParameterTypes(Method method, int index) {
List<Class> results = new ArrayList<Class>();
Type[] genericParameterTypes = method.getGenericParameterTypes();
if (index >= genericParameterTypes.length || index < 0) {
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数"));
}
Type genericParameterType = genericParameterTypes[index];
if (genericParameterType instanceof ParameterizedType) {
ParameterizedType aType = (ParameterizedType) genericParameterType;
Type[] parameterArgTypes = aType.getActualTypeArguments();
for (Type parameterArgType : parameterArgTypes) {
Class parameterArgClass = (Class) parameterArgType;
results.add(parameterArgClass);
}
return results;
}
return results;
}
/**
* 断言非空
* @param dataName 参数
* @param values 值
*/
public static void checkNull(String dataName, Object... values){
if(values == null){
throw new ValidationException("["+ dataName + "] cannot be null");
}
for (Object value : values) {
if (value == null) {
throw new ValidationException("[" + dataName + "] cannot be null");
}
}
}
/**
* 断言非空
* @param dataName 参数
* @param values 值
*/
public static void checkBlank(String dataName, Object... values){
if(values == null){
throw new ValidationException("["+ dataName + "] cannot be null");
}
for (Object value : values) {
if (value == null || StrUtil.isBlank(value.toString())) {
throw new ValidationException("[" + dataName + "] cannot be blank");
}
}
}
/**
* 获取ipV4
* @return ipV4
*/
public static String getLocalIpV4(){
LinkedHashSet<String> ipV4Set = NetUtil.localIpv4s();
return ipV4Set.isEmpty()?"":ipV4Set.toArray()[0].toString();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/util/HtmlContentUtil.java
================================================
package com.redant.core.common.util;
import com.redant.core.common.html.HtmlMaker;
import com.redant.core.common.constants.CommonConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public class HtmlContentUtil {
private final static Logger logger = LoggerFactory.getLogger(HtmlContentUtil.class);
private HtmlContentUtil(){
}
/**
* 获取页面内容
* @param htmlMaker htmlMaker
* @param htmlTemplate html模板
* @param contentMap 参数
* @return 页面内容
*/
public static String getPageContent(HtmlMaker htmlMaker, String htmlTemplate, Map<String, Object> contentMap){
try {
return htmlMaker.make(htmlTemplate,contentMap);
} catch (Exception e) {
logger.error("getPageContent Error,cause:",e);
}
return CommonConstants.SERVER_INTERNAL_ERROR_DESC;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/util/HttpRenderUtil.java
================================================
package com.redant.core.common.util;
import com.alibaba.fastjson.JSONObject;
import com.redant.core.common.html.DefaultHtmlMaker;
import com.redant.core.common.html.HtmlMaker;
import com.redant.core.common.html.HtmlMakerEnum;
import com.redant.core.common.html.HtmlMakerFactory;
import com.redant.core.common.view.Page404;
import com.redant.core.render.RenderType;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
/**
* HttpRenderUtil
*
* @author houyi.wh
* @date 2017-10-20
*/
public class HttpRenderUtil {
public static final String EMPTY_CONTENT = "";
private HttpRenderUtil() {
}
/**
* response输出
*
* @param content 内容
* @param renderType 返回类型
* @return 响应对象
*/
public static FullHttpResponse render(Object content, RenderType renderType) {
byte[] bytes = HttpRenderUtil.getBytes(content);
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf);
RenderType type = renderType != null ? renderType : RenderType.JSON;
response.headers().add(HttpHeaderNames.CONTENT_TYPE, type.getContentType());
response.headers().add(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(byteBuf.readableBytes()));
return response;
}
/**
* 404NotFoundResponse
*
* @return 响应对象
*/
public static FullHttpResponse getNotFoundResponse() {
HtmlMaker htmlMaker = HtmlMakerFactory.instance().build(HtmlMakerEnum.STRING, DefaultHtmlMaker.class);
String htmlTpl = Page404.HTML;
String content = HtmlContentUtil.getPageContent(htmlMaker, htmlTpl, null);
return render(content, RenderType.HTML);
}
/**
* ServerErrorResponse
*
* @return 响应对象
*/
public static FullHttpResponse getServerErrorResponse() {
JSONObject object = new JSONObject();
object.put("code", 500);
object.put("message", "Server Internal Error!");
return render(object, RenderType.JSON);
}
/**
* ErrorResponse
*
* @param errorMessage 错误信息
* @return 响应对象
*/
public static FullHttpResponse getErrorResponse(String errorMessage) {
JSONObject object = new JSONObject();
object.put("code", 300);
object.put("message", errorMessage);
return render(object, RenderType.JSON);
}
/**
* BlockedResponse
*
* @return 响应对象
*/
public static FullHttpResponse getBlockedResponse() {
JSONObject object = new JSONObject();
object.put("code", 1000);
object.put("message", "Blocked by user defined interceptor");
return render(object, RenderType.JSON);
}
/**
* 转换byte
*
* @param content 内容
* @return 响应对象
*/
private static byte[] getBytes(Object content) {
if (content == null) {
return EMPTY_CONTENT.getBytes(CharsetUtil.UTF_8);
}
String data = content.toString();
data = (data == null || data.trim().length() == 0) ? EMPTY_CONTENT : data;
return data.getBytes(CharsetUtil.UTF_8);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/util/HttpRequestUtil.java
================================================
package com.redant.core.common.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.redant.core.common.enums.ContentType;
import com.redant.core.converter.PrimitiveTypeUtil;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author houyi.wh
* @date 2017/11/16
**/
public class HttpRequestUtil {
/**
* 获取请求参数的Map
* @param request http请求
* @return 参数map
*/
public static Map<String, List<String>> getParameterMap(HttpRequest request){
Map<String, List<String>> paramMap = new HashMap<>();
HttpMethod method = request.method();
if(HttpMethod.GET.equals(method)){
String uri = request.uri();
QueryStringDecoder queryDecoder = new QueryStringDecoder(uri, CharsetUtil.UTF_8);
paramMap = queryDecoder.parameters();
}else if(HttpMethod.POST.equals(method)){
FullHttpRequest fullRequest = (FullHttpRequest) request;
paramMap = getPostParamMap(fullRequest);
}
return paramMap;
}
/**
* 获取post请求的参数map
* 目前支持最常用的 application/json 、application/x-www-form-urlencoded 几种 POST Content-type,可自行扩展!!!
*/
@SuppressWarnings("unchecked")
private static Map<String, List<String>> getPostParamMap(FullHttpRequest fullRequest) {
Map<String, List<String>> paramMap = new HashMap<>();
HttpHeaders headers = fullRequest.headers();
String contentType = getContentType(headers);
if(ContentType.APPLICATION_JSON.toString().equals(contentType)){
String jsonStr = fullRequest.content().toString(CharsetUtil.UTF_8);
JSONObject obj = JSON.parseObject(jsonStr);
for(Map.Entry<String, Object> item : obj.entrySet()){
String key = item.getKey();
Object value = item.getValue();
Class<?> valueType = value.getClass();
List<String> valueList;
if(paramMap.containsKey(key)){
valueList = paramMap.get(key);
}else{
valueList = new ArrayList<String>();
}
if(PrimitiveTypeUtil.isPriType(valueType)){
valueList.add(value.toString());
paramMap.put(key, valueList);
}else if(valueType.isArray()){
int length = Array.getLength(value);
for(int i=0; i<length; i++){
String arrayItem = String.valueOf(Array.get(value, i));
valueList.add(arrayItem);
}
paramMap.put(key, valueList);
}else if(List.class.isAssignableFrom(valueType)){
if(valueType.equals(JSONArray.class)){
JSONArray jArray = JSONArray.parseArray(value.toString());
for(int i=0; i<jArray.size(); i++){
valueList.add(jArray.getString(i));
}
}else{
valueList = (ArrayList<String>) value;
}
paramMap.put(key, valueList);
}else if(Map.class.isAssignableFrom(valueType)){
Map<String, String> tempMap = (Map<String, String>) value;
for(Map.Entry<String, String> entry : tempMap.entrySet()){
List<String> tempList = new ArrayList<String>();
tempList.add(entry.getValue());
paramMap.put(entry.getKey(), tempList);
}
}
}
}else if(ContentType.APPLICATION_FORM_URLENCODED.toString().equals(contentType)){
String jsonStr = fullRequest.content().toString(CharsetUtil.UTF_8);
QueryStringDecoder queryDecoder = new QueryStringDecoder(jsonStr, false);
paramMap = queryDecoder.parameters();
}
return paramMap;
}
/**
* 获取contentType
* @param headers http请求头
* @return 内容类型
*/
private static String getContentType(HttpHeaders headers){
String contentType = headers.get(HttpHeaderNames.CONTENT_TYPE);
String[] list = contentType.split(";");
return list[0];
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/util/PropertiesUtil.java
================================================
package com.redant.core.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
/**
* 使用单例模式,获取配置文件中的信息
* @author hwang
* @date 2015-04-15
*/
public class PropertiesUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesUtil.class);
private static Map<String,PropertiesUtil> propertiesUtilsHolder = null;
private static Map<PropertiesUtil,Properties> propertiesMap = null;
private volatile boolean propertiesLoaded;
private PropertiesUtil(){
}
static{
propertiesUtilsHolder = new HashMap<>();
propertiesMap = new HashMap<>();
}
/**
* 是否加载完毕
*/
private boolean propertiesLoaded(){
int retryTime = 0;
int retryTimeout = 1000;
int sleep = 500;
while(!propertiesLoaded && retryTime<retryTimeout){
try {
Thread.sleep(sleep);
retryTime+=sleep;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return propertiesLoaded;
}
/**
* 根据Resource获取properties
*/
public static Properties getPropertiesByResource(String propertiesPath){
InputStream inputStream = null;
Properties properties = null;
try{
inputStream = PropertiesUtil.class.getResourceAsStream(propertiesPath);
if(inputStream!=null){
properties = new Properties();
properties.load(inputStream);
}
} catch (Exception e) {
LOGGER.error("getInstance occur error,cause:",e);
} finally{
try {
if(inputStream!=null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return properties;
}
/**
* 获取实例
*/
public static synchronized PropertiesUtil getInstance(String propertiesPath){
PropertiesUtil propertiesUtil = propertiesUtilsHolder.get(propertiesPath);
if(null==propertiesUtil){
LOGGER.info("[PropertiesUtil] instance is null with propertiesPath={},will create a new instance directly.",propertiesPath);
InputStream inputStream = null;
try{
propertiesUtil = new PropertiesUtil();
Properties properties = new Properties();
inputStream = PropertiesUtil.class.getResourceAsStream(propertiesPath);
if(inputStream!=null){
properties.load(inputStream);
propertiesUtilsHolder.put(propertiesPath, propertiesUtil);
propertiesMap.put(propertiesUtil, properties);
LOGGER.info("[PropertiesUtil] instance init success.");
propertiesUtil.propertiesLoaded = true;
}
} catch (Exception e) {
LOGGER.error("[PropertiesUtil] getInstance error,cause:{}",e.getMessage(),e);
} finally{
try {
if(inputStream!=null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return propertiesUtil;
}
/**
* 获得配置信息的String值
*/
public String getString(String key){
if(propertiesLoaded()){
Properties properties = propertiesMap.get(this);
return null != properties ? properties.getProperty(key) : null;
}
return null;
}
/**
* 获得配置信息的boolean值
*/
public boolean getBoolean(String key){
String value = getString(key);
return "true".equalsIgnoreCase(value);
}
/**
* 获得配置信息的int值
*/
public int getInt(String key,int defaultValue){
String value = getString(key);
int intValue;
try{
intValue = Integer.parseInt(value);
}catch(Exception e){
intValue = defaultValue;
}
return intValue;
}
/**
* 获得配置信息的long值
*/
public long getLong(String key,long defaultValue){
String value = getString(key);
long longValue;
try{
longValue = Long.parseLong(value);
}catch(Exception e){
longValue = defaultValue;
}
return longValue;
}
public static void main(String[] args) {
int loopTimes = 200;
final CountDownLatch latch = new CountDownLatch(loopTimes);
class Runner implements Runnable{
private Logger logger = LoggerFactory.getLogger(Runner.class);
@Override
public void run() {
String property = PropertiesUtil.getInstance("/redant.properties").getString("bean.scan.package");
logger.info("property={},currentThread={}",property,Thread.currentThread().getName());
latch.countDown();
}
}
TagUtil.addTag("start");
for(int i=0;i<loopTimes;i++){
new Thread(new Runner()).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
TagUtil.addTag("end");
TagUtil.showCost("start","end");
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/util/TagUtil.java
================================================
package com.redant.core.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 记录两个标签之间所耗的时间
* @author houyi.wh
*
*/
public class TagUtil {
private final static Logger LOGGER = LoggerFactory.getLogger(TagUtil.class);
private static class GHandle{
public static Map<String,Long> tags = Collections.synchronizedMap(new HashMap<>());
}
/**
* 新增标签点
* @param tag 标签名称
*/
public static void addTag(String tag){
if(tag==null || tag.trim().length()==0){
throw new RuntimeException("标签名称不可以为空");
}
GHandle.tags.put(tag, System.currentTimeMillis());
}
/**
* 计算开始标签和结束标签之间的耗时
* @param startTag 开始标签名称
* @param endTag 结束标签名称,如果为空,以当前调用代码所在行设置默认标签名称并计算耗时
*/
public static void showCost(String startTag,String endTag){
if(startTag==null || startTag.trim().length()==0){
throw new RuntimeException("开始标签名称不可以为空");
}
if(endTag==null || endTag.trim().length()==0){
String tempTag= "cur_"+System.currentTimeMillis();
addTag(tempTag);
endTag=tempTag;
}else if(!GHandle.tags.containsKey(endTag)){
addTag(endTag);
}
Long start= GHandle.tags.get(startTag);
Long end= GHandle.tags.get(endTag);
if(start==null){
throw new RuntimeException("获取标签["+startTag+"]信息失败!");
}
if(end==null){
throw new RuntimeException("获取标签["+endTag+"]信息失败!");
}
long cost = end-start;
LOGGER.info("from ["+startTag+"] to ["+endTag+"] cost ["+cost+"ms]");
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/util/ThreadUtil.java
================================================
package com.redant.core.common.util;
/**
* 线程工具类
* @author houyi.wh
* @date 2017-10-20
*/
public class ThreadUtil {
/**
* 获取当前线程名称
* @return 线程名称
*/
public static String currentThreadName(){
return Thread.currentThread().getName();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/view/HtmlKeyHolder.java
================================================
package com.redant.core.common.view;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public interface HtmlKeyHolder {
/**
* 未转义
*/
String START_NO_ESCAPE = "#[";
/**
* 对[转义
*/
String START_ESCAPE = "#\\[";
String END = "]";
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/view/Page404.java
================================================
package com.redant.core.common.view;
import cn.hutool.core.util.StrUtil;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public final class Page404 {
private Page404(){
}
public static final String HTML;
static{
StringBuffer sb = new StringBuffer();
sb.append("<!DOCTYPE html>").append(StrUtil.CRLF)
.append("<html lang=\"en\">").append(StrUtil.CRLF)
.append("<head>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<meta charset=\"UTF-8\">").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<title>404-Resource Not Found</title>").append(StrUtil.CRLF)
.append("</head>").append(StrUtil.CRLF)
.append("<body>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<div>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append(StrUtil.TAB).append("Resource Not Found!").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("</div>").append(StrUtil.CRLF)
.append("</body>").append(StrUtil.CRLF)
.append("</html>");
HTML = sb.toString();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/view/Page500.java
================================================
package com.redant.core.common.view;
import cn.hutool.core.util.StrUtil;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public final class Page500 {
private Page500(){
}
public static final String HTML;
static{
StringBuffer sb = new StringBuffer();
sb.append("<!DOCTYPE html>").append(StrUtil.CRLF)
.append("<html lang=\"en\">").append(StrUtil.CRLF)
.append("<head>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<meta charset=\"UTF-8\">").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<title>500-Server Internal Error</title>").append(StrUtil.CRLF)
.append("</head>").append(StrUtil.CRLF)
.append("<body>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<div>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append(StrUtil.TAB).append("Server Internal Error!").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("</div>").append(StrUtil.CRLF)
.append("</body>").append(StrUtil.CRLF)
.append("</html>");
HTML = sb.toString();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/view/PageError.java
================================================
package com.redant.core.common.view;
import cn.hutool.core.util.StrUtil;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public final class PageError {
private PageError(){
}
public static final String HTML;
static{
StringBuffer sb = new StringBuffer();
sb.append("<!DOCTYPE html>").append(StrUtil.CRLF)
.append("<html lang=\"en\">").append(StrUtil.CRLF)
.append("<head>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<meta charset=\"UTF-8\">").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<title>Error Occur</title>").append(StrUtil.CRLF)
.append("</head>").append(StrUtil.CRLF)
.append("<body>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<div>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append(StrUtil.TAB).append("<p>").append("Error Occur,Cause:").append("</p>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append(StrUtil.TAB).append("<p>").append(HtmlKeyHolder.START_NO_ESCAPE+"errorMessage"+HtmlKeyHolder.END).append("</p>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("</div>").append(StrUtil.CRLF)
.append("</body>").append(StrUtil.CRLF)
.append("</html>");
HTML = sb.toString();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/common/view/PageIndex.java
================================================
package com.redant.core.common.view;
import cn.hutool.core.util.StrUtil;
/**
* @author houyi.wh
* @date 2017/12/1
**/
public final class PageIndex {
private PageIndex(){
}
public static final String HTML;
static{
StringBuffer sb = new StringBuffer();
sb.append("<!DOCTYPE html>").append(StrUtil.CRLF)
.append("<html lang=\"en\">").append(StrUtil.CRLF)
.append("<head>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<meta charset=\"UTF-8\">").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<title>redant</title>").append(StrUtil.CRLF)
.append("</head>").append(StrUtil.CRLF)
.append("<body>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("<div>").append(StrUtil.CRLF)
.append(StrUtil.TAB).append(StrUtil.TAB).append("Welcome to redant!").append(StrUtil.CRLF)
.append(StrUtil.TAB).append("</div>").append(StrUtil.CRLF)
.append("</body>").append(StrUtil.CRLF)
.append("</html>");
HTML = sb.toString();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/context/RedantContext.java
================================================
package com.redant.core.context;
import cn.hutool.core.collection.CollectionUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.util.concurrent.FastThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Set;
/**
* @author houyi
**/
public class RedantContext {
private static final Logger LOGGER = LoggerFactory.getLogger(RedantContext.class);
/**
* 使用FastThreadLocal替代JDK自带的ThreadLocal以提升并发性能
*/
private static final FastThreadLocal<RedantContext> CONTEXT_HOLDER = new FastThreadLocal<>();
private HttpRequest request;
private ChannelHandlerContext context;
private HttpResponse response;
private Set<Cookie> cookies;
private RedantContext(){
}
public RedantContext setRequest(HttpRequest request){
this.request = request;
return this;
}
public RedantContext setContext(ChannelHandlerContext context){
this.context = context;
return this;
}
public RedantContext setResponse(HttpResponse response){
this.response = response;
return this;
}
public RedantContext addCookie(Cookie cookie){
if(cookie!=null){
if(CollectionUtil.isEmpty(cookies)){
cookies = new HashSet<>();
}
cookies.add(cookie);
}
return this;
}
public RedantContext addCookies(Set<Cookie> cookieSet){
if(CollectionUtil.isNotEmpty(cookieSet)){
if(CollectionUtil.isEmpty(cookies)){
cookies = new HashSet<>();
}
cookies.addAll(cookieSet);
}
return this;
}
public HttpRequest getRequest() {
return request;
}
public ChannelHandlerContext getContext() {
return context;
}
public HttpResponse getResponse() {
return response;
}
public Set<Cookie> getCookies() {
return cookies;
}
public static RedantContext currentContext(){
RedantContext context = CONTEXT_HOLDER.get();
if(context==null){
context = new RedantContext();
CONTEXT_HOLDER.set(context);
}
return context;
}
public static void clear(){
CONTEXT_HOLDER.remove();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/controller/ControllerProxy.java
================================================
package com.redant.core.controller;
import com.redant.core.common.enums.RequestMethod;
import com.redant.core.render.RenderType;
import java.lang.reflect.Method;
/**
* 路由请求代理,用以根据路由调用具体的controller类
* @author houyi.wh
* @date 2017-10-20
*/
public class ControllerProxy {
private RenderType renderType;
private RequestMethod requestMethod;
private Object controller;
private Method method;
private String methodName;
public RenderType getRenderType() {
return renderType;
}
public void setRenderType(RenderType renderType) {
this.renderType = renderType;
}
public RequestMethod getRequestMethod() {
return requestMethod;
}
public void setRequestMethod(RequestMethod requestMethod) {
this.requestMethod = requestMethod;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
@Override
public String toString() {
return "{requestMethod:"+requestMethod+",controller:"+controller.getClass().getName()+",methodName:"+methodName+"}";
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/controller/ProxyInvocation.java
================================================
package com.redant.core.controller;
import com.redant.core.common.exception.InvocationException;
import com.redant.core.common.exception.ValidationException;
import com.redant.core.common.util.GenericsUtil;
import com.redant.core.common.util.HttpRequestUtil;
import com.redant.core.context.RedantContext;
import com.redant.core.converter.PrimitiveConverter;
import com.redant.core.converter.PrimitiveTypeUtil;
import com.redant.core.controller.annotation.Param;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* 封装了ControllerProxy的调用过程
* @author houyi.wh
* @date 2017-10-20
*/
public class ProxyInvocation {
private static Invocation invocation = new Invocation();
public static Object invoke(ControllerProxy proxy) throws Exception{
Object controller = proxy.getController();
Method method = proxy.getMethod();
String methodName = proxy.getMethodName();
return invocation.invoke(controller,method,methodName);
}
private static class Invocation {
private static Logger logger = LoggerFactory.getLogger(Invocation.class);
private final int KEY_VALUE_SIZE = 2;
public Invocation(){
}
/**
* 获得方法调用的参数
* @param method 方法
* @param parameterTypes 方法参数类型
* @return 参数
* @throws Exception 参数异常
*/
private Object[] getParameters(Method method,Class<?>[] parameterTypes) throws Exception {
//用于存放调用参数的对象数组
Object[] parameters = new Object[parameterTypes.length];
//获得所调用方法的参数的Annotation数组
Annotation[][] annotationArray = method.getParameterAnnotations();
//获取参数列表
Map<String, List<String>> paramMap = HttpRequestUtil.getParameterMap(RedantContext.currentContext().getRequest());
//构造调用所需要的参数数组
for (int i = 0; i < parameterTypes.length; i++) {
Object parameter;
Class<?> type = parameterTypes[i];
Annotation[] annotation = annotationArray[i];
// 如果该参数没有 Param 注解
if (annotation == null || annotation.length == 0) {
// 如果该参数类型是基础类型,则需要加 Param 注解
if(PrimitiveTypeUtil.isPriType(type)){
logger.warn("Must specify a @Param annotation for primitive type parameter in method={}", method.getName());
continue;
}
// 封装对象类型的parameter
parameter = type.newInstance();
BeanUtils.populate(parameter,paramMap);
parameters[i] = parameter;
}else{
Param param = (Param) annotation[0];
try{
// 生成当前的调用参数
parameter = parseParameter(paramMap, type, param, method, i);
if(param.notNull()){
GenericsUtil.checkNull(param.key(), parameter);
}
if(param.notBlank()){
GenericsUtil.checkBlank(param.key(), parameter);
}
parameters[i] = parameter;
}catch(Exception e){
logger.error("param ["+param.key()+"] is invalid,cause:"+e.getMessage());
throw new IllegalArgumentException("参数 "+param.key()+" 不合法:"+e.getMessage());
}
}
}
return parameters;
}
/**
* GET 参数解析
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private Object parseParameter(Map<String, List<String>> paramMap, Class<?> type, Param param, Method method, int index) throws InstantiationException, IllegalAccessException{
Object value = null;
String key = param.key();
String defaultValue= param.defaultValue();
if(key.length() > 0){
// 如果参数是map类型
if(Map.class.isAssignableFrom(type)){
if(index > 0){
throw new ValidationException("Must have only one Map type parameter");
}
List<Class> types = GenericsUtil.getMethodGenericParameterTypes(method, index);
if(types.size() == KEY_VALUE_SIZE && (types.get(0) != String.class || types.get(1) != String.class)){
throw new ValidationException("Map type parameter must both be String, Occurring Point: " + method.toGenericString());
}
Map<String, String> valueMap = new HashMap<String, String>(paramMap.size());
for(Map.Entry<String, List<String>> entry : paramMap.entrySet()){
List<String> valueList = entry.getValue();
valueMap.put(entry.getKey(), valueList.get(0));
}
value = valueMap;
}else{
List<String> params = paramMap.get(key);
if(params != null){
// 基础类型
if(PrimitiveTypeUtil.isPriType(type)){
value = PrimitiveConverter.getInstance().convert(params.get(0), type);
// 数组
}else if(type.isArray()){
String[] strArray = params.toArray(new String[]{});
value = PrimitiveConverter.getInstance().convert(strArray, type);
// List
}else if(List.class.isAssignableFrom(type)){
List<Object> list;
List<Class> types = GenericsUtil.getMethodGenericParameterTypes(method, index);
Class<?> listType = types.size() == 1?types.get(0):String.class;
if(List.class == type){
list = new ArrayList<Object>();
}else{
list = (List<Object>) type.newInstance();
}
for (String param1 : params) {
if (param1.length() > 0) {
list.add(PrimitiveConverter.getInstance().convert(param1, listType));
}
}
value = list;
}
}else{
if(PrimitiveTypeUtil.isPriType(type)){
value = PrimitiveConverter.getInstance().convert(defaultValue, type);
}
}
}
}
return value;
}
/**
* 返回调用异常
* @param msg 消息
* @param cause 异常
* @return 调用异常
*/
private InvocationException getInvokeException(String msg, Throwable cause){
return new InvocationException(msg,cause);
}
//==================================
/**
* 执行方法的调用
* @param controller 控制器
* @param method 方法
* @param methodName 方法名
* @return 渲染结果
* @throws Exception 异常
*/
Object invoke(Object controller, Method method, String methodName) throws Exception {
if (method == null) {
throw new NoSuchMethodException("Can not find specified method: " + methodName);
}
Class<?> clazz = controller.getClass();
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] parameters = null;
Object result;
try {
parameters = getParameters(method,parameterTypes);
// 使用 CGLib 执行反射调用
FastClass fastClass = FastClass.create(clazz);
FastMethod fastMethod = fastClass.getMethod(methodName, parameterTypes);
// 调用,并得到调用结果
result = fastMethod.invoke(controller, parameters);
} catch(InvocationTargetException e){
String msg = "调用出错,请求类["+controller.getClass().getName()+"],方法名[" + method.getName() + "],参数[" + Arrays.toString(parameters)+"]";
throw getInvokeException(msg, e);
}
return result;
}
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/controller/annotation/Controller.java
================================================
package com.redant.core.controller.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
/**
* 请求uri
* @return url
*/
String path() default "";
}
================================================
FILE: redant-core/src/main/java/com/redant/core/controller/annotation/Mapping.java
================================================
package com.redant.core.controller.annotation;
import com.redant.core.common.enums.RequestMethod;
import com.redant.core.render.RenderType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Mapping {
/**
* 请求方法类型
* @return 方法类型
*/
RequestMethod requestMethod() default RequestMethod.GET;
/**
* 请求的uri
* @return url
*/
String path() default "";
/**
* 返回类型
* @return 返回类型
*/
RenderType renderType() default RenderType.JSON;
}
================================================
FILE: redant-core/src/main/java/com/redant/core/controller/annotation/Param.java
================================================
package com.redant.core.controller.annotation;
import java.lang.annotation.*;
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Param {
/**
* 将使用什么样的键值读取对象,
* 对于field,就是他名字
* 对于method的parameter,需要指明
* @return 参数的key
*/
String key() default "";
/**
* 提供设置缺省值
* @return 提供设置缺省值
*/
String defaultValue() default "";
/**
* 是否校验参数为空
* @return true:校验参数 false:不校验参数
*/
boolean notNull() default false;
/**
* 是否校验参数为空
* @return true:校验参数 false:不校验参数
*/
boolean notBlank() default false;
}
================================================
FILE: redant-core/src/main/java/com/redant/core/controller/context/ControllerContext.java
================================================
package com.redant.core.controller.context;
import com.redant.core.controller.ControllerProxy;
import io.netty.handler.codec.http.HttpMethod;
/**
* @author houyi.wh
* @date 2019-01-15
*/
public interface ControllerContext {
/**
* 添加Controller代理
* @param path 请求路径
* @param proxy 代理
*/
void addProxy(String path,ControllerProxy proxy);
/**
* 获取Controller代理
* @param method 请求方法类型
* @param uri 请求url
* @return 代理
*/
ControllerProxy getProxy(HttpMethod method, String uri);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/controller/context/DefaultControllerContext.java
================================================
package com.redant.core.controller.context;
import com.redant.core.controller.ControllerProxy;
import com.redant.core.render.RenderType;
import com.redant.core.router.RouteResult;
import com.redant.core.router.context.DefaultRouterContext;
import com.redant.core.router.context.RouterContext;
import io.netty.handler.codec.http.HttpMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author houyi.wh
* @date 2019-01-15
*/
public class DefaultControllerContext implements ControllerContext {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultControllerContext.class);
/**
* 保存所有的RouterController的代理类
*/
private static Map<String, ControllerProxy> proxyMap;
/**
* 路由上下文
*/
private static RouterContext routerContext;
private static final class DefaultControllerContextHolder {
private static DefaultControllerContext context = new DefaultControllerContext();
}
private DefaultControllerContext() {
routerContext = DefaultRouterContext.getInstance();
proxyMap = new ConcurrentHashMap<>();
}
public static ControllerContext getInstance() {
return DefaultControllerContextHolder.context;
}
@Override
public void addProxy(String path, ControllerProxy proxy) {
proxyMap.putIfAbsent(path, proxy);
}
@Override
public ControllerProxy getProxy(HttpMethod method, String uri) {
RouteResult<RenderType> routeResult = routerContext.getRouteResult(method, uri);
if (routeResult == null) {
return null;
}
// 获取代理
ControllerProxy controllerProxy = proxyMap.get(routeResult.decodedPath());
LOGGER.debug("\n========================= getControllerProxy =========================" +
"\nmethod={}, uri={}" +
"\ncontrollerProxy={}" +
"\n========================= getControllerProxy =========================",
method, uri, controllerProxy);
return controllerProxy;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/converter/AbstractConverter.java
================================================
package com.redant.core.converter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
/**
* 转换器的抽象实现类
* @author houyi.wh
* @date 2017-10-20
*/
public abstract class AbstractConverter implements Converter {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 实现了TypeConverter中的相同方法
*/
@Override
public Object convert(Object source, Class<?> toType, Object... parmas) {
/**
* 如果对象本身已经是所指定的类型则不进行转换直接返回
* 如果对象能够被复制,则返回复制后的对象
*/
if (source != null && toType.isInstance(source)) {
if (source instanceof Cloneable) {
if(source.getClass().isArray() && source.getClass().getComponentType() == String.class){
// 字符串数组虽然是Cloneable的子类,但并没有clone方法
return source;
}
try {
Method m = source.getClass().getDeclaredMethod("clone", new Class[0]);
m.setAccessible(true);
return m.invoke(source, new Object[0]);
} catch (Exception e) {
logger.debug("Can not clone object " + source, e);
}
}
return source;
}
/**
* 如果需要转换,且value为String类型并且长度为0,则按照null值进行处理
*/
if (source != null && source instanceof String && ((String)source).length() == 0) {
source = null;
}
/**
* 不对Annotation, Interface,
* Enummeration类型进行转换。
*/
if (toType == null || (source == null && !toType.isPrimitive())
|| toType.isInterface() || toType.isAnnotation()
|| toType.isEnum()) {
return null;
}
return doConvertValue(source, toType);
}
/**
* 需要被子类所实现的转换方法
* @param source 需要进行类型转换的对象
* @param toType 需要被转换成的类型
* @param params 转值时需要提供的可选参数
* @return 转换后所生成的对象,如果不能够进行转换则返回null
*/
protected abstract Object doConvertValue(Object source, Class<?> toType, Object... params);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/converter/Converter.java
================================================
package com.redant.core.converter;
/**
* 类型转换器所需要实现的总接口。TypeConverter中有唯一的一个方法,实现类请特别注意方法所需要返回的值。
* @author houyi.wh
* @date 2017-10-20
*/
public interface Converter {
/**
* 类型转换
* @param source 需要被转换的值
* @param toType 需要被转换成的类型
* @param params 转值时需要提供的可选参数
* @return 经转换过的类型,如果实现类没有能力进行所指定的类型转换,应返回null
*/
Object convert(Object source, Class<?> toType, Object... params);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/converter/PrimitiveConverter.java
================================================
package com.redant.core.converter;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 基本类型和基本类型数组转换器
* 能够转换的基本类型仅限于com.sitechasia.webx.core.utils.populator.PrimitiveType中所定义的类型
*
* @see # com.sitechasia.webx.core.utils.populator.PrimitiveTypeUtil
* @author houyi.wh
* @date 2017-10-20
*/
public final class PrimitiveConverter extends AbstractConverter {
private static final PrimitiveConverter CONVERTER;
static {
CONVERTER = new PrimitiveConverter();
}
private static final char[] DIGITAL_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
/**
* 对基本类型进行类型转换
*/
@Override
@SuppressWarnings("unused")
protected Object doConvertValue(Object source, Class<?> toType, Object... params) {
/**
* 如果是基础类型,则直接返回
*/
if (source != null && (!PrimitiveTypeUtil.isPriType(source.getClass()) || !PrimitiveTypeUtil.isPriType(toType))) {
return null;
}
/**
* 如果都是数组类型,则构造数组
*/
if (source != null && source.getClass().isArray() && toType.isArray()) {
Object result;
Class<?> componentType = toType.getComponentType();
result = Array.newInstance(componentType, Array.getLength(source));
for (int i = 0; i < Array.getLength(source); i++) {
Array.set(result, i, convert(Array.get(source, i),componentType, params));
}
return result;
}
return doConvert(source, toType);
}
private boolean isNumberString(String stringValue) {
if (stringValue == null || stringValue.length() == 0){
return false;
}
OUTER: for (char charInString : stringValue.toCharArray()) {
for (char digit : DIGITAL_CHAR) {
if (charInString == digit){
continue OUTER;
}
}
return false;
}
return true;
}
private boolean booleanValue(Object source) {
if (source == null){
return false;
}
Class<? extends Object> c = source.getClass();
if (c == Boolean.class){
return (Boolean) source;
}
if (c == String.class) {
String stringValue = (String) source;
return !(stringValue.length() == 0
|| stringValue.equals("0")
|| stringValue.equalsIgnoreCase("false")
|| stringValue.equalsIgnoreCase("no")
|| stringValue.equalsIgnoreCase("f") || stringValue
.equalsIgnoreCase("n"));
}
if (c == Character.class){
return ((Character) source).charValue() != 0;
}
if (source instanceof Number){
return ((Number) source).doubleValue() != 0;
}
return true;
}
private long longValue(Object source) throws NumberFormatException {
if (source == null){
return 0L;
}
Class<? extends Object> c = source.getClass();
if (c.getSuperclass() == Number.class){
return ((Number) source).longValue();
}
if (c == Boolean.class){
return ((Boolean) source).booleanValue() ? 1 : 0;
}
if (c == Character.class){
return ((Character) source).charValue();
}
String s = stringValue(source, true);
return (s.length() == 0) ? 0L : Long.parseLong(s);
}
private double doubleValue(Object source) throws NumberFormatException {
if (source == null){
return 0.0;
}
Class<? extends Object> c = source.getClass();
if (c.getSuperclass() == Number.class){
return ((Number) source).doubleValue();
}
if (c == Boolean.class){
return ((Boolean) source).booleanValue() ? 1 : 0;
}
if (c == Character.class){
return ((Character) source).charValue();
}
String s = stringValue(source, true);
return (s.length() == 0) ? 0.0 : Double.parseDouble(s);
}
private BigInteger bigIntValue(Object source) throws NumberFormatException {
if (source == null){
return BigInteger.valueOf(0L);
}
Class<? extends Object> c = source.getClass();
if (c == BigInteger.class){
return (BigInteger) source;
}
if (c == BigDecimal.class){
return ((BigDecimal) source).toBigInteger();
}
if (c.getSuperclass() == Number.class){
return BigInteger.valueOf(((Number) source).longValue());
}
if (c == Boolean.class){
return BigInteger.valueOf(((Boolean) source).booleanValue() ? 1 : 0);
}
if (c == Character.class){
return BigInteger.valueOf(((Character) source).charValue());
}
String s = stringValue(source, true);
return (s.length() == 0) ? BigInteger.valueOf(0L) : new BigInteger(s);
}
private BigDecimal bigDecValue(Object source) throws NumberFormatException {
if (source == null){
return BigDecimal.valueOf(0L);
}
Class<? extends Object> c = source.getClass();
if (c == BigDecimal.class){
return (BigDecimal) source;
}
if (c == BigInteger.class){
return new BigDecimal((BigInteger) source);
}
if (c.getSuperclass() == Number.class){
return new BigDecimal(((Number) source).doubleValue());
}
if (c == Boolean.class){
return BigDecimal.valueOf(((Boolean) source).booleanValue() ? 1 : 0);
}
if (c == Character.class){
return BigDecimal.valueOf(((Character) source).charValue());
}
String s = stringValue(source, true);
return (s.length() == 0) ? BigDecimal.valueOf(0L) : new BigDecimal(s);
}
private String stringValue(Object source, boolean trim) {
String result;
if (source == null) {
result = null;
} else {
result = source.toString();
if (trim) {
result = result.trim();
}
}
return result;
}
private char charValue(Object source) {
char result;
if (source.getClass() == String.class && ((String) source).length() > 0 && !isNumberString((String) source)){
result = ((String) source).charAt(0);
}else{
result = (char) longValue(source);
}
return result;
}
private String stringValue(Object source) {
return stringValue(source, false);
}
private Object doConvert(Object source, Class<?> toType) {
Object result = null;
if (source != null) {
if ((toType == Integer.class) || (toType == Integer.TYPE)) {
result = (int) longValue(source);
}else if ((toType == Double.class) || (toType == Double.TYPE)){
result = doubleValue(source);
}else if ((toType == Boolean.class) || (toType == Boolean.TYPE)){
result = booleanValue(source);
}else if ((toType == Byte.class) || (toType == Byte.TYPE)){
result = (byte) longValue(source);
}else if ((toType == Character.class) || (toType == Character.TYPE)){
result = charValue(source);
}else if ((toType == Short.class) || (toType == Short.TYPE)){
result = (short) longValue(source);
}else if ((toType == Long.class) || (toType == Long.TYPE)){
result = longValue(source);
}else if ((toType == Float.class) || (toType == Float.TYPE)){
result = (float) doubleValue(source);
}else if (toType == BigInteger.class){
result = bigIntValue(source);
}else if (toType == BigDecimal.class){
result = bigDecValue(source);
}else if (toType == String.class){
result = stringValue(source);
}
} else {
if (toType.isPrimitive()) {
result = PrimitiveTypeUtil.getPriDefaultValue(toType);
}
}
return result;
}
public static PrimitiveConverter getInstance(){
return CONVERTER;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/converter/PrimitiveTypeUtil.java
================================================
package com.redant.core.converter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
/**
* 定义了基本类型的工具类,
* 可以方便的判断一个Class对象是否属于基本类型或基本类型的数组。
* 本工具类所包含的基本类型判断包括如下一些内容:
*
* String
* boolean
* byte
* short
* int
* long
* float
* double
* char
* Boolean
* Byte
* Short
* Integer
* Long
* Float
* Double
* Character
* BigInteger
* BigDecimal
*
* @author yunfeng.cheng
* @date 2016-08-12
*/
public class PrimitiveTypeUtil {
/**
* 私有的构造函数防止用户进行实例化。
*/
private PrimitiveTypeUtil() {}
/** 基本类型 **/
private static final Class<?>[] PRI_TYPE = {
String.class,
boolean.class,
byte.class,
short.class,
int.class,
long.class,
float.class,
double.class,
char.class,
Boolean.class,
Byte.class,
Short.class,
Integer.class,
Long.class,
Float.class,
Double.class,
Character.class,
BigInteger.class,
BigDecimal.class
};
/** 基本数组类型 **/
private static final Class<?>[] PRI_ARRAY_TYPE = {
String[].class,
boolean[].class,
byte[].class,
short[].class,
int[].class,
long[].class,
float[].class,
double[].class,
char[].class,
Boolean[].class,
Byte[].class,
Short[].class,
Integer[].class,
Long[].class,
Float[].class,
Double[].class,
Character[].class,
BigInteger[].class,
BigDecimal[].class
};
/**
* 基本类型默认值
*/
private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>(9);
static {
primitiveDefaults.put(boolean.class, false);
primitiveDefaults.put(byte.class, (byte)0);
primitiveDefaults.put(short.class, (short)0);
primitiveDefaults.put(char.class, (char)0);
primitiveDefaults.put(int.class, 0);
primitiveDefaults.put(long.class, 0L);
primitiveDefaults.put(float.class, 0.0f);
primitiveDefaults.put(double.class, 0.0);
}
/**
* 判断是否为基本类型
* @param cls 需要进行判断的Class对象
* @return 是否为基本类型
*/
public static boolean isPriType(Class<?> cls) {
for (Class<?> priType : PRI_TYPE) {
if (cls == priType){
return true;
}
}
return false;
}
/**
* 判断是否为基本类型数组
* @param cls 需要进行判断的Class对象
* @return 是否为基本类型数组
*/
public static boolean isPriArrayType(Class<?> cls) {
for (Class<?> priType : PRI_ARRAY_TYPE) {
if (cls == priType){
return true;
}
}
return false;
}
/**
* 获得基本类型的默认值
* @param type 基本类型的Class
* @return 基本类型的默认值
*/
public static Object getPriDefaultValue(Class<?> type) {
return primitiveDefaults.get(type);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/cookie/CookieManager.java
================================================
package com.redant.core.cookie;
import io.netty.handler.codec.http.cookie.Cookie;
import java.util.Map;
import java.util.Set;
/**
* cookie管理
* @author houyi.wh
* @date 2019-01-16
*/
public interface CookieManager {
/**
* 获取所有的cookie
* @return cookie集合
*/
Set<Cookie> getCookies();
/**
* 获取所有的cookie,并返回一个Map
* @return cookie的map
*/
Map<String,Cookie> getCookieMap();
/**
* 根据名称获取cookie
* @param name 名称
* @return cookie
*/
Cookie getCookie(String name);
/**
* 根据名称获取cookie的值
* @param name 名称
* @return cookie的值
*/
String getCookieValue(String name);
/**
* 设置cookie到响应结果中
* @param cookie cookie
*/
void setCookie(Cookie cookie);
/**
* 获取所有的cookie后,全部设置到响应结果中
*/
void setCookies();
/**
* 添加一个cookie
* @param name cookie的名称
* @param value cookie的值
*/
void addCookie(String name,String value);
/**
* 添加一个cookie
* @param name cookie的名称
* @param value cookie的值
* @param domain cookie的作用域
*/
void addCookie(String name,String value,String domain);
/**
* 添加一个cookie
* @param name cookie的名称
* @param value cookie的值
* @param maxAge cookie的有效期
*/
void addCookie(String name,String value,long maxAge);
/**
* 添加一个cookie
* @param name cookie的名称
* @param value cookie的值
* @param domain cookie的作用域
* @param maxAge cookie的有效期
*/
void addCookie(String name,String value,String domain,long maxAge);
/**
* 删除一个cookie
* @param name cookie的名称
* @return 操作结果
*/
boolean deleteCookie(String name);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/cookie/DefaultCookieManager.java
================================================
package com.redant.core.cookie;
import cn.hutool.core.util.StrUtil;
import com.redant.core.context.RedantContext;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.DefaultCookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Cookie管理器
*
* @author houyi.wh
* @date 2019-01-16
*/
public class DefaultCookieManager implements CookieManager {
private static final class DefaultCookieManagerHolder {
private static DefaultCookieManager cookieManager = new DefaultCookieManager();
}
private DefaultCookieManager() {
}
public static CookieManager getInstance() {
return DefaultCookieManagerHolder.cookieManager;
}
@Override
public Set<Cookie> getCookies() {
HttpRequest request = RedantContext.currentContext().getRequest();
Set<Cookie> cookies = new HashSet<>();
if (request != null) {
String value = request.headers().get(HttpHeaderNames.COOKIE);
if (value != null) {
cookies = ServerCookieDecoder.STRICT.decode(value);
}
}
return cookies;
}
@Override
public Map<String, Cookie> getCookieMap() {
Map<String, Cookie> cookieMap = new HashMap<>();
Set<Cookie> cookies = getCookies();
if (null != cookies && !cookies.isEmpty()) {
for (Cookie cookie : cookies) {
cookieMap.put(cookie.name(), cookie);
}
}
return cookieMap;
}
@Override
public Cookie getCookie(String name) {
Map<String, Cookie> cookieMap = getCookieMap();
return cookieMap.getOrDefault(name, null);
}
@Override
public String getCookieValue(String name) {
Cookie cookie = getCookie(name);
return cookie == null ? null : cookie.value();
}
@Override
public void setCookie(Cookie cookie) {
RedantContext.currentContext().addCookie(cookie);
}
@Override
public void setCookies() {
Set<Cookie> cookies = getCookies();
if (!cookies.isEmpty()) {
for (Cookie cookie : cookies) {
setCookie(cookie);
}
}
}
@Override
public void addCookie(String name, String value) {
addCookie(name, value, null);
}
@Override
public void addCookie(String name, String value, String domain) {
addCookie(name, value, domain, 0);
}
@Override
public void addCookie(String name, String value, long maxAge) {
addCookie(name, value, null, maxAge);
}
@Override
public void addCookie(String name, String value, String domain, long maxAge) {
if (StrUtil.isNotBlank(name) && StrUtil.isNotBlank(value)) {
Cookie cookie = new DefaultCookie(name, value);
cookie.setPath("/");
if (domain != null && domain.trim().length() > 0) {
cookie.setDomain(domain);
}
if (maxAge > 0) {
cookie.setMaxAge(maxAge);
}
setCookie(cookie);
}
}
@Override
public boolean deleteCookie(String name) {
Cookie cookie = getCookie(name);
if (cookie != null) {
cookie.setMaxAge(0);
cookie.setPath("/");
setCookie(cookie);
return true;
}
return false;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/executor/AbstractExecutor.java
================================================
package com.redant.core.executor;
import com.redant.core.common.constants.CommonConstants;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author houyi
*/
public abstract class AbstractExecutor<T> implements Executor<T> {
private final static Logger LOGGER = LoggerFactory.getLogger(AbstractExecutor.class);
private java.util.concurrent.Executor eventExecutor;
public AbstractExecutor() {
this(null);
}
public AbstractExecutor(java.util.concurrent.Executor eventExecutor) {
this.eventExecutor = eventExecutor == null ? EventExecutorHolder.eventExecutor : eventExecutor;
}
@Override
public T execute(Object... request) {
return doExecute(request);
}
@Override
public Future<T> asyncExecute(Promise<T> promise, Object... request) {
if (promise == null) {
throw new IllegalArgumentException("promise should not be null");
}
// 异步执行
eventExecutor.execute(new Runnable() {
@Override
public void run() {
try {
T response = doExecute(request);
promise.setSuccess(response);
} catch (Exception e) {
promise.setFailure(e);
}
}
});
// 返回promise
return promise;
}
/**
* 执行具体的方法
*
* @param request 请求对象
* @return 返回结果
*/
public abstract T doExecute(Object... request);
private static final class EventExecutorHolder {
private static java.util.concurrent.Executor eventExecutor = new ThreadPoolExecutor(
CommonConstants.EVENT_EXECUTOR_POOL_CORE_SIZE,
CommonConstants.EVENT_EXECUTOR_POOL_MAX_SIZE,
CommonConstants.EVENT_EXECUTOR_POOL_KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(CommonConstants.EVENT_EXECUTOR_POOL_BLOCKING_QUEUE_SIZE));
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/executor/Executor.java
================================================
package com.redant.core.executor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
/**
* @author houyi
*/
public interface Executor<T> {
/**
* 同步执行任务获得结果
* @param request 请求对象
* @return 结果
*/
T execute(Object... request);
/**
* 异步执行任务获得 Future 结果
* @param promise 异步结果包装类
* @param request 请求对象
* @return 异步结果
*/
Future<T> asyncExecute(Promise<T> promise, Object... request);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/executor/HttpResponseExecutor.java
================================================
package com.redant.core.executor;
import cn.hutool.core.collection.CollectionUtil;
import com.redant.core.common.exception.InvocationException;
import com.redant.core.common.util.HttpRenderUtil;
import com.redant.core.common.util.HttpRequestUtil;
import com.redant.core.context.RedantContext;
import com.redant.core.controller.ControllerProxy;
import com.redant.core.controller.ProxyInvocation;
import com.redant.core.controller.context.ControllerContext;
import com.redant.core.controller.context.DefaultControllerContext;
import com.redant.core.interceptor.InterceptorHandler;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author houyi
*/
public class HttpResponseExecutor extends AbstractExecutor<HttpResponse> {
private final static Logger LOGGER = LoggerFactory.getLogger(HttpResponseExecutor.class);
private static ControllerContext controllerContext = DefaultControllerContext.getInstance();
public static HttpResponseExecutor getInstance() {
return HttpResponseExecutorHolder.executor;
}
private HttpResponseExecutor() {
}
@Override
public HttpResponse doExecute(Object... request) {
HttpRequest httpRequest = (HttpRequest) request[0];
// 暂存请求对象
// 将request存储到ThreadLocal中去,便于后期在其他地方获取并使用
RedantContext.currentContext().setRequest(httpRequest);
HttpResponse response = null;
try {
// 获取参数列表
Map<String, List<String>> paramMap = HttpRequestUtil.getParameterMap(httpRequest);
// 处理拦截器的前置方法
if (!InterceptorHandler.preHandle(paramMap)) {
// 先从RedantContext中获取response,检查用户是否设置了response
response = RedantContext.currentContext().getResponse();
// 若用户没有设置就返回一个默认的
if (response == null) {
response = HttpRenderUtil.getBlockedResponse();
}
} else {
// 处理业务逻辑
response = invoke(httpRequest);
// 处理拦截器的后置方法
InterceptorHandler.postHandle(paramMap);
}
} catch (Exception e) {
LOGGER.error("Server Internal Error,cause:", e);
response = getErrorResponse(e);
} finally {
// 构造响应头
buildHeaders(response, RedantContext.currentContext());
// 释放ThreadLocal对象
RedantContext.clear();
}
return response;
}
private HttpResponse invoke(HttpRequest request) throws Exception {
// 根据路由获得具体的ControllerProxy
ControllerProxy controllerProxy = controllerContext.getProxy(request.method(), request.uri());
if (controllerProxy == null) {
return HttpRenderUtil.getNotFoundResponse();
}
// 调用用户自定义的Controller,获得结果
Object result = ProxyInvocation.invoke(controllerProxy);
return HttpRenderUtil.render(result, controllerProxy.getRenderType());
}
private HttpResponse getErrorResponse(Exception e) {
HttpResponse response;
if (e instanceof IllegalArgumentException || e instanceof InvocationException) {
response = HttpRenderUtil.getErrorResponse(e.getMessage());
} else {
response = HttpRenderUtil.getServerErrorResponse();
}
return response;
}
private void buildHeaders(HttpResponse response, RedantContext redantContext) {
if (response == null) {
return;
}
FullHttpResponse fullHttpResponse = (FullHttpResponse) response;
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(fullHttpResponse.content().readableBytes()));
// 写cookie
Set<Cookie> cookies = redantContext.getCookies();
if (CollectionUtil.isNotEmpty(cookies)) {
for (Cookie cookie : cookies) {
fullHttpResponse.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
}
}
}
private static final class HttpResponseExecutorHolder {
private static HttpResponseExecutor executor = new HttpResponseExecutor();
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/handler/ControllerDispatcher.java
================================================
package com.redant.core.handler;
import com.redant.core.common.constants.CommonConstants;
import com.redant.core.executor.Executor;
import com.redant.core.executor.HttpResponseExecutor;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.concurrent.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 请求分发控制器
*
* @author houyi.wh
* @date 2017-10-20
*/
public class ControllerDispatcher extends SimpleChannelInboundHandler<HttpRequest> {
private final static Logger LOGGER = LoggerFactory.getLogger(ControllerDispatcher.class);
private static Executor<HttpResponse> executor = HttpResponseExecutor.getInstance();
@Override
public void channelRead0(ChannelHandlerContext ctx, HttpRequest request) {
if (CommonConstants.ASYNC_EXECUTE_EVENT) {
// 当前通道所持有的线程池
EventExecutor channelExecutor = ctx.executor();
// 创建一个异步结果,并指定该promise
Promise<HttpResponse> promise = new DefaultPromise<>(channelExecutor);
// 在自定义线程池中执行业务逻辑,并返回一个异步结果
Future<HttpResponse> future = executor.asyncExecute(promise, request);
future.addListener(new GenericFutureListener<Future<HttpResponse>>() {
@Override
public void operationComplete(Future<HttpResponse> f) throws Exception {
// 异步结果执行成功后,取出结果
HttpResponse response = f.get();
// 通过IO线程写响应结果
ctx.channel().writeAndFlush(response);
}
});
} else {
// 同步执行
HttpResponse response = executor.execute(request);
ctx.channel().writeAndFlush(response);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
LOGGER.error("ctx close,cause:", cause);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/handler/ssl/SslContextHelper.java
================================================
package com.redant.core.handler.ssl;
import cn.hutool.crypto.SecureUtil;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author houyi.wh
* @date 2017/11/19
**/
public class SslContextHelper {
private static final String KEY_STORE_JKS = "JKS";
private static final String ALGORITHM = "SunX509";
private static Map<String,SslContext> contents = new ConcurrentHashMap<String,SslContext>();
private static String getKey(String keyPath,String keyPassword){
if(keyPath==null || keyPath.trim().length()==0 || keyPassword==null || keyPassword.trim().length()==0){
return null;
}
String keyStr = keyPath+"&"+keyPassword;
return SecureUtil.md5(keyStr);
}
/**
* 获取SslContext
* @param keyPath
* @param keyPassword
* @return
*/
public static SslContext getSslContext(String keyPath,String keyPassword){
if(keyPath==null || keyPath.trim().length()==0 || keyPassword==null || keyPassword.trim().length()==0){
return null;
}
SslContext sslContext = null;
InputStream is = null;
try {
String key = getKey(keyPath,keyPassword);
sslContext = contents.get(key);
if(sslContext!=null){
return sslContext;
}
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_JKS);
is = new FileInputStream(keyPath);
keyStore.load(is, keyPassword.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(ALGORITHM);
keyManagerFactory.init(keyStore,keyPassword.toCharArray());
sslContext = SslContextBuilder.forServer(keyManagerFactory).build();
if(sslContext!=null){
contents.put(key,sslContext);
}
} catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException | CertificateException | IOException e) {
e.printStackTrace();
} finally {
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sslContext;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/init/InitExecutor.java
================================================
package com.redant.core.init;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.ClassScaner;
import com.redant.core.common.constants.CommonConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author houyi.wh
* @date 2019-01-14
*/
public final class InitExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger(InitExecutor.class);
private static AtomicBoolean initialized = new AtomicBoolean(false);
public static void init() {
if (!initialized.compareAndSet(false, true)) {
return;
}
try {
Set<Class<?>> classSet = ClassScaner.scanPackageBySuper(CommonConstants.BEAN_SCAN_PACKAGE,InitFunc.class);
if (CollectionUtil.isNotEmpty(classSet)) {
List<OrderWrapper> initList = new ArrayList<>();
for (Class<?> cls : classSet) {
// 如果cls是InitFunc的实现类
if(!cls.isInterface() && InitFunc.class.isAssignableFrom(cls)){
Constructor<?> constructor = cls.getDeclaredConstructor();
constructor.setAccessible(true);
InitFunc initFunc = (InitFunc)constructor.newInstance();
LOGGER.info("[InitExecutor] found InitFunc: " + initFunc.getClass().getCanonicalName());
insertSorted(initList, initFunc);
}
}
for (OrderWrapper w : initList) {
w.func.init();
LOGGER.info("[InitExecutor] initialized: {} with order={}", w.func.getClass().getCanonicalName(), w.order);
}
}
} catch (Exception ex) {
LOGGER.warn("[InitExecutor] init failed", ex);
ex.printStackTrace();
} catch (Error error) {
LOGGER.warn("[InitExecutor] init failed with fatal error", error);
error.printStackTrace();
throw error;
}
}
private static void insertSorted(List<OrderWrapper> list, InitFunc func) {
int order = resolveOrder(func);
int idx = 0;
for (; idx < list.size(); idx++) {
// 将func插入到order值比他大的第一个func前面
if (list.get(idx).getOrder() > order) {
break;
}
}
list.add(idx, new OrderWrapper(order, func));
}
private static int resolveOrder(InitFunc func) {
if (!func.getClass().isAnnotationPresent(InitOrder.class)) {
return InitOrder.LOWEST_PRECEDENCE;
} else {
return func.getClass().getAnnotation(InitOrder.class).value();
}
}
private InitExecutor() {}
private static class OrderWrapper {
private final int order;
private final InitFunc func;
OrderWrapper(int order, InitFunc func) {
this.order = order;
this.func = func;
}
int getOrder() {
return order;
}
InitFunc getFunc() {
return func;
}
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/init/InitFunc.java
================================================
package com.redant.core.init;
/**
* 初始化接口
* @author houyi.wh
* @date 2019-01-14
*/
public interface InitFunc {
/**
* 初始化方法
*/
void init();
}
================================================
FILE: redant-core/src/main/java/com/redant/core/init/InitOrder.java
================================================
package com.redant.core.init;
import java.lang.annotation.*;
/**
* 初始化器的排序规则,升序排序
* @author houyi.wh
* @date 2019-01-14
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface InitOrder {
/**
* 最低优先级
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* 最高优先级
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* The order value. Lowest precedence by default.
*
* @return the order value
*/
int value() default LOWEST_PRECEDENCE;
}
================================================
FILE: redant-core/src/main/java/com/redant/core/interceptor/Interceptor.java
================================================
package com.redant.core.interceptor;
import java.util.List;
import java.util.Map;
/**
* @author houyi
**/
public abstract class Interceptor {
/**
* 拦截器的前置处理方法
*/
public boolean preHandle(Map<String, List<String>> paramMap){
return true;
}
/**
* 拦截器的后置处理方法
*/
public abstract void postHandle(Map<String, List<String>> paramMap);
}
================================================
FILE: redant-core/src/main/java/com/redant/core/interceptor/InterceptorBuilder.java
================================================
package com.redant.core.interceptor;
import java.util.List;
/**
* 用户可以通过该接口自行定义需要生效哪些拦截器
* @author houyi
**/
public interface InterceptorBuilder {
/**
* 构造拦截器列表
* @return 列表
*/
List<Interceptor> build();
}
================================================
FILE: redant-core/src/main/java/com/redant/core/interceptor/InterceptorHandler.java
================================================
package com.redant.core.interceptor;
import cn.hutool.core.collection.CollectionUtil;
import java.util.*;
/**
* @author houyi.wh
* @date 2017/11/15
**/
public class InterceptorHandler {
public static boolean preHandle(Map<String, List<String>> paramMap){
List<Interceptor> interceptors = InterceptorProvider.getInterceptors();
if(CollectionUtil.isEmpty(interceptors)){
return true;
}
for(Interceptor interceptor : interceptors){
if(!interceptor.preHandle(paramMap)){
return false;
}
}
return true;
}
public static void postHandle(Map<String, List<String>> paramMap){
List<Interceptor> interceptors = InterceptorProvider.getInterceptors();
if(CollectionUtil.isEmpty(interceptors)){
return;
}
for(Interceptor interceptor : interceptors){
interceptor.postHandle(paramMap);
}
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/interceptor/InterceptorProvider.java
================================================
package com.redant.core.interceptor;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.ClassScaner;
import com.redant.core.anno.Order;
import com.redant.core.common.constants.CommonConstants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author houyi
**/
public class InterceptorProvider {
private static volatile boolean loaded = false;
private static volatile InterceptorBuilder builder = null;
public static List<Interceptor> getInterceptors(){
// 优先获取用户自定义的 InterceptorBuilder 构造的 Interceptor
if(!loaded){
synchronized (InterceptorProvider.class) {
if(!loaded) {
Set<Class<?>> builders = ClassScaner.scanPackageBySuper(CommonConstants.INTERCEPTOR_SCAN_PACKAGE, InterceptorBuilder.class);
if (CollectionUtil.isNotEmpty(builders)) {
try {
for (Class<?> cls : builders) {
builder = (InterceptorBuilder) cls.newInstance();
break;
}
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
}
loaded = true;
}
}
}
if(builder!=null){
return builder.build();
}
// 获取不到时,再扫描所有指定目录下的 Interceptor
return InterceptorsHolder.interceptors;
}
static class InterceptorsHolder {
static List<Interceptor> interceptors;
static {
interceptors = scanInterceptors();
}
private static List<Interceptor> scanInterceptors() {
Set<Class<?>> classSet = ClassScaner.scanPackageBySuper(CommonConstants.INTERCEPTOR_SCAN_PACKAGE,Interceptor.class);
if(CollectionUtil.isEmpty(classSet)){
return Collections.emptyList();
}
List<InterceptorWrapper> wrappers = new ArrayList<>(classSet.size());
try {
for (Class<?> cls : classSet) {
Interceptor interceptor =(Interceptor)cls.newInstance();
insertSorted(wrappers,interceptor);
}
}catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return wrappers.stream()
.map(InterceptorWrapper::getInterceptor)
.collect(Collectors.toList());
}
private static void insertSorted(List<InterceptorWrapper> list, Interceptor interceptor) {
int order = resolveOrder(interceptor);
int idx = 0;
for (; idx < list.size(); idx++) {
// 将当前interceptor插入到order值比他大的第一个interceptor前面
if (list.get(idx).getOrder() > order) {
break;
}
}
list.add(idx, new InterceptorWrapper(order, interceptor));
}
private static int resolveOrder(Interceptor interceptor) {
if (!interceptor.getClass().isAnnotationPresent(Order.class)) {
return Order.LOWEST_PRECEDENCE;
} else {
return interceptor.getClass().getAnnotation(Order.class).value();
}
}
private static class InterceptorWrapper {
private final int order;
private final Interceptor interceptor;
InterceptorWrapper(int order, Interceptor interceptor) {
this.order = order;
this.interceptor = interceptor;
}
int getOrder() {
return order;
}
Interceptor getInterceptor() {
return interceptor;
}
}
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/render/RenderType.java
================================================
package com.redant.core.render;
/**
* 返回的响应类型
* @author houyi.wh
* @date 2017-10-20
*/
public enum RenderType {
/**
* JSON
*/
JSON("application/json;charset=UTF-8"),
/**
* XML
*/
XML("text/xml;charset=UTF-8"),
/**
* TEXT
*/
TEXT("text/plain;charset=UTF-8"),
/**
* HTML
*/
HTML("text/html;charset=UTF-8");
private String contentType;
RenderType(String contentType){
this.contentType = contentType;
}
public String getContentType() {
return contentType;
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/router/BadClientSilencer.java
================================================
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 com.redant.core.router;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
/**
* This utility handler should be put at the last position of the inbound pipeline to
* catch all exceptions caused by bad client (closed connection, malformed request etc.)
* and server processing, then close the connection.
*
* By default exceptions are logged to Netty internal LOGGER. You may need to override
* {@link #onUnknownMessage(Object)}, {@link #onBadClient(Throwable)}, and
* {@link #onBadServer(Throwable)} to log to more suitable places.
*/
@Sharable
public class BadClientSilencer extends SimpleChannelInboundHandler<Object> {
private static final InternalLogger log = InternalLoggerFactory.getInstance(BadClientSilencer.class);
/** Logs to Netty internal LOGGER. Override this method to log to other places if you want. */
protected void onUnknownMessage(Object msg) {
log.warn("Unknown msg: " + msg);
}
/** Logs to Netty internal LOGGER. Override this method to log to other places if you want. */
protected void onBadClient(Throwable e) {
log.warn("Caught exception (maybe client is bad)", e);
}
/** Logs to Netty internal LOGGER. Override this method to log to other places if you want. */
protected void onBadServer(Throwable e) {
log.warn("Caught exception (maybe server is bad)", e);
}
//----------------------------------------------------------------------------
@Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) {
// This handler is the last inbound handler.
// This means msg has not been handled by any previous handler.
ctx.close();
if (msg != LastHttpContent.EMPTY_LAST_CONTENT) {
onUnknownMessage(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
ctx.close();
// To clarify where exceptions are from, imports are not used
if (e instanceof java.io.IOException || // Connection reset by peer, Broken pipe
e instanceof java.nio.channels.ClosedChannelException ||
e instanceof io.netty.handler.codec.DecoderException ||
e instanceof io.netty.handler.codec.CorruptedFrameException || // Bad WebSocket frame
e instanceof IllegalArgumentException || // Use https://... to connect to HTTP server
e instanceof javax.net.ssl.SSLException || // Use http://... to connect to HTTPS server
e instanceof io.netty.handler.ssl.NotSslRecordException) {
onBadClient(e); // Maybe client is bad
} else {
onBadServer(e); // Maybe server is bad
}
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/router/MethodlessRouter.java
================================================
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 com.redant.core.router;
/**
* Router that contains information about route matching orders, but doesn't
* contain information about HTTP request methods.
*
* <p>Routes are devided into 3 sections: "first", "last", and "other".
* Routes in "first" are matched first, then in "other", then in "last".
*/
final class MethodlessRouter<T> {
private final OrderlessRouter<T> first = new OrderlessRouter<T>();
private final OrderlessRouter<T> other = new OrderlessRouter<T>();
private final OrderlessRouter<T> last = new OrderlessRouter<T>();
//--------------------------------------------------------------------------
/**
* Returns the "first" router; routes in this router will be matched first.
*/
public OrderlessRouter<T> first() {
return first;
}
/**
* Returns the "other" router; routes in this router will be matched after
* those in the "first" router, but before those in the "last" router.
*/
public OrderlessRouter<T> other() {
return other;
}
/**
* Returns the "last" router; routes in this router will be matched last.
*/
public OrderlessRouter<T> last() {
return last;
}
/**
* Returns the number of routes in this router.
*/
public int size() {
return first.routes().size() + other.routes().size() + last.routes().size();
}
//--------------------------------------------------------------------------
/**
* Adds route to the "first" section.
*
* <p>A path pattern can only point to one target. This method does nothing if the pattern
* has already been added.
*/
public MethodlessRouter<T> addRouteFirst(String pathPattern, T target) {
first.addRoute(pathPattern, target);
return this;
}
/**
* Adds route to the "other" section.
*
* <p>A path pattern can only point to one target. This method does nothing if the pattern
* has already been added.
*/
public MethodlessRouter<T> addRoute(String pathPattern, T target) {
other.addRoute(pathPattern, target);
return this;
}
/**
* Adds route to the "last" section.
*
* <p>A path pattern can only point to one target. This method does nothing if the pattern
* has already been added.
*/
public MethodlessRouter<T> addRouteLast(String pathPattern, T target) {
last.addRoute(pathPattern, target);
return this;
}
//--------------------------------------------------------------------------
/**
* Removes the route specified by the path pattern.
*/
public void removePathPattern(String pathPattern) {
first.removePathPattern(pathPattern);
other.removePathPattern(pathPattern);
last.removePathPattern(pathPattern);
}
/**
* Removes all routes leading to the target.
*/
public void removeTarget(T target) {
first.removeTarget(target);
other.removeTarget(target);
last.removeTarget(target);
}
//--------------------------------------------------------------------------
/**
* @return {@code null} if no match
*/
public RouteResult<T> route(String uri, String decodedPath, String[] pathTokens) {
RouteResult<T> ret = first.route(uri, decodedPath, pathTokens);
if (ret != null) {
return ret;
}
ret = other.route(uri, decodedPath, pathTokens);
if (ret != null) {
return ret;
}
ret = last.route(uri, decodedPath, pathTokens);
if (ret != null) {
return ret;
}
return null;
}
/**
* Checks if there's any matching route.
*/
public boolean anyMatched(String[] requestPathTokens) {
return first.anyMatched(requestPathTokens) ||
other.anyMatched(requestPathTokens) ||
last.anyMatched(requestPathTokens);
}
/**
* Given a target and params, this method tries to do the reverse routing
* and returns the URI.
*
* <p>Placeholders in the path pattern will be filled with the params.
* The params can be a map of {@code placeholder name -> value}
* or ordered values.
*
* <p>If a param doesn't have a corresponding placeholder, it will be put
* to the query part of the result URI.
*
* @return {@code null} if there's no match
*/
public String uri(T target, Object... params) {
String ret = first.uri(target, params);
if (ret != null) {
return ret;
}
ret = other.uri(target, params);
if (ret != null) {
return ret;
}
return last.uri(target, params);
}
}
================================================
FILE: redant-core/src/main/java/com/redant/core/router/OrderlessRouter.java
================================================
/*
* Copyright 2015 The Netty Project
*
* The Netty Project 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 com.redant.core.router;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Router that doesn't contain information about HTTP request methods and route
* matching orders.
*/
final class OrderlessRouter<T> {
private static final InternalLogger log = InternalLoggerFactory.getInstance(OrderlessRouter.class);
/**
* A path pattern can only point to one target
*/
private final Map<PathPattern, T> routes = new HashMap<PathPattern, T>();
/**
* Reverse index to create reverse routes fast (a target can have multiple path patterns)
*/
private final Map<T, Set<PathPattern>> reverseRoutes = new HashMap<T, Set<PathPattern>>();
//--------------------------------------------------------------------------
/**
* Returns all routes in this router, an unmodifiable map of {@code PathPattern -> Target}.
*/
public Map<PathPattern, T> routes() {
return Collections.unmodifiableMap(routes);
}
/**
* This method does nothing if the path pattern has already been added.
* A path pattern can only point to one target.
*/
public OrderlessRouter<T> addRoute(String pathPattern, T target) {
PathPattern p = new PathPattern(pathPattern);
if (routes.containsKey(p)) {
return this;
}
routes.put(p, target);
addReverseRoute(target, p);
return this;
}
private void addReverseRoute(T target, PathPattern pathPattern) {
Set<PathPattern> patterns = reverseRoutes.get(target);
if (patterns == null) {
patterns = new HashSet<PathPattern>();
patterns.add(pathPattern);
reverseRoutes.put(target, patterns);
} else {
patterns.add(pathPattern);
}
}
//--------------------------------------------------------------------------
/**
* Removes the route specified by the path pattern.
*/
public void removePathPattern(String pathPattern) {
PathPattern p = new PathPattern(pathPattern);
T target = routes.remove(p);
if (target == null) {
return;
}
Set<PathPattern> paths = reverseRoutes.remove(target);
paths.remove(p);
}
/**
* Removes all routes leading to the target.
*/
public void removeTarget(T target) {
Set<PathPattern> patterns = reverseRoutes.remove(ObjectUtil.checkNotNull(target, "target"));
if (patterns == null) {
return;
}
// A pattern can only point to one target.
// A target can have multiple patterns.
// Remove all patterns leading to this target.
for (PathPattern pattern : patterns) {
routes.remove(pattern);
}
}
//--------------------------------------------------------------------------
/**
* @return {@code null} if no match
*/
public RouteResult<T> route(String uri, String decodedPath, String[] pathTokens) {
// Optimize: reuse requestPathTokens and pathParams in the loop
Map<String, String> pathParams = new HashMap<String, String>();
for (Map.Entry<PathPattern, T> entry : routes.entrySet()) {
PathPattern pattern = entry.getKey();
if (pattern.match(pathTokens, pathParams)) {
T target = entry.getValue();
return new RouteResult<T>(uri, decodedPath, pathParams, Collections.<String, List<String>>emptyMap(), target);
}
// Reset for the next try
pathParams.clear();
}
return null;
}
/**
* Checks if there's any matching route.
*/
public boolean anyMatched(String[] requestPathTokens) {
Map<String, String> pathParams = new HashMap<String, String>();
for (PathPattern pattern : routes.keySet()) {
if (pattern.match(requestPathTokens, pathParams)) {
return true;
}
// Reset for the next loop
pathParams.clear();
}
return false;
}
//--------------------------------------------------------------------------
/**
* Given a target and params, this method tries to do the reverse routing
* and returns the URI.
*
* <p>Placeholders in the path pattern will be filled with the params.
* The params can be a map of {@code placeholder name -> value}
* or ordered values.
*
* <p>If a param doesn't have a corresponding placeholder, it will be put
* to the query part of the result URI.
*
* @return {@code null} if there's no match
*/
@SuppressWarnings("unchecked")
public String uri(T target, Object... params) {
if (params.length == 0) {
return uri(target, Collections.emptyMap());
}
if (params.length == 1 && params[0] instanceof Map<?, ?>) {
return pathMap(target, (Map<Object, Object>) params[0]);
}
if (params.length % 2 == 1) {
throw new IllegalArgumentException("Missing value for param: " + params[params.length - 1]);
}
Map<Object, Object> map = new HashMap<Object, Object>(params.length / 2);
for (int i = 0; i < params.length; i += 2) {
String key = params[i].toString();
String value = params[i + 1].toString();
map.put(key, value);
}
return pathMap(target, map);
}
/**
* @return {@code null} if there's no match, or the params can't be UTF-8 encoded
*/
private String pathMap(T target, Map<Object, Object> params) {
Set<PathPattern> patterns = reverseRoutes.get(target);
if (patterns == null) {
return null;
}
try {
// The best one is the one with minimum number of params in the query
String bestCandidate = null;
int minQueryParams = Integer.MAX_VALUE;
boolean matched = true;
Set<String> usedKeys = new HashSet<String>();
for (PathPattern pattern : patterns) {
matched = true;
usedKeys.clear();
// "+ 16": Just in case the part befor that is 0
int i
gitextract_2l6xrivj/
├── .gitignore
├── LICENSE
├── README.md
├── md/
│ ├── interceptor.md
│ ├── overview.md
│ └── processor.md
├── pom.xml
├── redant-cluster/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── redant/
│ └── cluster/
│ ├── bootstrap/
│ │ ├── MasterServerBootstrap.java
│ │ ├── SlaveServerBootstrap.java
│ │ └── ZkBootstrap.java
│ ├── master/
│ │ ├── MasterServer.java
│ │ ├── MasterServerBackendHandler.java
│ │ └── MasterServerHandler.java
│ ├── node/
│ │ └── Node.java
│ ├── service/
│ │ ├── discover/
│ │ │ ├── ServiceDiscover.java
│ │ │ └── ZkServiceDiscover.java
│ │ └── register/
│ │ ├── ServiceRegister.java
│ │ └── ZkServiceRegister.java
│ ├── slave/
│ │ └── SlaveServer.java
│ └── zk/
│ ├── ZkClient.java
│ ├── ZkConfig.java
│ ├── ZkNode.java
│ └── ZkServer.java
├── redant-core/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── redant/
│ │ │ └── core/
│ │ │ ├── ServerBootstrap.java
│ │ │ ├── anno/
│ │ │ │ └── Order.java
│ │ │ ├── aware/
│ │ │ │ ├── Aware.java
│ │ │ │ └── BeanContextAware.java
│ │ │ ├── bean/
│ │ │ │ ├── BaseBean.java
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── Autowired.java
│ │ │ │ │ └── Bean.java
│ │ │ │ └── context/
│ │ │ │ ├── BeanContext.java
│ │ │ │ └── DefaultBeanContext.java
│ │ │ ├── common/
│ │ │ │ ├── constants/
│ │ │ │ │ └── CommonConstants.java
│ │ │ │ ├── enums/
│ │ │ │ │ ├── ContentType.java
│ │ │ │ │ └── RequestMethod.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── InvalidSessionException.java
│ │ │ │ │ ├── InvocationException.java
│ │ │ │ │ └── ValidationException.java
│ │ │ │ ├── html/
│ │ │ │ │ ├── DefaultHtmlMaker.java
│ │ │ │ │ ├── HtmlMaker.java
│ │ │ │ │ ├── HtmlMakerEnum.java
│ │ │ │ │ └── HtmlMakerFactory.java
│ │ │ │ ├── util/
│ │ │ │ │ ├── GenericsUtil.java
│ │ │ │ │ ├── HtmlContentUtil.java
│ │ │ │ │ ├── HttpRenderUtil.java
│ │ │ │ │ ├── HttpRequestUtil.java
│ │ │ │ │ ├── PropertiesUtil.java
│ │ │ │ │ ├── TagUtil.java
│ │ │ │ │ └── ThreadUtil.java
│ │ │ │ └── view/
│ │ │ │ ├── HtmlKeyHolder.java
│ │ │ │ ├── Page404.java
│ │ │ │ ├── Page500.java
│ │ │ │ ├── PageError.java
│ │ │ │ └── PageIndex.java
│ │ │ ├── context/
│ │ │ │ └── RedantContext.java
│ │ │ ├── controller/
│ │ │ │ ├── ControllerProxy.java
│ │ │ │ ├── ProxyInvocation.java
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── Controller.java
│ │ │ │ │ ├── Mapping.java
│ │ │ │ │ └── Param.java
│ │ │ │ └── context/
│ │ │ │ ├── ControllerContext.java
│ │ │ │ └── DefaultControllerContext.java
│ │ │ ├── converter/
│ │ │ │ ├── AbstractConverter.java
│ │ │ │ ├── Converter.java
│ │ │ │ ├── PrimitiveConverter.java
│ │ │ │ └── PrimitiveTypeUtil.java
│ │ │ ├── cookie/
│ │ │ │ ├── CookieManager.java
│ │ │ │ └── DefaultCookieManager.java
│ │ │ ├── executor/
│ │ │ │ ├── AbstractExecutor.java
│ │ │ │ ├── Executor.java
│ │ │ │ └── HttpResponseExecutor.java
│ │ │ ├── handler/
│ │ │ │ ├── ControllerDispatcher.java
│ │ │ │ └── ssl/
│ │ │ │ └── SslContextHelper.java
│ │ │ ├── init/
│ │ │ │ ├── InitExecutor.java
│ │ │ │ ├── InitFunc.java
│ │ │ │ └── InitOrder.java
│ │ │ ├── interceptor/
│ │ │ │ ├── Interceptor.java
│ │ │ │ ├── InterceptorBuilder.java
│ │ │ │ ├── InterceptorHandler.java
│ │ │ │ └── InterceptorProvider.java
│ │ │ ├── render/
│ │ │ │ └── RenderType.java
│ │ │ ├── router/
│ │ │ │ ├── BadClientSilencer.java
│ │ │ │ ├── MethodlessRouter.java
│ │ │ │ ├── OrderlessRouter.java
│ │ │ │ ├── PathPattern.java
│ │ │ │ ├── RouteResult.java
│ │ │ │ ├── Router.java
│ │ │ │ └── context/
│ │ │ │ ├── DefaultRouterContext.java
│ │ │ │ └── RouterContext.java
│ │ │ ├── server/
│ │ │ │ ├── NettyHttpServer.java
│ │ │ │ ├── NettyHttpServerInitializer.java
│ │ │ │ └── Server.java
│ │ │ └── session/
│ │ │ ├── HttpSession.java
│ │ │ ├── SessionConfig.java
│ │ │ ├── SessionHelper.java
│ │ │ └── SessionManager.java
│ │ └── resources/
│ │ ├── logback.xml
│ │ ├── redant.properties
│ │ └── zk.cfg
│ └── test/
│ └── java/
│ └── com/
│ └── redant/
│ └── core/
│ └── context/
│ └── RedantContextTest.java
└── redant-example/
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── redant/
│ │ └── example/
│ │ ├── bootstrap/
│ │ │ ├── cluster/
│ │ │ │ ├── MasterServerBootstrap.java
│ │ │ │ ├── SlaveServerBootstrap.java
│ │ │ │ └── ZkBootstrap.java
│ │ │ └── standalone/
│ │ │ └── ServerBootstrap.java
│ │ ├── controller/
│ │ │ ├── BaseController.java
│ │ │ ├── CookieController.java
│ │ │ └── UserController.java
│ │ ├── interceptor/
│ │ │ ├── BlockInterceptor.java
│ │ │ ├── CustomInterceptorBuilder.java
│ │ │ └── PerformanceInterceptor.java
│ │ └── service/
│ │ ├── UserBean.java
│ │ ├── UserService.java
│ │ └── UserServiceImpl.java
│ └── resources/
│ └── logback.xml
└── test/
└── java/
└── com/
└── lememo/
└── core/
└── interceptor/
└── InterceptorProviderTest.java
SYMBOL INDEX (541 symbols across 97 files)
FILE: redant-cluster/src/main/java/com/redant/cluster/bootstrap/MasterServerBootstrap.java
class MasterServerBootstrap (line 13) | public class MasterServerBootstrap {
method main (line 15) | public static void main(String[] args) {
FILE: redant-cluster/src/main/java/com/redant/cluster/bootstrap/SlaveServerBootstrap.java
class SlaveServerBootstrap (line 14) | public class SlaveServerBootstrap {
method main (line 16) | public static void main(String[] args) {
FILE: redant-cluster/src/main/java/com/redant/cluster/bootstrap/ZkBootstrap.java
class ZkBootstrap (line 14) | public class ZkBootstrap {
method main (line 19) | public static void main(String[] args) {
FILE: redant-cluster/src/main/java/com/redant/cluster/master/MasterServer.java
class MasterServer (line 25) | public final class MasterServer implements Server {
method MasterServer (line 31) | public MasterServer(String zkAddress){
method preStart (line 35) | @Override
method start (line 41) | @Override
class MasterServerInitializer (line 68) | private static class MasterServerInitializer extends ChannelInitialize...
method MasterServerInitializer (line 72) | MasterServerInitializer(String zkAddress){
method initChannel (line 76) | @Override
method addAdvanced (line 89) | private void addAdvanced(ChannelPipeline pipeline){
FILE: redant-cluster/src/main/java/com/redant/cluster/master/MasterServerBackendHandler.java
class MasterServerBackendHandler (line 9) | public class MasterServerBackendHandler extends ChannelInboundHandlerAda...
method MasterServerBackendHandler (line 13) | public MasterServerBackendHandler(Channel inboundChannel){
method channelActive (line 17) | @Override
method channelRead (line 22) | @Override
method channelInactive (line 36) | @Override
method exceptionCaught (line 41) | @Override
FILE: redant-cluster/src/main/java/com/redant/cluster/master/MasterServerHandler.java
class MasterServerHandler (line 23) | public class MasterServerHandler extends ChannelInboundHandlerAdapter {
method MasterServerHandler (line 43) | public MasterServerHandler(String zkAddress){
method channelActive (line 47) | @Override
method channelRead (line 74) | @Override
method channelInactive (line 114) | @Override
method exceptionCaught (line 121) | @Override
method closeOnFlush (line 130) | static void closeOnFlush(Channel channel) {
FILE: redant-cluster/src/main/java/com/redant/cluster/node/Node.java
class Node (line 14) | public class Node {
method Node (line 28) | public Node(int port){
method Node (line 32) | public Node(String host, int port){
method Node (line 36) | public Node(String id, String host, int port){
method getNodeWithArgs (line 42) | public static Node getNodeWithArgs(String[] args){
method parse (line 55) | public static Node parse(JSONObject object){
method getHost (line 65) | public String getHost() {
method setHost (line 69) | public void setHost(String host) {
method getPort (line 73) | public int getPort() {
method setPort (line 77) | public void setPort(int port) {
method getId (line 81) | public String getId(){
method toString (line 85) | @Override
FILE: redant-cluster/src/main/java/com/redant/cluster/service/discover/ServiceDiscover.java
type ServiceDiscover (line 10) | public interface ServiceDiscover {
method watch (line 15) | void watch();
method discover (line 21) | Node discover();
FILE: redant-cluster/src/main/java/com/redant/cluster/service/discover/ZkServiceDiscover.java
class ZkServiceDiscover (line 28) | public class ZkServiceDiscover implements ServiceDiscover {
method ZkServiceDiscover (line 42) | private ZkServiceDiscover(){
method ZkServiceDiscover (line 46) | private ZkServiceDiscover(String zkAddress){
method getInstance (line 52) | public static ServiceDiscover getInstance(String zkAddress){
method watch (line 63) | @Override
method discover (line 72) | @Override
method initNodeOnFirst (line 95) | private void initNodeOnFirst(){
method doWatch (line 114) | private void doWatch(){
class SlaveNodeWatcher (line 128) | private class SlaveNodeWatcher implements PathChildrenCacheListener {
method childEvent (line 129) | @Override
FILE: redant-cluster/src/main/java/com/redant/cluster/service/register/ServiceRegister.java
type ServiceRegister (line 10) | public interface ServiceRegister {
method register (line 16) | void register(Node node);
FILE: redant-cluster/src/main/java/com/redant/cluster/service/register/ZkServiceRegister.java
class ZkServiceRegister (line 16) | public class ZkServiceRegister implements ServiceRegister {
method ZkServiceRegister (line 24) | private ZkServiceRegister(){
method ZkServiceRegister (line 28) | private ZkServiceRegister(String zkAddress){
method getInstance (line 32) | public static ServiceRegister getInstance(String zkAddress){
method register (line 43) | @Override
FILE: redant-cluster/src/main/java/com/redant/cluster/slave/SlaveServer.java
class SlaveServer (line 25) | public final class SlaveServer implements Server {
method SlaveServer (line 32) | public SlaveServer(String zkAddress, Node node){
method preStart (line 37) | @Override
method start (line 44) | @Override
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkClient.java
class ZkClient (line 17) | public class ZkClient {
method getClient (line 44) | public static CuratorFramework getClient(String zkAddress){
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkConfig.java
class ZkConfig (line 13) | public class ZkConfig {
type ZkConstant (line 15) | private interface ZkConstant {
method ZkConfig (line 49) | public ZkConfig(int clientPort,String dataDir,String dataLogDir){
method generateZkAddress (line 58) | public String generateZkAddress(){
method toProp (line 62) | public Properties toProp(){
method getClientPort (line 80) | public int getClientPort() {
method setClientPort (line 84) | public void setClientPort(int clientPort) {
method getTickTime (line 88) | public int getTickTime() {
method setTickTime (line 92) | public void setTickTime(int tickTime) {
method getInitLimit (line 96) | public int getInitLimit() {
method setInitLimit (line 100) | public void setInitLimit(int initLimit) {
method getSyncLimit (line 104) | public int getSyncLimit() {
method setSyncLimit (line 108) | public void setSyncLimit(int syncLimit) {
method getDataDir (line 112) | public String getDataDir() {
method setDataDir (line 116) | public void setDataDir(String dataDir) {
method getDataLogDir (line 120) | public String getDataLogDir() {
method setDataLogDir (line 124) | public void setDataLogDir(String dataLogDir) {
method getMaxClientCnxns (line 128) | public int getMaxClientCnxns() {
method setMaxClientCnxns (line 132) | public void setMaxClientCnxns(int maxClientCnxns) {
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkNode.java
class ZkNode (line 7) | public class ZkNode {
FILE: redant-cluster/src/main/java/com/redant/cluster/zk/ZkServer.java
class ZkServer (line 21) | public class ZkServer {
method getZkAddressArgs (line 25) | public static String getZkAddressArgs(String[] args, ZkConfig zkConfig){
method getZkAddress (line 37) | public static String getZkAddress(ZkConfig zkConfig){
method startStandalone (line 47) | public void startStandalone(ZkConfig zkConfig) throws ConfigException,...
method startCluster (line 68) | public void startCluster(ZkConfig zkConfig) throws ConfigException, IO...
FILE: redant-core/src/main/java/com/redant/core/ServerBootstrap.java
class ServerBootstrap (line 11) | public final class ServerBootstrap {
method main (line 13) | public static void main(String[] args) {
FILE: redant-core/src/main/java/com/redant/core/aware/Aware.java
type Aware (line 7) | public interface Aware {
FILE: redant-core/src/main/java/com/redant/core/aware/BeanContextAware.java
type BeanContextAware (line 9) | public interface BeanContextAware extends Aware{
method setBeanContext (line 15) | void setBeanContext(BeanContext beanContext);
FILE: redant-core/src/main/java/com/redant/core/bean/BaseBean.java
class BaseBean (line 14) | public class BaseBean implements Serializable {
method BaseBean (line 17) | public BaseBean() {
method toString (line 20) | @Override
FILE: redant-core/src/main/java/com/redant/core/bean/context/BeanContext.java
type BeanContext (line 8) | public interface BeanContext {
method getBean (line 15) | Object getBean(String name);
method getBean (line 24) | <T> T getBean(String name,Class<T> clazz);
FILE: redant-core/src/main/java/com/redant/core/bean/context/DefaultBeanContext.java
class DefaultBeanContext (line 31) | @InitOrder(1)
class DefaultBeanContextHolder (line 46) | private static final class DefaultBeanContextHolder {
method DefaultBeanContext (line 50) | private DefaultBeanContext() {
method getInstance (line 53) | public static BeanContext getInstance() {
method init (line 57) | @Override
method getBean (line 66) | @Override
method getBean (line 74) | @Override
method inited (line 83) | private boolean inited() {
method doInit (line 98) | private void doInit() {
method initBean (line 118) | private void initBean() {
method injectAnnotation (line 156) | private void injectAnnotation() {
method processBeanContextAware (line 173) | private void processBeanContextAware() {
method propertyAnnotation (line 202) | private void propertyAnnotation(Object bean) {
method fieldAnnotation (line 247) | private void fieldAnnotation(Object bean) {
FILE: redant-core/src/main/java/com/redant/core/common/constants/CommonConstants.java
class CommonConstants (line 10) | public class CommonConstants {
FILE: redant-core/src/main/java/com/redant/core/common/enums/ContentType.java
type ContentType (line 3) | public enum ContentType {
method ContentType (line 16) | ContentType(String content){
method toString (line 20) | @Override
FILE: redant-core/src/main/java/com/redant/core/common/enums/RequestMethod.java
type RequestMethod (line 9) | public enum RequestMethod {
method RequestMethod (line 45) | RequestMethod(HttpMethod httpMethod) {
method getHttpMethod (line 49) | public static HttpMethod getHttpMethod(RequestMethod requestMethod){
FILE: redant-core/src/main/java/com/redant/core/common/exception/InvalidSessionException.java
class InvalidSessionException (line 8) | public class InvalidSessionException extends RuntimeException{
method InvalidSessionException (line 11) | public InvalidSessionException(String s) {
FILE: redant-core/src/main/java/com/redant/core/common/exception/InvocationException.java
class InvocationException (line 3) | public class InvocationException extends Exception{
method InvocationException (line 6) | public InvocationException(String message, Throwable cause) {
FILE: redant-core/src/main/java/com/redant/core/common/exception/ValidationException.java
class ValidationException (line 8) | public class ValidationException extends RuntimeException{
method ValidationException (line 11) | public ValidationException(String s) {
method ValidationException (line 15) | public ValidationException(String message, Throwable cause) {
FILE: redant-core/src/main/java/com/redant/core/common/html/DefaultHtmlMaker.java
class DefaultHtmlMaker (line 13) | public class DefaultHtmlMaker implements HtmlMaker {
method make (line 15) | @Override
FILE: redant-core/src/main/java/com/redant/core/common/html/HtmlMaker.java
type HtmlMaker (line 10) | public interface HtmlMaker {
method make (line 18) | String make(String htmlTemplate,Map<String, Object> contentMap);
FILE: redant-core/src/main/java/com/redant/core/common/html/HtmlMakerEnum.java
type HtmlMakerEnum (line 7) | public enum HtmlMakerEnum {
FILE: redant-core/src/main/java/com/redant/core/common/html/HtmlMakerFactory.java
class HtmlMakerFactory (line 16) | public class HtmlMakerFactory {
method HtmlMakerFactory (line 24) | private HtmlMakerFactory(){
method instance (line 32) | public static HtmlMakerFactory instance(){
method build (line 46) | public HtmlMaker build(HtmlMakerEnum type,Class<? extends HtmlMaker> c...
method main (line 68) | public static void main(String[] args) {
FILE: redant-core/src/main/java/com/redant/core/common/util/GenericsUtil.java
class GenericsUtil (line 19) | public class GenericsUtil {
method getMethodGenericParameterTypes (line 28) | @SuppressWarnings("rawtypes")
method checkNull (line 54) | public static void checkNull(String dataName, Object... values){
method checkBlank (line 70) | public static void checkBlank(String dataName, Object... values){
method getLocalIpV4 (line 85) | public static String getLocalIpV4(){
FILE: redant-core/src/main/java/com/redant/core/common/util/HtmlContentUtil.java
class HtmlContentUtil (line 14) | public class HtmlContentUtil {
method HtmlContentUtil (line 18) | private HtmlContentUtil(){
method getPageContent (line 29) | public static String getPageContent(HtmlMaker htmlMaker, String htmlTe...
FILE: redant-core/src/main/java/com/redant/core/common/util/HttpRenderUtil.java
class HttpRenderUtil (line 21) | public class HttpRenderUtil {
method HttpRenderUtil (line 25) | private HttpRenderUtil() {
method render (line 36) | public static FullHttpResponse render(Object content, RenderType rende...
method getNotFoundResponse (line 51) | public static FullHttpResponse getNotFoundResponse() {
method getServerErrorResponse (line 63) | public static FullHttpResponse getServerErrorResponse() {
method getErrorResponse (line 76) | public static FullHttpResponse getErrorResponse(String errorMessage) {
method getBlockedResponse (line 88) | public static FullHttpResponse getBlockedResponse() {
method getBytes (line 101) | private static byte[] getBytes(Object content) {
FILE: redant-core/src/main/java/com/redant/core/common/util/HttpRequestUtil.java
class HttpRequestUtil (line 21) | public class HttpRequestUtil {
method getParameterMap (line 28) | public static Map<String, List<String>> getParameterMap(HttpRequest re...
method getPostParamMap (line 50) | @SuppressWarnings("unchecked")
method getContentType (line 117) | private static String getContentType(HttpHeaders headers){
FILE: redant-core/src/main/java/com/redant/core/common/util/PropertiesUtil.java
class PropertiesUtil (line 18) | public class PropertiesUtil {
method PropertiesUtil (line 28) | private PropertiesUtil(){
method propertiesLoaded (line 40) | private boolean propertiesLoaded(){
method getPropertiesByResource (line 59) | public static Properties getPropertiesByResource(String propertiesPath){
method getInstance (line 85) | public static synchronized PropertiesUtil getInstance(String propertie...
method getString (line 120) | public String getString(String key){
method getBoolean (line 131) | public boolean getBoolean(String key){
method getInt (line 139) | public int getInt(String key,int defaultValue){
method getLong (line 153) | public long getLong(String key,long defaultValue){
method main (line 165) | public static void main(String[] args) {
FILE: redant-core/src/main/java/com/redant/core/common/util/TagUtil.java
class TagUtil (line 15) | public class TagUtil {
class GHandle (line 19) | private static class GHandle{
method addTag (line 27) | public static void addTag(String tag){
method showCost (line 39) | public static void showCost(String startTag,String endTag){
FILE: redant-core/src/main/java/com/redant/core/common/util/ThreadUtil.java
class ThreadUtil (line 8) | public class ThreadUtil {
method currentThreadName (line 14) | public static String currentThreadName(){
FILE: redant-core/src/main/java/com/redant/core/common/view/HtmlKeyHolder.java
type HtmlKeyHolder (line 7) | public interface HtmlKeyHolder {
FILE: redant-core/src/main/java/com/redant/core/common/view/Page404.java
class Page404 (line 10) | public final class Page404 {
method Page404 (line 12) | private Page404(){
FILE: redant-core/src/main/java/com/redant/core/common/view/Page500.java
class Page500 (line 9) | public final class Page500 {
method Page500 (line 11) | private Page500(){
FILE: redant-core/src/main/java/com/redant/core/common/view/PageError.java
class PageError (line 9) | public final class PageError {
method PageError (line 11) | private PageError(){
FILE: redant-core/src/main/java/com/redant/core/common/view/PageIndex.java
class PageIndex (line 9) | public final class PageIndex {
method PageIndex (line 11) | private PageIndex(){
FILE: redant-core/src/main/java/com/redant/core/context/RedantContext.java
class RedantContext (line 18) | public class RedantContext {
method RedantContext (line 35) | private RedantContext(){
method setRequest (line 39) | public RedantContext setRequest(HttpRequest request){
method setContext (line 44) | public RedantContext setContext(ChannelHandlerContext context){
method setResponse (line 49) | public RedantContext setResponse(HttpResponse response){
method addCookie (line 54) | public RedantContext addCookie(Cookie cookie){
method addCookies (line 64) | public RedantContext addCookies(Set<Cookie> cookieSet){
method getRequest (line 74) | public HttpRequest getRequest() {
method getContext (line 78) | public ChannelHandlerContext getContext() {
method getResponse (line 82) | public HttpResponse getResponse() {
method getCookies (line 86) | public Set<Cookie> getCookies() {
method currentContext (line 90) | public static RedantContext currentContext(){
method clear (line 99) | public static void clear(){
FILE: redant-core/src/main/java/com/redant/core/controller/ControllerProxy.java
class ControllerProxy (line 13) | public class ControllerProxy {
method getRenderType (line 25) | public RenderType getRenderType() {
method setRenderType (line 29) | public void setRenderType(RenderType renderType) {
method getRequestMethod (line 33) | public RequestMethod getRequestMethod() {
method setRequestMethod (line 37) | public void setRequestMethod(RequestMethod requestMethod) {
method getController (line 41) | public Object getController() {
method setController (line 45) | public void setController(Object controller) {
method getMethod (line 49) | public Method getMethod() {
method setMethod (line 53) | public void setMethod(Method method) {
method getMethodName (line 57) | public String getMethodName() {
method setMethodName (line 61) | public void setMethodName(String methodName) {
method toString (line 65) | @Override
FILE: redant-core/src/main/java/com/redant/core/controller/ProxyInvocation.java
class ProxyInvocation (line 29) | public class ProxyInvocation {
method invoke (line 33) | public static Object invoke(ControllerProxy proxy) throws Exception{
class Invocation (line 41) | private static class Invocation {
method Invocation (line 47) | public Invocation(){
method getParameters (line 58) | private Object[] getParameters(Method method,Class<?>[] parameterTyp...
method parseParameter (line 109) | @SuppressWarnings({ "rawtypes", "unchecked" })
method getInvokeException (line 178) | private InvocationException getInvokeException(String msg, Throwable...
method invoke (line 194) | Object invoke(Object controller, Method method, String methodName) t...
FILE: redant-core/src/main/java/com/redant/core/controller/context/ControllerContext.java
type ControllerContext (line 10) | public interface ControllerContext {
method addProxy (line 17) | void addProxy(String path,ControllerProxy proxy);
method getProxy (line 25) | ControllerProxy getProxy(HttpMethod method, String uri);
FILE: redant-core/src/main/java/com/redant/core/controller/context/DefaultControllerContext.java
class DefaultControllerContext (line 19) | public class DefaultControllerContext implements ControllerContext {
class DefaultControllerContextHolder (line 33) | private static final class DefaultControllerContextHolder {
method DefaultControllerContext (line 37) | private DefaultControllerContext() {
method getInstance (line 42) | public static ControllerContext getInstance() {
method addProxy (line 47) | @Override
method getProxy (line 52) | @Override
FILE: redant-core/src/main/java/com/redant/core/converter/AbstractConverter.java
class AbstractConverter (line 13) | public abstract class AbstractConverter implements Converter {
method convert (line 20) | @Override
method doConvertValue (line 72) | protected abstract Object doConvertValue(Object source, Class<?> toTyp...
FILE: redant-core/src/main/java/com/redant/core/converter/Converter.java
type Converter (line 8) | public interface Converter {
method convert (line 17) | Object convert(Object source, Class<?> toType, Object... params);
FILE: redant-core/src/main/java/com/redant/core/converter/PrimitiveConverter.java
class PrimitiveConverter (line 15) | public final class PrimitiveConverter extends AbstractConverter {
method doConvertValue (line 28) | @Override
method isNumberString (line 54) | private boolean isNumberString(String stringValue) {
method booleanValue (line 70) | private boolean booleanValue(Object source) {
method longValue (line 97) | private long longValue(Object source) throws NumberFormatException {
method doubleValue (line 116) | private double doubleValue(Object source) throws NumberFormatException {
method bigIntValue (line 134) | private BigInteger bigIntValue(Object source) throws NumberFormatExcep...
method bigDecValue (line 159) | private BigDecimal bigDecValue(Object source) throws NumberFormatExcep...
method stringValue (line 184) | private String stringValue(Object source, boolean trim) {
method charValue (line 198) | private char charValue(Object source) {
method stringValue (line 210) | private String stringValue(Object source) {
method doConvert (line 214) | private Object doConvert(Object source, Class<?> toType) {
method getInstance (line 249) | public static PrimitiveConverter getInstance(){
FILE: redant-core/src/main/java/com/redant/core/converter/PrimitiveTypeUtil.java
class PrimitiveTypeUtil (line 36) | public class PrimitiveTypeUtil {
method PrimitiveTypeUtil (line 41) | private PrimitiveTypeUtil() {}
method isPriType (line 109) | public static boolean isPriType(Class<?> cls) {
method isPriArrayType (line 123) | public static boolean isPriArrayType(Class<?> cls) {
method getPriDefaultValue (line 137) | public static Object getPriDefaultValue(Class<?> type) {
FILE: redant-core/src/main/java/com/redant/core/cookie/CookieManager.java
type CookieManager (line 13) | public interface CookieManager {
method getCookies (line 19) | Set<Cookie> getCookies();
method getCookieMap (line 25) | Map<String,Cookie> getCookieMap();
method getCookie (line 32) | Cookie getCookie(String name);
method getCookieValue (line 39) | String getCookieValue(String name);
method setCookie (line 45) | void setCookie(Cookie cookie);
method setCookies (line 50) | void setCookies();
method addCookie (line 57) | void addCookie(String name,String value);
method addCookie (line 65) | void addCookie(String name,String value,String domain);
method addCookie (line 73) | void addCookie(String name,String value,long maxAge);
method addCookie (line 82) | void addCookie(String name,String value,String domain,long maxAge);
method deleteCookie (line 89) | boolean deleteCookie(String name);
FILE: redant-core/src/main/java/com/redant/core/cookie/DefaultCookieManager.java
class DefaultCookieManager (line 23) | public class DefaultCookieManager implements CookieManager {
class DefaultCookieManagerHolder (line 25) | private static final class DefaultCookieManagerHolder {
method DefaultCookieManager (line 29) | private DefaultCookieManager() {
method getInstance (line 33) | public static CookieManager getInstance() {
method getCookies (line 38) | @Override
method getCookieMap (line 51) | @Override
method getCookie (line 63) | @Override
method getCookieValue (line 69) | @Override
method setCookie (line 75) | @Override
method setCookies (line 80) | @Override
method addCookie (line 90) | @Override
method addCookie (line 95) | @Override
method addCookie (line 100) | @Override
method addCookie (line 105) | @Override
method deleteCookie (line 120) | @Override
FILE: redant-core/src/main/java/com/redant/core/executor/AbstractExecutor.java
class AbstractExecutor (line 16) | public abstract class AbstractExecutor<T> implements Executor<T> {
method AbstractExecutor (line 22) | public AbstractExecutor() {
method AbstractExecutor (line 26) | public AbstractExecutor(java.util.concurrent.Executor eventExecutor) {
method execute (line 30) | @Override
method asyncExecute (line 35) | @Override
method doExecute (line 62) | public abstract T doExecute(Object... request);
class EventExecutorHolder (line 64) | private static final class EventExecutorHolder {
FILE: redant-core/src/main/java/com/redant/core/executor/Executor.java
type Executor (line 9) | public interface Executor<T> {
method execute (line 16) | T execute(Object... request);
method asyncExecute (line 24) | Future<T> asyncExecute(Promise<T> promise, Object... request);
FILE: redant-core/src/main/java/com/redant/core/executor/HttpResponseExecutor.java
class HttpResponseExecutor (line 29) | public class HttpResponseExecutor extends AbstractExecutor<HttpResponse> {
method getInstance (line 35) | public static HttpResponseExecutor getInstance() {
method HttpResponseExecutor (line 39) | private HttpResponseExecutor() {
method doExecute (line 42) | @Override
method invoke (line 78) | private HttpResponse invoke(HttpRequest request) throws Exception {
method getErrorResponse (line 89) | private HttpResponse getErrorResponse(Exception e) {
method buildHeaders (line 99) | private void buildHeaders(HttpResponse response, RedantContext redantC...
class HttpResponseExecutorHolder (line 114) | private static final class HttpResponseExecutorHolder {
FILE: redant-core/src/main/java/com/redant/core/handler/ControllerDispatcher.java
class ControllerDispatcher (line 20) | public class ControllerDispatcher extends SimpleChannelInboundHandler<Ht...
method channelRead0 (line 26) | @Override
method exceptionCaught (line 51) | @Override
FILE: redant-core/src/main/java/com/redant/core/handler/ssl/SslContextHelper.java
class SslContextHelper (line 25) | public class SslContextHelper {
method getKey (line 33) | private static String getKey(String keyPath,String keyPassword){
method getSslContext (line 47) | public static SslContext getSslContext(String keyPath,String keyPasswo...
FILE: redant-core/src/main/java/com/redant/core/init/InitExecutor.java
class InitExecutor (line 20) | public final class InitExecutor {
method init (line 26) | public static void init() {
method insertSorted (line 59) | private static void insertSorted(List<OrderWrapper> list, InitFunc fun...
method resolveOrder (line 71) | private static int resolveOrder(InitFunc func) {
method InitExecutor (line 79) | private InitExecutor() {}
class OrderWrapper (line 81) | private static class OrderWrapper {
method OrderWrapper (line 85) | OrderWrapper(int order, InitFunc func) {
method getOrder (line 90) | int getOrder() {
method getFunc (line 94) | InitFunc getFunc() {
FILE: redant-core/src/main/java/com/redant/core/init/InitFunc.java
type InitFunc (line 8) | public interface InitFunc {
method init (line 13) | void init();
FILE: redant-core/src/main/java/com/redant/core/interceptor/Interceptor.java
class Interceptor (line 9) | public abstract class Interceptor {
method preHandle (line 14) | public boolean preHandle(Map<String, List<String>> paramMap){
method postHandle (line 21) | public abstract void postHandle(Map<String, List<String>> paramMap);
FILE: redant-core/src/main/java/com/redant/core/interceptor/InterceptorBuilder.java
type InterceptorBuilder (line 9) | public interface InterceptorBuilder {
method build (line 15) | List<Interceptor> build();
FILE: redant-core/src/main/java/com/redant/core/interceptor/InterceptorHandler.java
class InterceptorHandler (line 11) | public class InterceptorHandler {
method preHandle (line 13) | public static boolean preHandle(Map<String, List<String>> paramMap){
method postHandle (line 26) | public static void postHandle(Map<String, List<String>> paramMap){
FILE: redant-core/src/main/java/com/redant/core/interceptor/InterceptorProvider.java
class InterceptorProvider (line 17) | public class InterceptorProvider {
method getInterceptors (line 23) | public static List<Interceptor> getInterceptors(){
class InterceptorsHolder (line 50) | static class InterceptorsHolder {
method scanInterceptors (line 58) | private static List<Interceptor> scanInterceptors() {
method insertSorted (line 77) | private static void insertSorted(List<InterceptorWrapper> list, Inte...
method resolveOrder (line 89) | private static int resolveOrder(Interceptor interceptor) {
class InterceptorWrapper (line 97) | private static class InterceptorWrapper {
method InterceptorWrapper (line 101) | InterceptorWrapper(int order, Interceptor interceptor) {
method getOrder (line 106) | int getOrder() {
method getInterceptor (line 110) | Interceptor getInterceptor() {
FILE: redant-core/src/main/java/com/redant/core/render/RenderType.java
type RenderType (line 8) | public enum RenderType {
method RenderType (line 29) | RenderType(String contentType){
method getContentType (line 33) | public String getContentType() {
FILE: redant-core/src/main/java/com/redant/core/router/BadClientSilencer.java
class BadClientSilencer (line 34) | @Sharable
method onUnknownMessage (line 39) | protected void onUnknownMessage(Object msg) {
method onBadClient (line 44) | protected void onBadClient(Throwable e) {
method onBadServer (line 49) | protected void onBadServer(Throwable e) {
method channelRead0 (line 55) | @Override
method exceptionCaught (line 66) | @Override
FILE: redant-core/src/main/java/com/redant/core/router/MethodlessRouter.java
class MethodlessRouter (line 25) | final class MethodlessRouter<T> {
method first (line 35) | public OrderlessRouter<T> first() {
method other (line 43) | public OrderlessRouter<T> other() {
method last (line 50) | public OrderlessRouter<T> last() {
method size (line 57) | public int size() {
method addRouteFirst (line 69) | public MethodlessRouter<T> addRouteFirst(String pathPattern, T target) {
method addRoute (line 80) | public MethodlessRouter<T> addRoute(String pathPattern, T target) {
method addRouteLast (line 91) | public MethodlessRouter<T> addRouteLast(String pathPattern, T target) {
method removePathPattern (line 101) | public void removePathPattern(String pathPattern) {
method removeTarget (line 110) | public void removeTarget(T target) {
method route (line 121) | public RouteResult<T> route(String uri, String decodedPath, String[] p...
method anyMatched (line 143) | public boolean anyMatched(String[] requestPathTokens) {
method uri (line 162) | public String uri(T target, Object... params) {
FILE: redant-core/src/main/java/com/redant/core/router/OrderlessRouter.java
class OrderlessRouter (line 35) | final class OrderlessRouter<T> {
method routes (line 53) | public Map<PathPattern, T> routes() {
method addRoute (line 61) | public OrderlessRouter<T> addRoute(String pathPattern, T target) {
method addReverseRoute (line 72) | private void addReverseRoute(T target, PathPattern pathPattern) {
method removePathPattern (line 88) | public void removePathPattern(String pathPattern) {
method removeTarget (line 102) | public void removeTarget(T target) {
method route (line 121) | public RouteResult<T> route(String uri, String decodedPath, String[] p...
method anyMatched (line 141) | public boolean anyMatched(String[] requestPathTokens) {
method uri (line 170) | @SuppressWarnings("unchecked")
method pathMap (line 196) | private String pathMap(T target, Map<Object, Object> params) {
FILE: redant-core/src/main/java/com/redant/core/router/PathPattern.java
class PathPattern (line 35) | final class PathPattern {
method removeSlashesAtBothEnds (line 36) | public static String removeSlashesAtBothEnds(String path) {
method PathPattern (line 70) | public PathPattern(String pattern) {
method pattern (line 82) | public String pattern() {
method tokens (line 90) | public String[] tokens() {
method hashCode (line 97) | @Override
method equals (line 102) | @Override
method match (line 127) | public boolean match(String[] requestPathTokens, Map<String, String> p...
FILE: redant-core/src/main/java/com/redant/core/router/RouteResult.java
class RouteResult (line 29) | public class RouteResult<T> {
method RouteResult (line 41) | public RouteResult(
method uri (line 56) | public String uri() {
method decodedPath (line 63) | public String decodedPath() {
method pathParams (line 70) | public Map<String, String> pathParams() {
method queryParams (line 77) | public Map<String, List<String>> queryParams() {
method target (line 81) | public T target() {
method queryParam (line 93) | public String queryParam(String name) {
method param (line 104) | public String param(String name) {
method params (line 114) | public List<String> params(String name) {
method toString (line 132) | @Override
FILE: redant-core/src/main/java/com/redant/core/router/Router.java
class Router (line 141) | public class Router<T> {
method notFound (line 160) | public T notFound() {
method size (line 167) | public int size() {
method addRouteFirst (line 185) | public Router<T> addRouteFirst(HttpMethod method, String pathPattern, ...
method addRoute (line 196) | public Router<T> addRoute(HttpMethod method, String pathPattern, T tar...
method addRouteLast (line 207) | public Router<T> addRouteLast(HttpMethod method, String pathPattern, T...
method notFound (line 216) | public Router<T> notFound(T target) {
method getMethodlessRouter (line 221) | private MethodlessRouter<T> getMethodlessRouter(HttpMethod method) {
method removePathPattern (line 240) | public void removePathPattern(String pathPattern) {
method removeTarget (line 250) | public void removeTarget(T target) {
method route (line 263) | public RouteResult<T> route(HttpMethod method, String uri) {
method decodePathTokens (line 291) | private String[] decodePathTokens(String uri) {
method allowedMethods (line 317) | public Set<HttpMethod> allowedMethods(String uri) {
method allAllowedMethods (line 340) | public Set<HttpMethod> allAllowedMethods() {
method uri (line 374) | public String uri(HttpMethod method, T target, Object... params) {
method uri (line 404) | public String uri(T target, Object... params) {
method toString (line 420) | @Override
method aggregateRoutes (line 467) | private static <T> void aggregateRoutes(
method maxLength (line 480) | private static int maxLength(List<String> coll) {
method targetToString (line 498) | private static String targetToString(Object target) {
method CONNECT (line 508) | public Router<T> CONNECT(String path, T target) {
method DELETE (line 512) | public Router<T> DELETE(String path, T target) {
method GET (line 516) | public Router<T> GET(String path, T target) {
method HEAD (line 520) | public Router<T> HEAD(String path, T target) {
method OPTIONS (line 524) | public Router<T> OPTIONS(String path, T target) {
method PATCH (line 528) | public Router<T> PATCH(String path, T target) {
method POST (line 532) | public Router<T> POST(String path, T target) {
method PUT (line 536) | public Router<T> PUT(String path, T target) {
method TRACE (line 540) | public Router<T> TRACE(String path, T target) {
method ANY (line 544) | public Router<T> ANY(String path, T target) {
method CONNECT_FIRST (line 550) | public Router<T> CONNECT_FIRST(String path, T target) {
method DELETE_FIRST (line 554) | public Router<T> DELETE_FIRST(String path, T target) {
method GET_FIRST (line 558) | public Router<T> GET_FIRST(String path, T target) {
method HEAD_FIRST (line 562) | public Router<T> HEAD_FIRST(String path, T target) {
method OPTIONS_FIRST (line 566) | public Router<T> OPTIONS_FIRST(String path, T target) {
method PATCH_FIRST (line 570) | public Router<T> PATCH_FIRST(String path, T target) {
method POST_FIRST (line 574) | public Router<T> POST_FIRST(String path, T target) {
method PUT_FIRST (line 578) | public Router<T> PUT_FIRST(String path, T target) {
method TRACE_FIRST (line 582) | public Router<T> TRACE_FIRST(String path, T target) {
method ANY_FIRST (line 586) | public Router<T> ANY_FIRST(String path, T target) {
method CONNECT_LAST (line 592) | public Router<T> CONNECT_LAST(String path, T target) {
method DELETE_LAST (line 596) | public Router<T> DELETE_LAST(String path, T target) {
method GET_LAST (line 600) | public Router<T> GET_LAST(String path, T target) {
method HEAD_LAST (line 604) | public Router<T> HEAD_LAST(String path, T target) {
method OPTIONS_LAST (line 608) | public Router<T> OPTIONS_LAST(String path, T target) {
method PATCH_LAST (line 612) | public Router<T> PATCH_LAST(String path, T target) {
method POST_LAST (line 616) | public Router<T> POST_LAST(String path, T target) {
method PUT_LAST (line 620) | public Router<T> PUT_LAST(String path, T target) {
method TRACE_LAST (line 624) | public Router<T> TRACE_LAST(String path, T target) {
method ANY_LAST (line 628) | public Router<T> ANY_LAST(String path, T target) {
FILE: redant-core/src/main/java/com/redant/core/router/context/DefaultRouterContext.java
class DefaultRouterContext (line 34) | @InitOrder(2)
class DefaultRouterContextHolder (line 59) | private static final class DefaultRouterContextHolder {
method DefaultRouterContext (line 63) | private DefaultRouterContext() {
method getInstance (line 67) | public static RouterContext getInstance() {
method init (line 71) | @Override
method getRouteResult (line 79) | @Override
method inited (line 92) | private boolean inited() {
method doInit (line 107) | private void doInit() {
method initRouter (line 122) | private void initRouter() {
method addRoute (line 158) | private void addRoute(Controller controller, Mapping mapping) {
method addProxy (line 166) | private void addProxy(Class<?> cls, Method method, Controller controll...
FILE: redant-core/src/main/java/com/redant/core/router/context/RouterContext.java
type RouterContext (line 12) | public interface RouterContext {
method getRouteResult (line 20) | RouteResult<RenderType> getRouteResult(HttpMethod method, String uri);
FILE: redant-core/src/main/java/com/redant/core/server/NettyHttpServer.java
class NettyHttpServer (line 21) | public final class NettyHttpServer implements Server {
method preStart (line 25) | @Override
method start (line 30) | @Override
FILE: redant-core/src/main/java/com/redant/core/server/NettyHttpServerInitializer.java
class NettyHttpServerInitializer (line 24) | public class NettyHttpServerInitializer extends ChannelInitializer<Socke...
method initChannel (line 28) | @Override
method initSsl (line 41) | private void initSsl(SocketChannel ch){
method addAdvanced (line 58) | private void addAdvanced(ChannelPipeline pipeline){
FILE: redant-core/src/main/java/com/redant/core/server/Server.java
type Server (line 7) | public interface Server {
method preStart (line 12) | void preStart();
method start (line 17) | void start();
FILE: redant-core/src/main/java/com/redant/core/session/HttpSession.java
class HttpSession (line 14) | public class HttpSession {
method assertSessionMapNotNull (line 43) | private void assertSessionMapNotNull(){
method HttpSession (line 50) | private HttpSession(){
method HttpSession (line 58) | public HttpSession(ChannelHandlerContext context){
method HttpSession (line 62) | public HttpSession(ChannelId id,ChannelHandlerContext context){
method HttpSession (line 66) | public HttpSession(ChannelId id,ChannelHandlerContext context,Long cre...
method HttpSession (line 70) | public HttpSession(ChannelId id,ChannelHandlerContext context,Long cre...
method getId (line 78) | public ChannelId getId() {
method setId (line 82) | public void setId(ChannelId id) {
method getContext (line 86) | public ChannelHandlerContext getContext() {
method setContext (line 90) | public void setContext(ChannelHandlerContext context) {
method getCreateTime (line 94) | public Long getCreateTime() {
method setCreateTime (line 98) | public void setCreateTime(Long createTime) {
method getExpireTime (line 102) | public Long getExpireTime() {
method setExpireTime (line 106) | public void setExpireTime(Long expireTime) {
method isExpire (line 114) | public boolean isExpire(){
method setAttribute (line 123) | public void setAttribute(String key,Object val){
method getAttribute (line 131) | public Object getAttribute(String key){
method containsAttribute (line 139) | public boolean containsAttribute(String key){
FILE: redant-core/src/main/java/com/redant/core/session/SessionConfig.java
class SessionConfig (line 8) | public class SessionConfig {
method SessionConfig (line 15) | private SessionConfig(){
method instance (line 43) | public static SessionConfig instance(){
method sessionTimeOut (line 47) | public SessionConfig sessionTimeOut(Long sessionTimeOut){
method sessionTimeOut (line 52) | public Long sessionTimeOut(){
method toString (line 57) | @Override
FILE: redant-core/src/main/java/com/redant/core/session/SessionHelper.java
class SessionHelper (line 16) | public class SessionHelper {
method SessionHelper (line 25) | private SessionHelper(){
method instange (line 37) | public static SessionHelper instange(){
method containsSession (line 55) | public boolean containsSession(ChannelHandlerContext context){
method addSession (line 64) | public void addSession(ChannelHandlerContext context,HttpSession sessi...
method getSession (line 76) | public HttpSession getSession(ChannelHandlerContext context){
method getSession (line 89) | public HttpSession getSession(ChannelHandlerContext context,boolean cr...
method clearExpireSession (line 102) | public void clearExpireSession(){
FILE: redant-core/src/main/java/com/redant/core/session/SessionManager.java
type SessionManager (line 8) | public interface SessionManager {
method sessionExists (line 14) | boolean sessionExists();
method addSession (line 20) | void addSession(HttpSession session);
method getSession (line 26) | HttpSession getSession();
method getSession (line 33) | HttpSession getSession(boolean createIfNull);
method clearExpireSession (line 39) | void clearExpireSession();
FILE: redant-core/src/test/java/com/redant/core/context/RedantContextTest.java
class RedantContextTest (line 6) | public class RedantContextTest {
method main (line 8) | public static void main(String[] args) {
class ContextRunner (line 14) | private static class ContextRunner implements Runnable {
method run (line 15) | @Override
FILE: redant-example/src/main/java/com/redant/example/bootstrap/cluster/MasterServerBootstrap.java
class MasterServerBootstrap (line 8) | public class MasterServerBootstrap {
method main (line 10) | public static void main(String[] args) {
FILE: redant-example/src/main/java/com/redant/example/bootstrap/cluster/SlaveServerBootstrap.java
class SlaveServerBootstrap (line 8) | public class SlaveServerBootstrap {
method main (line 10) | public static void main(String[] args) {
FILE: redant-example/src/main/java/com/redant/example/bootstrap/cluster/ZkBootstrap.java
class ZkBootstrap (line 8) | public class ZkBootstrap {
method main (line 10) | public static void main(String[] args) {
FILE: redant-example/src/main/java/com/redant/example/bootstrap/standalone/ServerBootstrap.java
class ServerBootstrap (line 8) | public final class ServerBootstrap {
method main (line 10) | public static void main(String[] args) {
FILE: redant-example/src/main/java/com/redant/example/controller/BaseController.java
class BaseController (line 21) | @Controller(path="/")
method index (line 24) | @Mapping(requestMethod=RequestMethod.GET,renderType=RenderType.HTML)
FILE: redant-example/src/main/java/com/redant/example/controller/CookieController.java
class CookieController (line 18) | @Bean
method add (line 24) | @Mapping(path="/add",requestMethod=RequestMethod.GET,renderType=Render...
method delete (line 34) | @Mapping(path="/delete",requestMethod=RequestMethod.GET,renderType=Ren...
FILE: redant-example/src/main/java/com/redant/example/controller/UserController.java
class UserController (line 22) | @Bean
method info (line 32) | @Mapping(path = "/info", requestMethod = RequestMethod.GET, renderType...
method list (line 42) | @Mapping(path = "/list", requestMethod = RequestMethod.GET, renderType...
method count (line 54) | @Mapping(path = "/count", requestMethod = RequestMethod.GET, renderTyp...
FILE: redant-example/src/main/java/com/redant/example/interceptor/BlockInterceptor.java
class BlockInterceptor (line 21) | @Order(value = 1)
method preHandle (line 26) | @Override
method postHandle (line 48) | @Override
FILE: redant-example/src/main/java/com/redant/example/interceptor/CustomInterceptorBuilder.java
class CustomInterceptorBuilder (line 12) | public class CustomInterceptorBuilder implements InterceptorBuilder {
method build (line 18) | @Override
method activeBlock (line 37) | private boolean activeBlock(){
method activePerf (line 41) | private boolean activePerf(){
FILE: redant-example/src/main/java/com/redant/example/interceptor/PerformanceInterceptor.java
class PerformanceInterceptor (line 17) | @Order(value = 2)
method preHandle (line 24) | @Override
method postHandle (line 30) | @Override
FILE: redant-example/src/main/java/com/redant/example/service/UserBean.java
class UserBean (line 13) | public class UserBean implements Serializable {
method getId (line 21) | public Integer getId() {
method setId (line 25) | public void setId(Integer id) {
method getUserName (line 29) | public String getUserName() {
method setUserName (line 33) | public void setUserName(String userName) {
method getPassword (line 37) | public String getPassword() {
method setPassword (line 41) | public void setPassword(String password) {
method toString (line 45) | @Override
FILE: redant-example/src/main/java/com/redant/example/service/UserService.java
type UserService (line 7) | public interface UserService {
method selectUserInfo (line 14) | UserBean selectUserInfo(Integer id);
method selectCount (line 20) | int selectCount();
FILE: redant-example/src/main/java/com/redant/example/service/UserServiceImpl.java
class UserServiceImpl (line 9) | @Bean(name="userService")
method selectUserInfo (line 12) | @Override
method selectCount (line 20) | @Override
FILE: redant-example/src/test/java/com/lememo/core/interceptor/InterceptorProviderTest.java
class InterceptorProviderTest (line 11) | public class InterceptorProviderTest {
method main (line 13) | public static void main(String[] args) {
class Run (line 19) | static class Run implements Runnable {
method run (line 20) | @Override
Condensed preview — 118 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (306K chars).
[
{
"path": ".gitignore",
"chars": 878,
"preview": "# Compiled source #\n###################\n*.com\n*.class\n*.dll\n*.exe\n*.o\n*.so\n\n# Packages #\n############\n# it's better to u"
},
{
"path": "LICENSE",
"chars": 10252,
"preview": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AN"
},
{
"path": "README.md",
"chars": 4215,
"preview": "# RedAnt 项目\n\n**RedAnt** 是一个基于 Netty 的轻量级 Web 容器\n\n **特性:**\n\n- [x] **IOC容器** : 通过 @Bean 注解可以管理所有对象,通过 @Autowired 注解进行对象注入\n"
},
{
"path": "md/interceptor.md",
"chars": 3425,
"preview": "# 用责任链模式设计拦截器\n\n我在 Redant(https://github.com/all4you/redant) 中通过继承 ChannelHandler 实现了拦截器的功能,并且 pipeline 就是一种责任链模式的应用。但是我后"
},
{
"path": "md/overview.md",
"chars": 8841,
"preview": "**RedAnt** 是一个基于 Netty 的轻量级 Web 容器,创建这个项目的目的主要是学习使用 Netty,俗话说不要轻易的造轮子,但是通过造轮子我们可以学到很多优秀开源框架的设计思路,编写优美的代码,更好的提升自己。\n\n![fea"
},
{
"path": "md/processor.md",
"chars": 1915,
"preview": "# 责任链\n\n我在 Redant 中实现的拦截器所使用的责任链,其实是通过了一个 List 来保存了所有的 Interceptor,那我们通常所说的责任链除了使用 List 来实现外,还可以通过真正的链表结构来实现,Netty 和 Sent"
},
{
"path": "pom.xml",
"chars": 4947,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "redant-cluster/pom.xml",
"chars": 1336,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/bootstrap/MasterServerBootstrap.java",
"chars": 595,
"preview": "package com.redant.cluster.bootstrap;\n\nimport com.redant.cluster.master.MasterServer;\nimport com.redant.cluster.zk.ZkCon"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/bootstrap/SlaveServerBootstrap.java",
"chars": 676,
"preview": "package com.redant.cluster.bootstrap;\n\nimport com.redant.cluster.node.Node;\nimport com.redant.cluster.slave.SlaveServer;"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/bootstrap/ZkBootstrap.java",
"chars": 654,
"preview": "package com.redant.cluster.bootstrap;\n\nimport com.redant.cluster.zk.ZkConfig;\nimport com.redant.cluster.zk.ZkServer;\nimp"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/master/MasterServer.java",
"chars": 3579,
"preview": "package com.redant.cluster.master;\n\nimport com.redant.cluster.service.discover.ZkServiceDiscover;\nimport com.redant.core"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/master/MasterServerBackendHandler.java",
"chars": 1250,
"preview": "package com.redant.cluster.master;\n\nimport io.netty.channel.*;\n\n/**\n * @author houyi.wh\n * @date 2018/1/18\n **/\npublic c"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/master/MasterServerHandler.java",
"chars": 5281,
"preview": "package com.redant.cluster.master;\n\nimport com.redant.cluster.service.discover.ServiceDiscover;\nimport com.redant.cluste"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/node/Node.java",
"chars": 1939,
"preview": "package com.redant.cluster.node;\n\nimport cn.hutool.core.util.NumberUtil;\nimport cn.hutool.crypto.SecureUtil;\nimport com."
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/service/discover/ServiceDiscover.java",
"chars": 314,
"preview": "package com.redant.cluster.service.discover;\n\nimport com.redant.cluster.node.Node;\n\n/**\n * 服务发现-应用级别\n * @author houyi.wh"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/service/discover/ZkServiceDiscover.java",
"chars": 5564,
"preview": "package com.redant.cluster.service.discover;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/service/register/ServiceRegister.java",
"chars": 264,
"preview": "package com.redant.cluster.service.register;\n\nimport com.redant.cluster.node.Node;\n\n/**\n * 服务注册-应用级别\n * @author houyi.wh"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/service/register/ZkServiceRegister.java",
"chars": 1885,
"preview": "package com.redant.cluster.service.register;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.redant.cluster.node.Node;\ni"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/slave/SlaveServer.java",
"chars": 2575,
"preview": "package com.redant.cluster.slave;\n\nimport com.redant.cluster.node.Node;\nimport com.redant.cluster.service.register.ZkSer"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/zk/ZkClient.java",
"chars": 1914,
"preview": "package com.redant.cluster.zk;\n\nimport org.apache.curator.framework.CuratorFramework;\nimport org.apache.curator.framewor"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/zk/ZkConfig.java",
"chars": 2912,
"preview": "package com.redant.cluster.zk;\n\n\nimport com.redant.core.common.util.GenericsUtil;\n\nimport java.util.Properties;\n\n/**\n * "
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/zk/ZkNode.java",
"chars": 308,
"preview": "package com.redant.cluster.zk;\n\n/**\n * @author houyi.wh\n * @date 2017/11/21\n **/\npublic class ZkNode {\n\n /**\n * 根"
},
{
"path": "redant-cluster/src/main/java/com/redant/cluster/zk/ZkServer.java",
"chars": 2118,
"preview": "package com.redant.cluster.zk;\n\nimport cn.hutool.core.util.StrUtil;\nimport org.apache.zookeeper.server.ServerConfig;\nimp"
},
{
"path": "redant-core/pom.xml",
"chars": 1767,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "redant-core/src/main/java/com/redant/core/ServerBootstrap.java",
"chars": 412,
"preview": "package com.redant.core;\n\nimport com.redant.core.server.NettyHttpServer;\nimport com.redant.core.server.Server;\n\n/**\n * 服"
},
{
"path": "redant-core/src/main/java/com/redant/core/anno/Order.java",
"chars": 534,
"preview": "package com.redant.core.anno;\n\nimport java.lang.annotation.*;\n\n/**\n * 排序规则,升序排序\n * @author houyi.wh\n * @date 2019-01-14\n"
},
{
"path": "redant-core/src/main/java/com/redant/core/aware/Aware.java",
"chars": 107,
"preview": "package com.redant.core.aware;\n\n/**\n * @author houyi.wh\n * @date 2019-01-14\n */\npublic interface Aware {\n\n}"
},
{
"path": "redant-core/src/main/java/com/redant/core/aware/BeanContextAware.java",
"chars": 308,
"preview": "package com.redant.core.aware;\n\nimport com.redant.core.bean.context.BeanContext;\n\n/**\n * @author houyi.wh\n * @date 2019-"
},
{
"path": "redant-core/src/main/java/com/redant/core/bean/BaseBean.java",
"chars": 414,
"preview": "package com.redant.core.bean;\n\n\nimport com.alibaba.fastjson.JSON;\n\nimport java.io.Serializable;\n\n\n/**\n * BaseBean 所有bean"
},
{
"path": "redant-core/src/main/java/com/redant/core/bean/annotation/Autowired.java",
"chars": 352,
"preview": "package com.redant.core.bean.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention"
},
{
"path": "redant-core/src/main/java/com/redant/core/bean/annotation/Bean.java",
"chars": 324,
"preview": "package com.redant.core.bean.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention"
},
{
"path": "redant-core/src/main/java/com/redant/core/bean/context/BeanContext.java",
"chars": 416,
"preview": "package com.redant.core.bean.context;\n\n/**\n * Bean上下文\n * @author houyi.wh\n * @date 2017-10-20\n */\npublic interface BeanC"
},
{
"path": "redant-core/src/main/java/com/redant/core/bean/context/DefaultBeanContext.java",
"chars": 9651,
"preview": "package com.redant.core.bean.context;\n\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.Clas"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/constants/CommonConstants.java",
"chars": 3435,
"preview": "package com.redant.core.common.constants;\n\nimport com.redant.core.common.util.PropertiesUtil;\n\n/**\n * 公共常量\n * @author ho"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/enums/ContentType.java",
"chars": 614,
"preview": "package com.redant.core.common.enums;\n\npublic enum ContentType {\n\n APPLICATION_ATOM_XML(\"application/atom+xml\"),\n "
},
{
"path": "redant-core/src/main/java/com/redant/core/common/enums/RequestMethod.java",
"chars": 988,
"preview": "package com.redant.core.common.enums;\n\nimport io.netty.handler.codec.http.HttpMethod;\n\n/**\n * @author houyi.wh\n * @date "
},
{
"path": "redant-core/src/main/java/com/redant/core/common/exception/InvalidSessionException.java",
"chars": 289,
"preview": "package com.redant.core.common.exception;\n\n/**\n * 非法session\n * @author houyi.wh\n * @date 2017-10-20\n */\npublic class Inv"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/exception/InvocationException.java",
"chars": 252,
"preview": "package com.redant.core.common.exception;\n\npublic class InvocationException extends Exception{\n\tprivate static final lon"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/exception/ValidationException.java",
"chars": 393,
"preview": "package com.redant.core.common.exception;\n\n/**\n * ValidationException\n * @author houyi.wh\n * @date 2017-10-20\n */\npublic"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/html/DefaultHtmlMaker.java",
"chars": 854,
"preview": "package com.redant.core.common.html;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.redant.core.common.vie"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/html/HtmlMaker.java",
"chars": 347,
"preview": "package com.redant.core.common.html;\n\nimport java.util.Map;\n\n/**\n * html生成器\n * @author houyi.wh\n * @date 2017/12/1\n **/\n"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/html/HtmlMakerEnum.java",
"chars": 154,
"preview": "package com.redant.core.common.html;\n\n/**\n * @author houyi.wh\n * @date 2017/12/1\n **/\npublic enum HtmlMakerEnum {\n /*"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/html/HtmlMakerFactory.java",
"chars": 2321,
"preview": "package com.redant.core.common.html;\n\nimport cn.hutool.core.util.ReflectUtil;\nimport org.slf4j.Logger;\nimport org.slf4j."
},
{
"path": "redant-core/src/main/java/com/redant/core/common/util/GenericsUtil.java",
"chars": 2515,
"preview": "package com.redant.core.common.util;\n\nimport cn.hutool.core.util.NetUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/util/HtmlContentUtil.java",
"chars": 936,
"preview": "package com.redant.core.common.util;\n\nimport com.redant.core.common.html.HtmlMaker;\nimport com.redant.core.common.consta"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/util/HttpRenderUtil.java",
"chars": 3270,
"preview": "package com.redant.core.common.util;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.redant.core.common.html.Default"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/util/HttpRequestUtil.java",
"chars": 4513,
"preview": "package com.redant.core.common.util;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport co"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/util/PropertiesUtil.java",
"chars": 4674,
"preview": "package com.redant.core.common.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOExcepti"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/util/TagUtil.java",
"chars": 1511,
"preview": "package com.redant.core.common.util;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.Collect"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/util/ThreadUtil.java",
"chars": 279,
"preview": "package com.redant.core.common.util;\n\n/**\n * 线程工具类\n * @author houyi.wh\n * @date 2017-10-20\n */\npublic class ThreadUtil {"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/view/HtmlKeyHolder.java",
"chars": 271,
"preview": "package com.redant.core.common.view;\n\n/**\n * @author houyi.wh\n * @date 2017/12/1\n **/\npublic interface HtmlKeyHolder {\n\n"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/view/Page404.java",
"chars": 1159,
"preview": "package com.redant.core.common.view;\n\n\nimport cn.hutool.core.util.StrUtil;\n\n/**\n * @author houyi.wh\n * @date 2017/12/1\n "
},
{
"path": "redant-core/src/main/java/com/redant/core/common/view/Page500.java",
"chars": 1164,
"preview": "package com.redant.core.common.view;\n\nimport cn.hutool.core.util.StrUtil;\n\n/**\n * @author houyi.wh\n * @date 2017/12/1\n *"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/view/PageError.java",
"chars": 1357,
"preview": "package com.redant.core.common.view;\n\nimport cn.hutool.core.util.StrUtil;\n\n/**\n * @author houyi.wh\n * @date 2017/12/1\n *"
},
{
"path": "redant-core/src/main/java/com/redant/core/common/view/PageIndex.java",
"chars": 1079,
"preview": "package com.redant.core.common.view;\n\nimport cn.hutool.core.util.StrUtil;\n\n/**\n * @author houyi.wh\n * @date 2017/12/1\n *"
},
{
"path": "redant-core/src/main/java/com/redant/core/context/RedantContext.java",
"chars": 2464,
"preview": "package com.redant.core.context;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport io.netty.channel.ChannelHandle"
},
{
"path": "redant-core/src/main/java/com/redant/core/controller/ControllerProxy.java",
"chars": 1472,
"preview": "package com.redant.core.controller;\n\nimport com.redant.core.common.enums.RequestMethod;\nimport com.redant.core.render.Re"
},
{
"path": "redant-core/src/main/java/com/redant/core/controller/ProxyInvocation.java",
"chars": 6783,
"preview": "package com.redant.core.controller;\n\n\nimport com.redant.core.common.exception.InvocationException;\nimport com.redant.cor"
},
{
"path": "redant-core/src/main/java/com/redant/core/controller/annotation/Controller.java",
"chars": 384,
"preview": "package com.redant.core.controller.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Ret"
},
{
"path": "redant-core/src/main/java/com/redant/core/controller/annotation/Mapping.java",
"chars": 692,
"preview": "package com.redant.core.controller.annotation;\n\nimport com.redant.core.common.enums.RequestMethod;\nimport com.redant.cor"
},
{
"path": "redant-core/src/main/java/com/redant/core/controller/annotation/Param.java",
"chars": 621,
"preview": "package com.redant.core.controller.annotation;\n\nimport java.lang.annotation.*;\n\n@Target({ ElementType.FIELD, ElementType"
},
{
"path": "redant-core/src/main/java/com/redant/core/controller/context/ControllerContext.java",
"chars": 540,
"preview": "package com.redant.core.controller.context;\n\nimport com.redant.core.controller.ControllerProxy;\nimport io.netty.handler."
},
{
"path": "redant-core/src/main/java/com/redant/core/controller/context/DefaultControllerContext.java",
"chars": 2155,
"preview": "package com.redant.core.controller.context;\n\nimport com.redant.core.controller.ControllerProxy;\nimport com.redant.core.r"
},
{
"path": "redant-core/src/main/java/com/redant/core/converter/AbstractConverter.java",
"chars": 1752,
"preview": "package com.redant.core.converter;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.M"
},
{
"path": "redant-core/src/main/java/com/redant/core/converter/Converter.java",
"chars": 397,
"preview": "package com.redant.core.converter;\n\n/**\n * 类型转换器所需要实现的总接口。TypeConverter中有唯一的一个方法,实现类请特别注意方法所需要返回的值。\n * @author houyi.wh\n"
},
{
"path": "redant-core/src/main/java/com/redant/core/converter/PrimitiveConverter.java",
"chars": 6965,
"preview": "package com.redant.core.converter;\n\nimport java.lang.reflect.Array;\nimport java.math.BigDecimal;\nimport java.math.BigInt"
},
{
"path": "redant-core/src/main/java/com/redant/core/converter/PrimitiveTypeUtil.java",
"chars": 2610,
"preview": "package com.redant.core.converter;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.util.HashMap;\n"
},
{
"path": "redant-core/src/main/java/com/redant/core/cookie/CookieManager.java",
"chars": 1697,
"preview": "package com.redant.core.cookie;\n\nimport io.netty.handler.codec.http.cookie.Cookie;\n\nimport java.util.Map;\nimport java.ut"
},
{
"path": "redant-core/src/main/java/com/redant/core/cookie/DefaultCookieManager.java",
"chars": 3612,
"preview": "package com.redant.core.cookie;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.redant.core.context.RedantContext;\nimpor"
},
{
"path": "redant-core/src/main/java/com/redant/core/executor/AbstractExecutor.java",
"chars": 2504,
"preview": "package com.redant.core.executor;\n\nimport com.redant.core.common.constants.CommonConstants;\nimport io.netty.util.concurr"
},
{
"path": "redant-core/src/main/java/com/redant/core/executor/Executor.java",
"chars": 479,
"preview": "package com.redant.core.executor;\n\nimport io.netty.util.concurrent.Future;\nimport io.netty.util.concurrent.Promise;\n\n/**"
},
{
"path": "redant-core/src/main/java/com/redant/core/executor/HttpResponseExecutor.java",
"chars": 4535,
"preview": "package com.redant.core.executor;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.redant.core.common.except"
},
{
"path": "redant-core/src/main/java/com/redant/core/handler/ControllerDispatcher.java",
"chars": 2039,
"preview": "package com.redant.core.handler;\n\nimport com.redant.core.common.constants.CommonConstants;\nimport com.redant.core.execut"
},
{
"path": "redant-core/src/main/java/com/redant/core/handler/ssl/SslContextHelper.java",
"chars": 2781,
"preview": "package com.redant.core.handler.ssl;\n\nimport cn.hutool.crypto.SecureUtil;\nimport io.netty.handler.ssl.SslContext;\nimport"
},
{
"path": "redant-core/src/main/java/com/redant/core/init/InitExecutor.java",
"chars": 3247,
"preview": "package com.redant.core.init;\n\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.ClassScaner;"
},
{
"path": "redant-core/src/main/java/com/redant/core/init/InitFunc.java",
"chars": 165,
"preview": "package com.redant.core.init;\n\n/**\n * 初始化接口\n * @author houyi.wh\n * @date 2019-01-14\n */\npublic interface InitFunc {\n\n "
},
{
"path": "redant-core/src/main/java/com/redant/core/init/InitOrder.java",
"chars": 543,
"preview": "package com.redant.core.init;\n\nimport java.lang.annotation.*;\n\n/**\n * 初始化器的排序规则,升序排序\n * @author houyi.wh\n * @date 2019-0"
},
{
"path": "redant-core/src/main/java/com/redant/core/interceptor/Interceptor.java",
"chars": 385,
"preview": "package com.redant.core.interceptor;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author houyi\n **/\npublic abs"
},
{
"path": "redant-core/src/main/java/com/redant/core/interceptor/InterceptorBuilder.java",
"chars": 236,
"preview": "package com.redant.core.interceptor;\n\nimport java.util.List;\n\n/**\n * 用户可以通过该接口自行定义需要生效哪些拦截器\n * @author houyi\n **/\npublic"
},
{
"path": "redant-core/src/main/java/com/redant/core/interceptor/InterceptorHandler.java",
"chars": 968,
"preview": "package com.redant.core.interceptor;\n\nimport cn.hutool.core.collection.CollectionUtil;\n\nimport java.util.*;\n\n/**\n * @aut"
},
{
"path": "redant-core/src/main/java/com/redant/core/interceptor/InterceptorProvider.java",
"chars": 3976,
"preview": "package com.redant.core.interceptor;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.ClassS"
},
{
"path": "redant-core/src/main/java/com/redant/core/render/RenderType.java",
"chars": 498,
"preview": "package com.redant.core.render;\n\n/**\n * 返回的响应类型\n * @author houyi.wh\n * @date 2017-10-20\n */\npublic enum RenderType {\n\n\t/"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/BadClientSilencer.java",
"chars": 3686,
"preview": "/*\n * Copyright 2015 The Netty Project\n *\n * The Netty Project licenses this file to you under the Apache License,\n * ve"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/MethodlessRouter.java",
"chars": 5410,
"preview": "/*\n * Copyright 2015 The Netty Project\n *\n * The Netty Project licenses this file to you under the Apache License,\n * ve"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/OrderlessRouter.java",
"chars": 9704,
"preview": "/*\n * Copyright 2015 The Netty Project\n *\n * The Netty Project licenses this file to you under the Apache License,\n * ve"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/PathPattern.java",
"chars": 5623,
"preview": "/*\n * Copyright 2015 The Netty Project\n *\n * The Netty Project licenses this file to you under the Apache License,\n * ve"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/RouteResult.java",
"chars": 4197,
"preview": "/*\n * Copyright 2015 The Netty Project\n *\n * The Netty Project licenses this file to you under the Apache License,\n * ve"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/Router.java",
"chars": 20364,
"preview": "/*\n * Copyright 2015 The Netty Project\n *\n * The Netty Project licenses this file to you under the Apache License,\n * ve"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/context/DefaultRouterContext.java",
"chars": 6599,
"preview": "package com.redant.core.router.context;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport cn.hutool.core.lang.Cla"
},
{
"path": "redant-core/src/main/java/com/redant/core/router/context/RouterContext.java",
"chars": 441,
"preview": "package com.redant.core.router.context;\n\nimport com.redant.core.render.RenderType;\nimport com.redant.core.router.RouteRe"
},
{
"path": "redant-core/src/main/java/com/redant/core/server/NettyHttpServer.java",
"chars": 2077,
"preview": "package com.redant.core.server;\n\nimport com.redant.core.common.constants.CommonConstants;\nimport com.redant.core.init.In"
},
{
"path": "redant-core/src/main/java/com/redant/core/server/NettyHttpServerInitializer.java",
"chars": 2528,
"preview": "package com.redant.core.server;\n\nimport com.redant.core.common.constants.CommonConstants;\nimport com.redant.core.handler"
},
{
"path": "redant-core/src/main/java/com/redant/core/server/Server.java",
"chars": 215,
"preview": "package com.redant.core.server;\n\n/**\n * @author houyi.wh\n * @date 2019-01-10\n */\npublic interface Server {\n\n /**\n "
},
{
"path": "redant-core/src/main/java/com/redant/core/session/HttpSession.java",
"chars": 2764,
"preview": "package com.redant.core.session;\n\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelId;\n\nimp"
},
{
"path": "redant-core/src/main/java/com/redant/core/session/SessionConfig.java",
"chars": 1057,
"preview": "package com.redant.core.session;\n\n/**\n * 针对HttpSession的全局配置项\n * @author houyi.wh\n * @date 2017/11/6\n */\npublic class Ses"
},
{
"path": "redant-core/src/main/java/com/redant/core/session/SessionHelper.java",
"chars": 3043,
"preview": "package com.redant.core.session;\n\nimport com.redant.core.common.exception.InvalidSessionException;\nimport io.netty.chann"
},
{
"path": "redant-core/src/main/java/com/redant/core/session/SessionManager.java",
"chars": 668,
"preview": "package com.redant.core.session;\n\n/**\n * Session管理器\n * @author houyi.wh\n * @date 2017/11/6\n */\npublic interface SessionM"
},
{
"path": "redant-core/src/main/resources/logback.xml",
"chars": 3918,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n \n<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL --> \n<!-"
},
{
"path": "redant-core/src/main/resources/redant.properties",
"chars": 1023,
"preview": "# server port\nnetty.server.port=8888\n\n# the boss thread size which set to EventLoopGroup\nnetty.server.bossGroup.size=1\n\n"
},
{
"path": "redant-core/src/main/resources/zk.cfg",
"chars": 1389,
"preview": "{\n\t\"model\" : \"cluster\",\n\t\"configs\" : [\n\t\t{\n \"clientPort\": 2181,\n\t\t \"tickTime\": 2000,\n \"initLimi"
},
{
"path": "redant-core/src/test/java/com/redant/core/context/RedantContextTest.java",
"chars": 476,
"preview": "package com.redant.core.context;\n\n/**\n * @author houyi\n **/\npublic class RedantContextTest {\n\n public static void mai"
},
{
"path": "redant-example/pom.xml",
"chars": 1008,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www"
},
{
"path": "redant-example/src/main/java/com/redant/example/bootstrap/cluster/MasterServerBootstrap.java",
"chars": 284,
"preview": "package com.redant.example.bootstrap.cluster;\n\n/**\n * MasterServerBootstrap\n * @author houyi.wh\n * @date 2017/11/20\n **/"
},
{
"path": "redant-example/src/main/java/com/redant/example/bootstrap/cluster/SlaveServerBootstrap.java",
"chars": 281,
"preview": "package com.redant.example.bootstrap.cluster;\n\n/**\n * SlaveServerBootstrap\n * @author houyi.wh\n * @date 2017/11/20\n **/\n"
},
{
"path": "redant-example/src/main/java/com/redant/example/bootstrap/cluster/ZkBootstrap.java",
"chars": 254,
"preview": "package com.redant.example.bootstrap.cluster;\n\n/**\n * ZkBootstrap\n * @author houyi.wh\n * @date 2017/11/20\n **/\npublic cl"
},
{
"path": "redant-example/src/main/java/com/redant/example/bootstrap/standalone/ServerBootstrap.java",
"chars": 253,
"preview": "package com.redant.example.bootstrap.standalone;\n\n/**\n * 服务端启动入口\n * @author houyi.wh\n * @date 2017-10-20\n */\npublic fina"
},
{
"path": "redant-example/src/main/java/com/redant/example/controller/BaseController.java",
"chars": 998,
"preview": "package com.redant.example.controller;\n\n\nimport com.redant.core.common.enums.RequestMethod;\nimport com.redant.core.commo"
},
{
"path": "redant-example/src/main/java/com/redant/example/controller/CookieController.java",
"chars": 1547,
"preview": "package com.redant.example.controller;\n\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.redant.core.bean.annotation."
},
{
"path": "redant-example/src/main/java/com/redant/example/controller/UserController.java",
"chars": 1904,
"preview": "package com.redant.example.controller;\n\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\n"
},
{
"path": "redant-example/src/main/java/com/redant/example/interceptor/BlockInterceptor.java",
"chars": 1770,
"preview": "package com.redant.example.interceptor;\n\nimport cn.hutool.core.collection.CollectionUtil;\nimport com.alibaba.fastjson.JS"
},
{
"path": "redant-example/src/main/java/com/redant/example/interceptor/CustomInterceptorBuilder.java",
"chars": 1113,
"preview": "package com.redant.example.interceptor;\n\nimport com.redant.core.interceptor.Interceptor;\nimport com.redant.core.intercep"
},
{
"path": "redant-example/src/main/java/com/redant/example/interceptor/PerformanceInterceptor.java",
"chars": 1206,
"preview": "package com.redant.example.interceptor;\n\nimport com.redant.core.anno.Order;\nimport com.redant.core.context.RedantContext"
},
{
"path": "redant-example/src/main/java/com/redant/example/service/UserBean.java",
"chars": 818,
"preview": "package com.redant.example.service;\n\n\nimport com.alibaba.fastjson.JSON;\n\nimport java.io.Serializable;\n\n/**\n * UserBean\n "
},
{
"path": "redant-example/src/main/java/com/redant/example/service/UserService.java",
"chars": 306,
"preview": "package com.redant.example.service;\n\n/**\n * @author houyi.wh\n * @date 2017/12/1\n **/\npublic interface UserService {\n\n "
},
{
"path": "redant-example/src/main/java/com/redant/example/service/UserServiceImpl.java",
"chars": 474,
"preview": "package com.redant.example.service;\n\nimport com.redant.core.bean.annotation.Bean;\n\n/**\n * @author houyi.wh\n * @date 2017"
},
{
"path": "redant-example/src/main/resources/logback.xml",
"chars": 3918,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n \n<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL --> \n<!-"
},
{
"path": "redant-example/src/test/java/com/lememo/core/interceptor/InterceptorProviderTest.java",
"chars": 685,
"preview": "package com.lememo.core.interceptor;\n\nimport com.redant.core.interceptor.Interceptor;\nimport com.redant.core.interceptor"
}
]
About this extraction
This page contains the full source code of the all4you/redant GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 118 files (254.7 KB), approximately 66.9k tokens, and a symbol index with 541 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.