Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9f2e080da | ||
|
|
027fa3a18b | ||
|
|
d9f50d63f0 | ||
|
|
181486c348 | ||
|
|
74adfdfc99 | ||
|
|
c3783eb041 | ||
|
|
77451561e6 | ||
|
|
59d30b05f2 | ||
|
|
2fcf0bb644 | ||
|
|
71ab9c9728 | ||
|
|
09165127e3 | ||
|
|
c28310e0df | ||
|
|
51435a1c33 | ||
|
|
83c70b9767 | ||
|
|
5534dcd476 | ||
|
|
d18a55deaf | ||
|
|
ae2d64991c | ||
|
|
ecb6b80e5e | ||
|
|
cf332fa67a | ||
|
|
ffa80c9212 | ||
|
|
7463a8f6b5 | ||
|
|
a168897784 | ||
|
|
d39b3856ca | ||
|
|
ca9f74185b | ||
|
|
c35e421ba3 | ||
|
|
65755e0787 | ||
|
|
366c3becc4 | ||
|
|
a1ac6ec543 | ||
|
|
b27bbb7836 | ||
|
|
a57574dd10 | ||
|
|
a7dd22569c | ||
|
|
0bc0755fb3 | ||
|
|
c2edb60218 | ||
|
|
60c1a82a62 | ||
|
|
e3205128b4 | ||
|
|
a9dff0360f | ||
|
|
1e871cbee5 | ||
|
|
41aadf33f3 | ||
|
|
679567c85a | ||
|
|
b77050250c | ||
|
|
e178d1120b | ||
|
|
eca138b671 | ||
|
|
0366aef672 | ||
|
|
30103e5c8f | ||
|
|
cbba7701d8 | ||
|
|
da53bd7db9 | ||
|
|
9e7999da0f | ||
|
|
2c96f991d5 | ||
|
|
67f8127452 | ||
|
|
bce498885e | ||
|
|
eb57a25698 | ||
|
|
4e83e5bf71 | ||
|
|
19a44ce8cf | ||
|
|
0bd0df3245 | ||
|
|
4f0163736f | ||
|
|
8d03f52f09 | ||
|
|
815267a590 | ||
|
|
77f8d442b2 | ||
|
|
aacda5d35e | ||
|
|
58d02f6471 | ||
|
|
c735874cff | ||
|
|
045029b4a9 | ||
|
|
f6b5882cd4 | ||
|
|
63a9005e6b | ||
|
|
c4923f317b | ||
|
|
15e03c0459 | ||
|
|
74f4ddf50b | ||
|
|
fdc868641d | ||
|
|
910eb88c55 | ||
|
|
b597131de4 | ||
|
|
56d5f97556 | ||
|
|
b364dd5811 | ||
|
|
4fec27498c | ||
|
|
7a195ecf23 | ||
|
|
95b7e819cd | ||
|
|
73d243aaf1 | ||
|
|
998fecdd51 | ||
|
|
b1ddc0e3a5 | ||
|
|
641ff4709d | ||
|
|
13f2fbf7d6 | ||
|
|
62f9882314 | ||
|
|
7f270eb9d7 | ||
|
|
974a6bfeaa | ||
|
|
4958b454af | ||
|
|
3531d0963d | ||
|
|
41e6497a2e | ||
|
|
3439fab690 | ||
|
|
14274c8d04 | ||
|
|
b3cbd9be71 | ||
|
|
b1d810188c | ||
|
|
4b48f85162 | ||
|
|
738b02e1b9 | ||
|
|
dc487f9226 | ||
|
|
bb2f43c317 | ||
|
|
be61aef123 | ||
|
|
6ad7888e85 | ||
|
|
242adb3c9e | ||
|
|
8654c69d0c | ||
|
|
a7999ff160 | ||
|
|
9c04b8aab0 | ||
|
|
3643fefc9c | ||
|
|
47189901e5 | ||
|
|
2577684897 | ||
|
|
77396df8fd | ||
|
|
c517a1d469 | ||
|
|
b7d7e6567b | ||
|
|
67807e913e | ||
|
|
19a950dab5 | ||
|
|
79b91f8386 | ||
|
|
0359a4b7e9 | ||
|
|
d89f410749 | ||
|
|
850f6dd060 | ||
|
|
d891c6c8dc | ||
|
|
0a4a88ed5a | ||
|
|
3c02219da0 | ||
|
|
65efc3372e | ||
|
|
3acea66788 | ||
|
|
b448514e40 | ||
|
|
024147344b | ||
|
|
52a34d3871 | ||
|
|
1ada26e4dd | ||
|
|
94d1b61f81 | ||
|
|
29299edb90 | ||
|
|
a2e2c5e178 | ||
|
|
8ae39df2e8 | ||
|
|
508b269a82 | ||
|
|
a8627b6105 | ||
|
|
8fee6b2c68 | ||
|
|
dd58571ffd | ||
|
|
8c25683cc5 | ||
|
|
a96f003b8c | ||
|
|
ff01443246 | ||
|
|
e915a253f8 | ||
|
|
b463389733 | ||
|
|
41c97b92c7 | ||
|
|
1142f81e9c | ||
|
|
5bc9f77b7b | ||
|
|
c5d0582807 | ||
|
|
525e65d152 | ||
|
|
d948c7af47 | ||
|
|
11a29b4ed6 | ||
|
|
e31c4a3041 | ||
|
|
2928d5fc93 | ||
|
|
12fc6f7f10 | ||
|
|
4bd8c207b4 | ||
|
|
be030a3640 | ||
|
|
ebaa250f7b | ||
|
|
826a2d7ee6 | ||
|
|
e476cf8176 | ||
|
|
03115694f9 | ||
|
|
26ffb04834 | ||
|
|
2979fcc33d | ||
|
|
9a29a11e22 | ||
|
|
d73a27be71 | ||
|
|
d77f424504 | ||
|
|
3142ad6041 | ||
|
|
6044f014c7 | ||
|
|
d1cfdfa14f | ||
|
|
8f9bfc3f28 | ||
|
|
9ae847d392 | ||
|
|
178226b730 | ||
|
|
801e45abce | ||
|
|
cd54a7040a | ||
|
|
3bd880b061 | ||
|
|
bf355cce28 | ||
|
|
58d08c5787 | ||
|
|
a778af73d8 | ||
|
|
7ca95c3549 | ||
|
|
54933ac3ac | ||
|
|
255048bf5b | ||
|
|
206fa19f3e | ||
|
|
f0e9047f8c | ||
|
|
698966d551 | ||
|
|
fc6b5cb458 | ||
|
|
9eee3bfa58 | ||
|
|
0adc0845fd | ||
|
|
92f98eff5f | ||
|
|
d97f8acf23 | ||
|
|
47e14bf2ec | ||
|
|
8eed4083bc | ||
|
|
9ef7641cd1 | ||
|
|
bafb6065c8 | ||
|
|
8b2460b8ab | ||
|
|
827172e743 | ||
|
|
fa9bd30de5 | ||
|
|
78e66ff74b | ||
|
|
7e8d1c3567 | ||
|
|
c3a7603674 | ||
|
|
4b8cfbba00 | ||
|
|
30771e5366 | ||
|
|
313c7f4ba1 | ||
|
|
ece4215a8a | ||
|
|
7ea740edf1 | ||
|
|
716f4e6934 | ||
|
|
90ab302667 | ||
|
|
4695949362 | ||
|
|
4827893a0d | ||
|
|
9276f220b0 | ||
|
|
4071a5d165 | ||
|
|
da0ff24af6 | ||
|
|
afef635146 | ||
|
|
eda9d1c780 | ||
|
|
4b5749bc60 | ||
|
|
6bb23008c2 | ||
|
|
7746971b60 | ||
|
|
bc20c82fef | ||
|
|
fff70ed241 | ||
|
|
c590d45ce0 | ||
|
|
8bab9ad22b | ||
|
|
3c457dad2a | ||
|
|
eaae598234 | ||
|
|
606faf1bf8 | ||
|
|
ae9aa94323 | ||
|
|
2e41e44294 | ||
|
|
d806d9d6ff | ||
|
|
1260736c14 | ||
|
|
08c5cbbbf3 | ||
|
|
af0726cd79 | ||
|
|
ce2279030d | ||
|
|
83aba2ebee | ||
|
|
5c11742b51 | ||
|
|
5295e04275 | ||
|
|
47f723e63b | ||
|
|
54956e47d2 | ||
|
|
b2cbdf6642 | ||
|
|
989d1c6db9 | ||
|
|
e139b0cc5d | ||
|
|
66261e98b5 | ||
|
|
d9a268d30a | ||
|
|
a8bc50a947 | ||
|
|
6aa96daae2 | ||
|
|
293805a55e | ||
|
|
8b6319888c | ||
|
|
408676e97a | ||
|
|
1fabbae4f6 | ||
|
|
09a5b41d96 | ||
|
|
9d85a4dcaf | ||
|
|
9bc60c1c47 | ||
|
|
92aff864ef | ||
|
|
254e2e8ccd | ||
|
|
2480d127ac | ||
|
|
cc3d82e864 |
@@ -8,14 +8,14 @@
|
|||||||
<li>提供HTTP服务,同时内置JSON功能与限时缓存功能</li>
|
<li>提供HTTP服务,同时内置JSON功能与限时缓存功能</li>
|
||||||
<li>TCP层完全使用NIO.2,并统一TCP与UDP的接口换</li>
|
<li>TCP层完全使用NIO.2,并统一TCP与UDP的接口换</li>
|
||||||
<li>提供分布式与集中式部署的无缝切换</li>
|
<li>提供分布式与集中式部署的无缝切换</li>
|
||||||
<li>提供类似JPA功能,并包含数据缓存自动同步与简洁的数据层操作接口</li>
|
<li>提供类似JPA功能,包含数据缓存自动同步、分表分库与简洁的数据层操作接口</li>
|
||||||
<li>可以动态修改已依赖注入的资源</li>
|
<li>可以动态修改已依赖注入的资源</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<strong>Redkale 设计理念</strong>
|
<strong>Redkale 设计理念</strong>
|
||||||
<p>
|
<p>
|
||||||
作为一个全新的微服务框架,不仅是使用了Java8的新语法,更多是设计上与主流框架有所不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,而且也可以动态更改已注入的资源。Spring提倡控制反转思想,偏偏自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。若开发者不想使用Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若开发者想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现。这其实包含了控制反转的思想,让框架里的零件可以让开发者控制。<br/>
|
作为一个全新的微服务框架,Redkale在接口定义上使用了Java 8大量的新语法,接口有默认实现、接口带静态方法、重复注解等特性,同时在设计上与主流框架有很大不同。Redkale是按组件形式设计的,而非以容器为主,几乎每个子包都是能提供独立功能的组件。如Tomcat是按容器设计的,所有web资源/配置由Tomcat控制,开发者很能难控制到Tomcat内部,而Redkale的HTTP服务只是个组件,开发者既可以自己启动和配置HttpServer,也可以把Redkale当成容器通过Redkale进程来初始化服务。Spring的Ioc容器也是如此,Redkale提供的依赖注入仅通过ResouceFactory一个类来控制,非常轻量,并且可动态更改已注入的资源。Spring提倡控制反转思想,而自身的容器却让开发者很难控制。Redkale是一个既能以组件形式也能以容器形式存在的框架。从整体上看,Redkale的架构分两层:接口和默认实现。开发者若想替换掉Redkale内置的HTTP服务而使用符合JavaEE规范的HttpServlet, 可以采用自定义协议基于JSR 340(Servlet 3.1)来实现自己的HTTP服务;若想使用Hibernate作为数据库操作,可以写一个自己的DataSource实现类;JSON的序列化和反序列化也可以使用第三方的实现;Memcached或Redis也可以作为另一个CacheSource的实现替换Redkale的默认实现。这其实包含了控制反转的思想,让框架里的各个组件均可让开发者控制。<br/>
|
||||||
与主流框架比,功能上Redkale显得很简单,这也是Redkale的一个特点并非不足,从一个良好的设计习惯或架构上来看,有些常用功能是不需要提供的,比如Redkale的HTTP服务不支持HTTPS和JSP,HTTPS比HTTP多了一层加密解密,这种密集的数字计算不是Java的专长,同时一个稍好的提供HTTP服务的架构不会将Java动态服务器放在最前端,通常前面会放nginx或apache,除了负载均衡还能静动分离,既然Java服务器前面有C写的服务器,那么HTTPS的加解密就应该交给前面的服务器处理。Redkale再提供HTTPS服务就显得鸡肋。JSP其实算是一个落后的技术,现在是一个多样化终端的时代,终端不只局限于桌面程序和PC浏览器,还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端,这些都不是JSP能兼顾的,而HTTP+JSON作为通用性接口可以避免重复开发,模版引擎的功能加上各种强大的JS框架足以取代JSP(如果初级程序员还花大量时间去学习基于JSP的Struts或Spring MVC框架,就有点跟不上时代了)。Redkale在功能上做了筛选,不会只因为迎合主流而提供,而是以良好的设计思想为指导。这也是Redkale很重要的一个思想。
|
与主流框架比,功能上Redkale显得很简单,这体现了Redkale的简易性,而并非是不足,从一个良好的设计习惯或架构上来看,有些常用功能是不需要提供的,如Redkale的HTTP服务不支持HTTPS和JSP,HTTPS比HTTP多了一层加密解密,这种密集型的计算不是Java的专长,通常提供HTTP服务的架构不会将Java动态服务器放在最前端,而是在前方会放nginx或apache,除了负载均衡还能静动分离,因此HTTPS的加解密应交给nginx这样的高性能服务器处理。Redkale再提供HTTPS服务就显得鸡肋。JSP其实算是一个落后的技术,现在是一个多样化终端的时代,终端不只局限于桌面程序和PC浏览器,还有原生App、混合式App、微信端、移动H5、提供第三方接口等各种形式的终端,这些都不是JSP能方便兼顾的,而HTTP+JSON作为通用性接口可以避免重复开发,模版引擎的功能加上各种强大的JS框架足以取代JSP。Redkale在功能上做了筛选,不会为了迎合主流而提供,而是以良好的设计思想为指导。这是Redkale的主导思维。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
32
assembly.xml
Normal file
32
assembly.xml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
|
||||||
|
<id>redkale</id>
|
||||||
|
<formats>
|
||||||
|
<format>tar.gz</format>
|
||||||
|
</formats>
|
||||||
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
|
<fileSets>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.basedir}/bin</directory>
|
||||||
|
<outputDirectory>bin</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.basedir}/conf</directory>
|
||||||
|
<outputDirectory>conf</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
<fileSet>
|
||||||
|
<directory>${project.basedir}/logs</directory>
|
||||||
|
<outputDirectory>logs</outputDirectory>
|
||||||
|
</fileSet>
|
||||||
|
</fileSets>
|
||||||
|
<dependencySets>
|
||||||
|
<dependencySet>
|
||||||
|
<useProjectArtifact>true</useProjectArtifact>
|
||||||
|
<useTransitiveDependencies>false</useTransitiveDependencies>
|
||||||
|
<outputDirectory>lib</outputDirectory>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependencySet>
|
||||||
|
</dependencySets>
|
||||||
|
</assembly>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<application port="5050">
|
<application port="5050">
|
||||||
|
|
||||||
|
<!-- 详细配置说明见: http://redkale.org/redkale.html#redkale_confxml -->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<!--
|
<!--
|
||||||
<properties>
|
<properties>
|
||||||
@@ -9,6 +12,7 @@
|
|||||||
-->
|
-->
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
|
||||||
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
|
<server protocol="HTTP" host="0.0.0.0" port="6060" root="root">
|
||||||
<!--
|
<!--
|
||||||
<request>
|
<request>
|
||||||
|
|||||||
150
pom.xml
Normal file
150
pom.xml
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
<?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>org.redkale</groupId>
|
||||||
|
<artifactId>redkale</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<url>http://redkale.org</url>
|
||||||
|
<description>redkale -- java framework</description>
|
||||||
|
<version>1.6.2</version>
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>Apache 2</name>
|
||||||
|
<url>http://www.apache.org/licenses/</url>
|
||||||
|
<distribution>repo</distribution>
|
||||||
|
<comments>Apache License</comments>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>Redkale</id>
|
||||||
|
<name>redkale</name>
|
||||||
|
<email>redkale@qq.com</email>
|
||||||
|
<url>http://redkale.org</url>
|
||||||
|
<roles>
|
||||||
|
<role>Project Manager</role>
|
||||||
|
<role>Architect</role>
|
||||||
|
</roles>
|
||||||
|
<organization>redkale</organization>
|
||||||
|
<organizationUrl>http://redkale.org</organizationUrl>
|
||||||
|
<properties>
|
||||||
|
<dept>No</dept>
|
||||||
|
</properties>
|
||||||
|
<timezone>8</timezone>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
<name>Redkale</name>
|
||||||
|
<distributionManagement>
|
||||||
|
<snapshotRepository>
|
||||||
|
<id>ossrh</id>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||||
|
</snapshotRepository>
|
||||||
|
<repository>
|
||||||
|
<id>ossrh</id>
|
||||||
|
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
<scm>
|
||||||
|
<url>https://github.com/redkale/redkale</url>
|
||||||
|
<connection>scm:git:git@github.com/redkale/redkale.git</connection>
|
||||||
|
<developerConnection>scm:git:git@github.com:redkale/redkale.git</developerConnection>
|
||||||
|
</scm>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<configuration>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<compilerArguments>
|
||||||
|
<verbose />
|
||||||
|
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
|
||||||
|
</compilerArguments>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>org.redkale.boot.Application</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
|
<version>1.6</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>sign-artifacts</id>
|
||||||
|
<phase>verify</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>sign</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>2.10.3</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<appendAssemblyId>false</appendAssemblyId>
|
||||||
|
<descriptors>
|
||||||
|
<descriptor>assembly.xml</descriptor>
|
||||||
|
</descriptors>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>redkale</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
一个组包含多个NODE, 同一Service服务可以由多个进程提供,这些进程称为一个GROUP,且同一GROUP内的进程必须在同一机房或局域网内
|
一个组包含多个NODE, 同一Service服务可以由多个进程提供,这些进程称为一个GROUP,且同一GROUP内的进程必须在同一机房或局域网内
|
||||||
一个group节点对应一个 Transport 对象。
|
一个group节点对应一个 Transport 对象。
|
||||||
name: 服务组ID,长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
|
name: 服务组ID,长度不能超过11个字节. 默认为空字符串。 注意: name不能包含$符号。
|
||||||
protocol:值只能是UDP TCP, 默认TCP
|
protocol:值范围:UDP TCP, 默认TCP
|
||||||
kind: 与SNCP服务连接时的数据传输类型;可选值有:rest(不区分大小写);值为空或空字符串表示按SNCP协议传输; 为rest表示按REST传输。默认值为空
|
subprotocol: 子协议,预留字段。默认值为空
|
||||||
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息, 就必须有group节点信息。
|
注意: 一个node只能所属一个group。只要存在protocol=SNCP的Server节点信息, 就必须有group节点信息。
|
||||||
-->
|
-->
|
||||||
<group name="" protocol="TCP">
|
<group name="" protocol="TCP">
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
</group>
|
</group>
|
||||||
<!--
|
<!--
|
||||||
【节点全局唯一】
|
【节点全局唯一】
|
||||||
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入, 被注解的字段类型只能是String、primitive class
|
全局的参数配置, 可以通过@Resource(name="property.xxxxxx") 进行注入<property>的信息, 被注解的字段类型只能是String、primitive class
|
||||||
如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。
|
如果name是system.property.开头的值将会在进程启动时进行System.setProperty("yyyy", "YYYYYY")操作。
|
||||||
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
|
如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。
|
||||||
load: 加载文件,多个用;隔开。
|
load: 加载文件,多个用;隔开。
|
||||||
@@ -70,6 +70,8 @@
|
|||||||
System.setProperty("convert.bson.pool.size", "128");
|
System.setProperty("convert.bson.pool.size", "128");
|
||||||
System.setProperty("convert.json.writer.buffer.defsize", "4096");
|
System.setProperty("convert.json.writer.buffer.defsize", "4096");
|
||||||
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
|
System.setProperty("convert.bson.writer.buffer.defsize", "4096");
|
||||||
|
|
||||||
|
<properties>节点下也可包含非<property>节点,其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[]
|
||||||
-->
|
-->
|
||||||
<properties load="config.properties">
|
<properties load="config.properties">
|
||||||
<property name="system.property.yyyy" value="YYYYYY"/>
|
<property name="system.property.yyyy" value="YYYYYY"/>
|
||||||
@@ -77,6 +79,7 @@
|
|||||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||||
<property name="xxxxxx" value="XXXXXXXX"/>
|
<property name="xxxxxx" value="XXXXXXXX"/>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
<!--
|
<!--
|
||||||
protocol: required server所启动的协议,Redkale内置的有HTTP、SNCP,SNCP使用TCP实现;
|
protocol: required server所启动的协议,Redkale内置的有HTTP、SNCP,SNCP使用TCP实现;
|
||||||
@@ -137,7 +140,7 @@
|
|||||||
includes:当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
includes:当autoload="true", 拉取类名与includes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||||
excludes:当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
excludes:当autoload="true", 排除类名与excludes中的正则表达式匹配的类, 多个正则表达式用分号;隔开
|
||||||
-->
|
-->
|
||||||
<rest base="org.redkale.net.http.DefaultRestServlet" mustsign="false" autoload="true" includes="" excludes="">
|
<rest base="org.redkale.net.http.DefaultRestServlet" mustsign="true" autoload="true" includes="" excludes="">
|
||||||
<!--
|
<!--
|
||||||
value: Service类名,列出的表示必须被加载的Service对象
|
value: Service类名,列出的表示必须被加载的Service对象
|
||||||
ignore: 是否忽略,设置为true则不会加载该Service对象,默认值为false
|
ignore: 是否忽略,设置为true则不会加载该Service对象,默认值为false
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ java.util.logging.FileHandler.limit = 10485760
|
|||||||
java.util.logging.FileHandler.count = 100
|
java.util.logging.FileHandler.count = 100
|
||||||
java.util.logging.FileHandler.encoding = UTF-8
|
java.util.logging.FileHandler.encoding = UTF-8
|
||||||
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%u.log
|
java.util.logging.FileHandler.pattern = ${APP_HOME}/logs-%m/log-%u.log
|
||||||
|
#java.util.logging.FileHandler.unusual \u5c5e\u6027\u8868\u793a\u5c06 WARNING\u3001SEVERE \u7ea7\u522b\u7684\u65e5\u5fd7\u590d\u5236\u5199\u5165\u5355\u72ec\u7684\u6587\u4ef6\u4e2d
|
||||||
|
#java.util.logging.FileHandler.unusual = ${APP_HOME}/logs-%m/log-error-%u.log
|
||||||
java.util.logging.FileHandler.append = true
|
java.util.logging.FileHandler.append = true
|
||||||
|
|
||||||
#java.util.logging.ConsoleHandler.level = FINE
|
#java.util.logging.ConsoleHandler.level = FINE
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
<!-- 为NONE表示不启动缓存,@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 -->
|
<!-- 为NONE表示不启动缓存,@Cacheable 失效; 非NONE值(通常用ALL)表示开启缓存。 -->
|
||||||
<shared-cache-mode>NONE</shared-cache-mode>
|
<shared-cache-mode>NONE</shared-cache-mode>
|
||||||
<properties>
|
<properties>
|
||||||
|
<!--
|
||||||
|
DataSource的实现类,没有设置默认为org.redkale.source.DataJdbcSource的实现,使用常规基于JDBC的数据库驱动一般无需设置
|
||||||
|
-->
|
||||||
|
<property name="javax.persistence.datasource" value="org.redkale.source.DataJdbcSource"/>
|
||||||
|
|
||||||
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
|
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbuser?characterEncoding=utf8"/>
|
||||||
<!--
|
<!--
|
||||||
javax.persistence.jdbc.driver在JPA的值是JDBC驱动,Redkale有所不同,值应该是javax.sql.DataSource的子类。
|
javax.persistence.jdbc.driver在JPA的值是JDBC驱动,Redkale有所不同,值应该是javax.sql.DataSource的子类。
|
||||||
@@ -16,6 +21,7 @@
|
|||||||
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
|
oracle.jdbc.driver.OracleDriver —————— oracle.jdbc.pool.OracleConnectionPoolDataSource
|
||||||
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
|
com.microsoft.sqlserver.jdbc.SQLServerDriver —————— com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource
|
||||||
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
|
因此 com.mysql.jdbc.Driver 会被自动转换成 com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
|
||||||
|
并且如果JDBC驱动是以上几个版本,javax.persistence.jdbc.driver属性都可以省略,Redkale会根据javax.persistence.jdbc.url的值来识别驱动
|
||||||
-->
|
-->
|
||||||
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
|
||||||
<property name="javax.persistence.jdbc.user" value="root"/>
|
<property name="javax.persistence.jdbc.user" value="root"/>
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import java.lang.annotation.Target;
|
|||||||
* subclasses; it can be overridden by specifying
|
* subclasses; it can be overridden by specifying
|
||||||
* <code>Cacheable</code> on a subclass.
|
* <code>Cacheable</code> on a subclass.
|
||||||
*
|
*
|
||||||
* <p> <code>Cacheable(false)</code> means that the entity and its state must
|
* <p>
|
||||||
|
* <code>Cacheable(false)</code> means that the entity and its state must
|
||||||
* not be cached by the provider.
|
* not be cached by the provider.
|
||||||
*
|
*
|
||||||
* @since Java Persistence 2.0
|
* @since Java Persistence 2.0
|
||||||
@@ -39,7 +40,15 @@ public @interface Cacheable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* (Optional) Whether or not the entity should be cached.
|
* (Optional) Whether or not the entity should be cached.
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
boolean value() default true;
|
boolean value() default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) 定时自动更新缓存的周期秒数,为0表示不做定时更新, 大于0表示每经过interval秒后会自动从数据库中拉取数据更新Cache
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int interval() default 0;
|
||||||
}
|
}
|
||||||
|
|||||||
69
src/javax/persistence/Index.java
Normal file
69
src/javax/persistence/Index.java
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/** *****************************************************************************
|
||||||
|
* Copyright (c) 2011 - 2013 Oracle Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
|
||||||
|
* which accompanies this distribution.
|
||||||
|
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Linda DeMichiel - Java Persistence 2.1
|
||||||
|
*
|
||||||
|
***************************************************************************** */
|
||||||
|
package javax.persistence;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in schema generation to specify creation of an index.
|
||||||
|
* <p>
|
||||||
|
* Note that it is not necessary to specify an index for a primary key,
|
||||||
|
* as the primary key index will be created automatically.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The syntax of the <code>columnList</code> element is a
|
||||||
|
* <code>column_list</code>, as follows:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* column::= index_column [,index_column]*
|
||||||
|
* index_column::= column_name [ASC | DESC]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If <code>ASC</code> or <code>DESC</code> is not specified,
|
||||||
|
* <code>ASC</code> (ascending order) is assumed.
|
||||||
|
*
|
||||||
|
* @since Java Persistence 2.1
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Target({})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface Index {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) The name of the index; defaults to a provider-generated name.
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String name() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Required) The names of the columns to be included in the index,
|
||||||
|
* in order.
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String columnList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) Whether the index is unique.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
boolean unique() default false;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -59,4 +59,29 @@ public @interface Table {
|
|||||||
*/
|
*/
|
||||||
String catalog() default "";
|
String catalog() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) Unique constraints that are to be placed on
|
||||||
|
* the table. These are only used if table generation is in
|
||||||
|
* effect. These constraints apply in addition to any constraints
|
||||||
|
* specified by the <code>Column</code> and <code>JoinColumn</code>
|
||||||
|
* annotations and constraints entailed by primary key mappings.
|
||||||
|
* <p>
|
||||||
|
* Defaults to no additional constraints.
|
||||||
|
* @return UniqueConstraint[]
|
||||||
|
*/
|
||||||
|
UniqueConstraint[] uniqueConstraints() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Optional) Indexes for the table. These are only used if
|
||||||
|
* table generation is in effect. Note that it is not necessary
|
||||||
|
* to specify an index for a primary key, as the primary key
|
||||||
|
* index will be created automatically.
|
||||||
|
*
|
||||||
|
* @return indexes
|
||||||
|
* @since Java Persistence 2.1
|
||||||
|
*/
|
||||||
|
Index[] indexes() default {};
|
||||||
|
|
||||||
|
String comment() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
56
src/javax/persistence/UniqueConstraint.java
Normal file
56
src/javax/persistence/UniqueConstraint.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/** *****************************************************************************
|
||||||
|
* Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
|
||||||
|
* which accompanies this distribution.
|
||||||
|
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
* and the Eclipse Distribution License is available at
|
||||||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Linda DeMichiel - Java Persistence 2.1
|
||||||
|
* Linda DeMichiel - Java Persistence 2.0
|
||||||
|
*
|
||||||
|
***************************************************************************** */
|
||||||
|
package javax.persistence;
|
||||||
|
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies that a unique constraint is to be included in
|
||||||
|
* the generated DDL for a primary or secondary table.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Example:
|
||||||
|
* @Entity
|
||||||
|
* @Table(
|
||||||
|
* name="EMPLOYEE",
|
||||||
|
* uniqueConstraints=
|
||||||
|
* @UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})
|
||||||
|
* )
|
||||||
|
* public class Employee { ... }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @since Java Persistence 1.0
|
||||||
|
*/
|
||||||
|
@Target({})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface UniqueConstraint {
|
||||||
|
|
||||||
|
/** (Optional) Constraint name. A provider-chosen name will be chosen
|
||||||
|
* if a name is not specified.
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
* @since Java Persistence 2.0
|
||||||
|
*/
|
||||||
|
String name() default "";
|
||||||
|
|
||||||
|
/** (Required) An array of the column names that make up the constraint.
|
||||||
|
*
|
||||||
|
* @return String[]
|
||||||
|
*/
|
||||||
|
String[] columnNames();
|
||||||
|
}
|
||||||
@@ -8,21 +8,24 @@ package org.redkale.boot;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.*;
|
||||||
import org.redkale.convert.json.JsonConvert;
|
import org.redkale.convert.json.JsonConvert;
|
||||||
import org.redkale.net.http.*;
|
import org.redkale.net.http.*;
|
||||||
|
import org.redkale.source.*;
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 继承 HttpBaseServlet 是为了获取 WebAction 信息
|
* API接口文档生成类,作用:生成Application实例中所有HttpServer的可用HttpServlet的API接口方法 <br>
|
||||||
|
* 继承 HttpBaseServlet 是为了获取 WebMapping 信息
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
public class ApiDocs extends HttpBaseServlet {
|
public class ApiDocs extends HttpBaseServlet {
|
||||||
|
|
||||||
private final Application app;
|
private final Application app; //Application全局对象
|
||||||
|
|
||||||
public ApiDocs(Application app) {
|
public ApiDocs(Application app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
@@ -31,7 +34,7 @@ public class ApiDocs extends HttpBaseServlet {
|
|||||||
public void run() throws Exception {
|
public void run() throws Exception {
|
||||||
List<Map> serverList = new ArrayList<>();
|
List<Map> serverList = new ArrayList<>();
|
||||||
|
|
||||||
Map<String, Map<String, Map<String, String>>> typesmap = new LinkedHashMap<>();
|
Map<String, Map<String, Map<String, Object>>> typesmap = new LinkedHashMap<>();
|
||||||
for (NodeServer node : app.servers) {
|
for (NodeServer node : app.servers) {
|
||||||
if (!(node instanceof NodeHttpServer)) continue;
|
if (!(node instanceof NodeHttpServer)) continue;
|
||||||
final Map<String, Object> map = new LinkedHashMap<>();
|
final Map<String, Object> map = new LinkedHashMap<>();
|
||||||
@@ -49,30 +52,76 @@ public class ApiDocs extends HttpBaseServlet {
|
|||||||
}
|
}
|
||||||
final Map<String, Object> servletmap = new LinkedHashMap<>();
|
final Map<String, Object> servletmap = new LinkedHashMap<>();
|
||||||
String prefix = _prefix(servlet);
|
String prefix = _prefix(servlet);
|
||||||
String[] mappings = ws.value();
|
String[] urlregs = ws.value();
|
||||||
if (prefix != null && !prefix.isEmpty()) {
|
if (prefix != null && !prefix.isEmpty()) {
|
||||||
for (int i = 0; i < mappings.length; i++) {
|
for (int i = 0; i < urlregs.length; i++) {
|
||||||
mappings[i] = prefix + mappings[i];
|
urlregs[i] = prefix + urlregs[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
servletmap.put("mappings", mappings);
|
servletmap.put("urlregs", urlregs);
|
||||||
servletmap.put("moduleid", ws.moduleid());
|
servletmap.put("moduleid", ws.moduleid());
|
||||||
servletmap.put("name", ws.name());
|
servletmap.put("name", ws.name());
|
||||||
servletmap.put("comment", ws.comment());
|
servletmap.put("comment", ws.comment());
|
||||||
|
|
||||||
List<Map> actionsList = new ArrayList<>();
|
List<Map> mappingsList = new ArrayList<>();
|
||||||
servletmap.put("actions", actionsList);
|
servletmap.put("mappings", mappingsList);
|
||||||
for (Method method : servlet.getClass().getMethods()) {
|
final Class selfClz = servlet.getClass();
|
||||||
|
Class clz = servlet.getClass();
|
||||||
|
HashSet<String> actionurls = new HashSet<>();
|
||||||
|
do {
|
||||||
|
if (Modifier.isAbstract(clz.getModifiers())) break;
|
||||||
|
for (Method method : clz.getMethods()) {
|
||||||
if (method.getParameterCount() != 2) continue;
|
if (method.getParameterCount() != 2) continue;
|
||||||
WebAction action = method.getAnnotation(WebAction.class);
|
WebMapping action = method.getAnnotation(WebMapping.class);
|
||||||
if (action == null) continue;
|
if (action == null) continue;
|
||||||
final Map<String, Object> actionmap = new LinkedHashMap<>();
|
if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法
|
||||||
actionmap.put("url", prefix + action.url());
|
final Map<String, Object> mappingmap = new LinkedHashMap<>();
|
||||||
actionmap.put("auth", method.getAnnotation(AuthIgnore.class) == null);
|
if (actionurls.contains(action.url())) continue;
|
||||||
actionmap.put("actionid", action.actionid());
|
mappingmap.put("url", prefix + action.url());
|
||||||
actionmap.put("comment", action.comment());
|
actionurls.add(action.url());
|
||||||
|
mappingmap.put("auth", method.getAnnotation(AuthIgnore.class) == null);
|
||||||
|
mappingmap.put("actionid", action.actionid());
|
||||||
|
mappingmap.put("comment", action.comment());
|
||||||
List<Map> paramsList = new ArrayList<>();
|
List<Map> paramsList = new ArrayList<>();
|
||||||
actionmap.put("params", paramsList);
|
mappingmap.put("params", paramsList);
|
||||||
|
List<String> results = new ArrayList<>();
|
||||||
|
for (final Class rtype : action.results()) {
|
||||||
|
results.add(rtype.getName());
|
||||||
|
if (typesmap.containsKey(rtype.getName())) continue;
|
||||||
|
final boolean filter = FilterBean.class.isAssignableFrom(rtype);
|
||||||
|
final Map<String, Map<String, Object>> typemap = new LinkedHashMap<>();
|
||||||
|
Class loop = rtype;
|
||||||
|
do {
|
||||||
|
if (loop == null || loop.isInterface()) break;
|
||||||
|
for (Field field : loop.getDeclaredFields()) {
|
||||||
|
if (Modifier.isFinal(field.getModifiers())) continue;
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||||
|
|
||||||
|
Map<String, Object> fieldmap = new LinkedHashMap<>();
|
||||||
|
fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName());
|
||||||
|
|
||||||
|
Comment comment = field.getAnnotation(Comment.class);
|
||||||
|
Column col = field.getAnnotation(Column.class);
|
||||||
|
FilterColumn fc = field.getAnnotation(FilterColumn.class);
|
||||||
|
if (comment != null) {
|
||||||
|
fieldmap.put("comment", comment.value());
|
||||||
|
} else if (col != null) {
|
||||||
|
fieldmap.put("comment", col.comment());
|
||||||
|
} else if (fc != null) {
|
||||||
|
fieldmap.put("comment", fc.comment());
|
||||||
|
}
|
||||||
|
fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
|
||||||
|
fieldmap.put("updatable", (filter || col == null || col.updatable()));
|
||||||
|
if (servlet.getClass().getAnnotation(Rest.RestDynamic.class) != null) {
|
||||||
|
if (field.getAnnotation(RestAddress.class) != null) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
typemap.put(field.getName(), fieldmap);
|
||||||
|
}
|
||||||
|
} while ((loop = loop.getSuperclass()) != Object.class);
|
||||||
|
typesmap.put(rtype.getName(), typemap);
|
||||||
|
}
|
||||||
|
mappingmap.put("results", results);
|
||||||
for (WebParam param : method.getAnnotationsByType(WebParam.class)) {
|
for (WebParam param : method.getAnnotationsByType(WebParam.class)) {
|
||||||
final Map<String, Object> parammap = new LinkedHashMap<>();
|
final Map<String, Object> parammap = new LinkedHashMap<>();
|
||||||
final boolean isarray = param.type().isArray();
|
final boolean isarray = param.type().isArray();
|
||||||
@@ -82,28 +131,35 @@ public class ApiDocs extends HttpBaseServlet {
|
|||||||
parammap.put("type", ptype.getName() + (isarray ? "[]" : ""));
|
parammap.put("type", ptype.getName() + (isarray ? "[]" : ""));
|
||||||
parammap.put("src", param.src());
|
parammap.put("src", param.src());
|
||||||
parammap.put("comment", param.comment());
|
parammap.put("comment", param.comment());
|
||||||
|
parammap.put("required", param.required());
|
||||||
paramsList.add(parammap);
|
paramsList.add(parammap);
|
||||||
if (ptype.isPrimitive() || ptype == String.class) continue;
|
if (ptype.isPrimitive() || ptype == String.class) continue;
|
||||||
if (typesmap.containsKey(ptype.getName())) continue;
|
if (typesmap.containsKey(ptype.getName())) continue;
|
||||||
|
|
||||||
final Map<String, Map<String, String>> typemap = new LinkedHashMap<>();
|
final Map<String, Map<String, Object>> typemap = new LinkedHashMap<>();
|
||||||
Class loop = ptype;
|
Class loop = ptype;
|
||||||
|
final boolean filter = FilterBean.class.isAssignableFrom(loop);
|
||||||
do {
|
do {
|
||||||
if (loop == null || loop.isInterface()) break;
|
if (loop == null || loop.isInterface()) break;
|
||||||
for (Field field : loop.getDeclaredFields()) {
|
for (Field field : loop.getDeclaredFields()) {
|
||||||
if (Modifier.isFinal(field.getModifiers())) continue;
|
if (Modifier.isFinal(field.getModifiers())) continue;
|
||||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||||
|
|
||||||
Map<String, String> fieldmap = new LinkedHashMap<>();
|
Map<String, Object> fieldmap = new LinkedHashMap<>();
|
||||||
fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName());
|
fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName());
|
||||||
|
|
||||||
|
Column col = field.getAnnotation(Column.class);
|
||||||
|
FilterColumn fc = field.getAnnotation(FilterColumn.class);
|
||||||
Comment comment = field.getAnnotation(Comment.class);
|
Comment comment = field.getAnnotation(Comment.class);
|
||||||
if (comment != null) {
|
if (comment != null) {
|
||||||
fieldmap.put("comment", comment.value());
|
fieldmap.put("comment", comment.value());
|
||||||
} else {
|
} else if (col != null) {
|
||||||
Column col = field.getAnnotation(Column.class);
|
fieldmap.put("comment", col.comment());
|
||||||
if (col != null) fieldmap.put("comment", col.comment());
|
} else if (fc != null) {
|
||||||
|
fieldmap.put("comment", fc.comment());
|
||||||
}
|
}
|
||||||
|
fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null));
|
||||||
|
fieldmap.put("updatable", (filter || col == null || col.updatable()));
|
||||||
|
|
||||||
if (servlet.getClass().getAnnotation(Rest.RestDynamic.class) != null) {
|
if (servlet.getClass().getAnnotation(Rest.RestDynamic.class) != null) {
|
||||||
if (field.getAnnotation(RestAddress.class) != null) continue;
|
if (field.getAnnotation(RestAddress.class) != null) continue;
|
||||||
@@ -115,16 +171,17 @@ public class ApiDocs extends HttpBaseServlet {
|
|||||||
|
|
||||||
typesmap.put(ptype.getName(), typemap);
|
typesmap.put(ptype.getName(), typemap);
|
||||||
}
|
}
|
||||||
actionmap.put("result", action.result());
|
mappingmap.put("result", action.result());
|
||||||
actionsList.add(actionmap);
|
mappingsList.add(mappingmap);
|
||||||
}
|
}
|
||||||
actionsList.sort((o1, o2) -> ((String) o1.get("url")).compareTo((String) o2.get("url")));
|
} while ((clz = clz.getSuperclass()) != HttpServlet.class);
|
||||||
|
mappingsList.sort((o1, o2) -> ((String) o1.get("url")).compareTo((String) o2.get("url")));
|
||||||
servletsList.add(servletmap);
|
servletsList.add(servletmap);
|
||||||
}
|
}
|
||||||
servletsList.sort((o1, o2) -> {
|
servletsList.sort((o1, o2) -> {
|
||||||
String[] mappings1 = (String[]) o1.get("mappings");
|
String[] urlregs1 = (String[]) o1.get("urlregs");
|
||||||
String[] mappings2 = (String[]) o2.get("mappings");
|
String[] urlregs2 = (String[]) o2.get("urlregs");
|
||||||
return mappings1.length > 0 ? (mappings2.length > 0 ? mappings1[0].compareTo(mappings2[0]) : 1) : -1;
|
return urlregs1.length > 0 ? (urlregs2.length > 0 ? urlregs1[0].compareTo(urlregs2[0]) : 1) : -1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Map<String, Object> resultmap = new LinkedHashMap<>();
|
Map<String, Object> resultmap = new LinkedHashMap<>();
|
||||||
@@ -140,7 +197,7 @@ public class ApiDocs extends HttpBaseServlet {
|
|||||||
in = new FileInputStream(doctemplate);
|
in = new FileInputStream(doctemplate);
|
||||||
}
|
}
|
||||||
if (in == null) in = ApiDocs.class.getResourceAsStream("apidoc-template.html");
|
if (in == null) in = ApiDocs.class.getResourceAsStream("apidoc-template.html");
|
||||||
String content = Utility.read(in).replace("${content}", json);
|
String content = Utility.read(in).replace("'${content}'", json);
|
||||||
in.close();
|
in.close();
|
||||||
FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html"));
|
FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html"));
|
||||||
outhtml.write(content.getBytes("UTF-8"));
|
outhtml.write(content.getBytes("UTF-8"));
|
||||||
@@ -148,7 +205,7 @@ public class ApiDocs extends HttpBaseServlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
|
public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException {
|
||||||
return true;
|
next.execute(request, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,17 @@ import org.redkale.watch.WatchFactory;
|
|||||||
import org.w3c.dom.*;
|
import org.w3c.dom.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编译时需要加入: -XDignore.symbol.file=true
|
*
|
||||||
|
* 进程启动类,全局对象。 <br>
|
||||||
|
* <pre>
|
||||||
|
* 程序启动执行步骤:
|
||||||
|
* 1、读取application.xml
|
||||||
|
* 2、进行classpath扫描动态加载Service与Servlet
|
||||||
|
* 3、优先加载所有SNCP协议的服务,再加载其他协议服务
|
||||||
|
* 4、最后进行Service、Servlet与其他资源之间的依赖注入
|
||||||
|
* </pre>
|
||||||
* <p>
|
* <p>
|
||||||
* 进程启动类,程序启动后读取application.xml,进行classpath扫描动态加载Service与Servlet 优先加载所有SNCP协议的服务, 再加载其他协议服务, 最后进行Service、Servlet与其他资源之间的依赖注入。
|
* 编译时需要加入: -XDignore.symbol.file=true
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
@@ -40,65 +48,99 @@ import org.w3c.dom.*;
|
|||||||
*/
|
*/
|
||||||
public final class Application {
|
public final class Application {
|
||||||
|
|
||||||
//当前进程启动的时间, 类型: long
|
/**
|
||||||
|
* 当前进程启动的时间, 类型: long
|
||||||
|
*/
|
||||||
public static final String RESNAME_APP_TIME = "APP_TIME";
|
public static final String RESNAME_APP_TIME = "APP_TIME";
|
||||||
|
|
||||||
//当前进程的根目录, 类型:String、File、Path
|
/**
|
||||||
|
* 当前进程的根目录, 类型:String、File、Path
|
||||||
|
*/
|
||||||
public static final String RESNAME_APP_HOME = "APP_HOME";
|
public static final String RESNAME_APP_HOME = "APP_HOME";
|
||||||
|
|
||||||
//application.xml 文件中resources节点的内容, 类型: AnyValue
|
/**
|
||||||
|
* application.xml 文件中resources节点的内容, 类型: AnyValue
|
||||||
|
*/
|
||||||
public static final String RESNAME_APP_GRES = "APP_GRES";
|
public static final String RESNAME_APP_GRES = "APP_GRES";
|
||||||
|
|
||||||
//当前进程节点的name, 类型:String
|
/**
|
||||||
|
* 当前进程节点的name, 类型:String
|
||||||
|
*/
|
||||||
public static final String RESNAME_APP_NODE = "APP_NODE";
|
public static final String RESNAME_APP_NODE = "APP_NODE";
|
||||||
|
|
||||||
//当前进程节点的IP地址, 类型:InetAddress、String
|
/**
|
||||||
|
* 当前进程节点的IP地址, 类型:InetAddress、String
|
||||||
|
*/
|
||||||
public static final String RESNAME_APP_ADDR = "APP_ADDR";
|
public static final String RESNAME_APP_ADDR = "APP_ADDR";
|
||||||
|
|
||||||
//当前Service的IP地址+端口 类型: SocketAddress、InetSocketAddress、String
|
/**
|
||||||
|
* 当前Service的IP地址+端口 类型: SocketAddress、InetSocketAddress、String
|
||||||
|
*/
|
||||||
public static final String RESNAME_SERVER_ADDR = "SERVER_ADDR";
|
public static final String RESNAME_SERVER_ADDR = "SERVER_ADDR";
|
||||||
|
|
||||||
//当前SNCP Server所属的组 类型: String
|
/**
|
||||||
|
* 当前SNCP Server所属的组 类型: String
|
||||||
|
*/
|
||||||
public static final String RESNAME_SERVER_GROUP = "SERVER_GROUP";
|
public static final String RESNAME_SERVER_GROUP = "SERVER_GROUP";
|
||||||
|
|
||||||
//当前Server的ROOT目录 类型:String、File、Path
|
/**
|
||||||
|
* 当前Server的ROOT目录 类型:String、File、Path
|
||||||
|
*/
|
||||||
public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
|
public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
|
||||||
|
|
||||||
|
//每个地址对应的Group名
|
||||||
final Map<InetSocketAddress, String> globalNodes = new HashMap<>();
|
final Map<InetSocketAddress, String> globalNodes = new HashMap<>();
|
||||||
|
|
||||||
|
//协议地址的Group集合
|
||||||
final Map<String, GroupInfo> globalGroups = new HashMap<>();
|
final Map<String, GroupInfo> globalGroups = new HashMap<>();
|
||||||
|
|
||||||
|
//本地IP地址
|
||||||
final InetAddress localAddress;
|
final InetAddress localAddress;
|
||||||
|
|
||||||
|
//CacheSource 资源
|
||||||
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
|
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
//DataSource 资源
|
||||||
final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
|
final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
//NodeServer 资源
|
||||||
final List<NodeServer> servers = new CopyOnWriteArrayList<>();
|
final List<NodeServer> servers = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
//传输端的ByteBuffer对象池
|
||||||
final ObjectPool<ByteBuffer> transportBufferPool;
|
final ObjectPool<ByteBuffer> transportBufferPool;
|
||||||
|
|
||||||
|
//传输端的线程池
|
||||||
final ExecutorService transportExecutor;
|
final ExecutorService transportExecutor;
|
||||||
|
|
||||||
|
//传输端的ChannelGroup
|
||||||
final AsynchronousChannelGroup transportChannelGroup;
|
final AsynchronousChannelGroup transportChannelGroup;
|
||||||
|
|
||||||
|
//全局根ResourceFactory
|
||||||
final ResourceFactory resourceFactory = ResourceFactory.root();
|
final ResourceFactory resourceFactory = ResourceFactory.root();
|
||||||
|
|
||||||
|
//服务配置项
|
||||||
|
final AnyValue config;
|
||||||
|
|
||||||
|
//临时计数器
|
||||||
CountDownLatch servicecdl; //会出现两次赋值
|
CountDownLatch servicecdl; //会出现两次赋值
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------
|
||||||
|
//是否用于main方法运行
|
||||||
private final boolean singletonrun;
|
private final boolean singletonrun;
|
||||||
|
|
||||||
|
//根WatchFactory
|
||||||
private final WatchFactory watchFactory = WatchFactory.root();
|
private final WatchFactory watchFactory = WatchFactory.root();
|
||||||
|
|
||||||
|
//进程根目录
|
||||||
private final File home;
|
private final File home;
|
||||||
|
|
||||||
|
//日志
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
|
|
||||||
private final AnyValue config;
|
//服务启动时间
|
||||||
|
|
||||||
private final long startTime = System.currentTimeMillis();
|
private final long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
//Server启动的计数器,用于确保所有Server都启动完后再进行下一步处理
|
||||||
private final CountDownLatch serversLatch;
|
private final CountDownLatch serversLatch;
|
||||||
|
|
||||||
private Application(final AnyValue config) {
|
private Application(final AnyValue config) {
|
||||||
@@ -164,7 +206,8 @@ public final class Application {
|
|||||||
Properties prop = new Properties();
|
Properties prop = new Properties();
|
||||||
final String handlers = properties.getProperty("handlers");
|
final String handlers = properties.getProperty("handlers");
|
||||||
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
|
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
|
||||||
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", fileHandlerClass));
|
//singletonrun模式下不输出文件日志
|
||||||
|
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", singletonrun ? "" : fileHandlerClass));
|
||||||
}
|
}
|
||||||
if (!prop.isEmpty()) {
|
if (!prop.isEmpty()) {
|
||||||
String prefix = fileHandlerClass + ".";
|
String prefix = fileHandlerClass + ".";
|
||||||
@@ -189,6 +232,7 @@ public final class Application {
|
|||||||
}
|
}
|
||||||
this.logger = Logger.getLogger(this.getClass().getSimpleName());
|
this.logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||||
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
|
this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
|
||||||
|
logger.log(Level.INFO, "------------------------------- Redkale -------------------------------");
|
||||||
//------------------配置 <transport> 节点 ------------------
|
//------------------配置 <transport> 节点 ------------------
|
||||||
ObjectPool<ByteBuffer> transportPool = null;
|
ObjectPool<ByteBuffer> transportPool = null;
|
||||||
ExecutorService transportExec = null;
|
ExecutorService transportExec = null;
|
||||||
@@ -263,7 +307,7 @@ public final class Application {
|
|||||||
|
|
||||||
File persist = new File(this.home, "conf/persistence.xml");
|
File persist = new File(this.home, "conf/persistence.xml");
|
||||||
final String homepath = this.home.getCanonicalPath();
|
final String homepath = this.home.getCanonicalPath();
|
||||||
if (persist.isFile()) System.setProperty(DataDefaultSource.DATASOURCE_CONFPATH, persist.getCanonicalPath());
|
if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath());
|
||||||
logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
|
logger.log(Level.INFO, RESNAME_APP_HOME + "= " + homepath + "\r\n" + RESNAME_APP_ADDR + "= " + this.localAddress.getHostAddress());
|
||||||
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
|
String lib = config.getValue("lib", "").trim().replace("${APP_HOME}", homepath);
|
||||||
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
|
lib = lib.isEmpty() ? (homepath + "/conf") : (lib + ";" + homepath + "/conf");
|
||||||
@@ -294,6 +338,7 @@ public final class Application {
|
|||||||
String name = prop.getValue("name");
|
String name = prop.getValue("name");
|
||||||
String value = prop.getValue("value");
|
String value = prop.getValue("value");
|
||||||
if (name == null || value == null) continue;
|
if (name == null || value == null) continue;
|
||||||
|
value = value.replace("${APP_HOME}", home.getCanonicalPath()).replace('\\', '/');
|
||||||
if (name.startsWith("system.property.")) {
|
if (name.startsWith("system.property.")) {
|
||||||
System.setProperty(name.substring("system.property.".length()), value);
|
System.setProperty(name.substring("system.property.".length()), value);
|
||||||
} else if (name.startsWith("mimetype.property.")) {
|
} else if (name.startsWith("mimetype.property.")) {
|
||||||
@@ -306,11 +351,6 @@ public final class Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.localAddress != null && this.resourceFactory.find("property.datasource.nodeid", String.class) == null) {
|
|
||||||
byte[] bs = this.localAddress.getAddress();
|
|
||||||
int v = (0xff & bs[bs.length - 2]) % 10 * 100 + (0xff & bs[bs.length - 1]);
|
|
||||||
this.resourceFactory.register("property.datasource.nodeid", "" + v);
|
|
||||||
}
|
|
||||||
this.resourceFactory.register(BsonFactory.root());
|
this.resourceFactory.register(BsonFactory.root());
|
||||||
this.resourceFactory.register(JsonFactory.root());
|
this.resourceFactory.register(JsonFactory.root());
|
||||||
this.resourceFactory.register(BsonFactory.root().getConvert());
|
this.resourceFactory.register(BsonFactory.root().getConvert());
|
||||||
@@ -332,7 +372,7 @@ public final class Application {
|
|||||||
}
|
}
|
||||||
GroupInfo ginfo = globalGroups.get(group);
|
GroupInfo ginfo = globalGroups.get(group);
|
||||||
if (ginfo == null) {
|
if (ginfo == null) {
|
||||||
ginfo = new GroupInfo(group, protocol, conf.getValue("kind", ""), new LinkedHashSet<>());
|
ginfo = new GroupInfo(group, protocol, conf.getValue("subprotocol", ""), new LinkedHashSet<>());
|
||||||
globalGroups.put(group, ginfo);
|
globalGroups.put(group, ginfo);
|
||||||
}
|
}
|
||||||
for (AnyValue node : conf.getAnyValues("node")) {
|
for (AnyValue node : conf.getAnyValues("node")) {
|
||||||
@@ -422,6 +462,7 @@ public final class Application {
|
|||||||
channel.write(buffer);
|
channel.write(buffer);
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
channel.configureBlocking(false);
|
channel.configureBlocking(false);
|
||||||
|
try {
|
||||||
channel.read(buffer);
|
channel.read(buffer);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
byte[] bytes = new byte[buffer.remaining()];
|
byte[] bytes = new byte[buffer.remaining()];
|
||||||
@@ -429,6 +470,19 @@ public final class Application {
|
|||||||
channel.close();
|
channel.close();
|
||||||
logger.info(new String(bytes));
|
logger.info(new String(bytes));
|
||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (e instanceof PortUnreachableException) {
|
||||||
|
if ("APIDOC".equalsIgnoreCase(command)) {
|
||||||
|
final Application application = Application.create(true);
|
||||||
|
application.init();
|
||||||
|
application.start();
|
||||||
|
new ApiDocs(application).run();
|
||||||
|
logger.info("APIDOC OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
@@ -462,7 +516,7 @@ public final class Application {
|
|||||||
for (final AnyValue serconf : serconfs) {
|
for (final AnyValue serconf : serconfs) {
|
||||||
Thread thread = new Thread() {
|
Thread thread = new Thread() {
|
||||||
{
|
{
|
||||||
String host = serconf.getValue("host", "").replace("0.0.0.0", "[0]");
|
String host = serconf.getValue("host", "0.0.0.0").replace("0.0.0.0", "*");
|
||||||
setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
|
setName(serconf.getValue("protocol", "Server").toUpperCase() + "-" + host + ":" + serconf.getIntValue("port") + "-Thread");
|
||||||
this.setDaemon(true);
|
this.setDaemon(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ import org.redkale.util.AnyValue.DefaultAnyValue;
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final class ClassFilter<T> {
|
public final class ClassFilter<T> {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ClassFilter.class.getName());
|
private static final Logger logger = Logger.getLogger(ClassFilter.class.getName()); //日志对象
|
||||||
|
|
||||||
private static final boolean finer = logger.isLoggable(Level.FINER);
|
private static final boolean finer = logger.isLoggable(Level.FINER); //日志级别
|
||||||
|
|
||||||
private final Set<FilterEntry<T>> entrys = new HashSet<>(); //符合条件的结果
|
private final Set<FilterEntry<T>> entrys = new HashSet<>(); //符合条件的结果
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ package org.redkale.boot;
|
|||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import org.redkale.convert.json.JsonConvert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 协议地址组合对象, 对应application.xml 中 resources->group 节点信息
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -17,21 +19,21 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class GroupInfo {
|
public class GroupInfo {
|
||||||
|
|
||||||
protected String name;
|
protected String name; //地址
|
||||||
|
|
||||||
protected String protocol;
|
protected String protocol; //协议 取值范围: TCP、UDP
|
||||||
|
|
||||||
protected String kind;
|
protected String subprotocol; //子协议,预留使用
|
||||||
|
|
||||||
protected Set<InetSocketAddress> addrs;
|
protected Set<InetSocketAddress> addrs; //地址列表, 对应 resources->group->node节点信息
|
||||||
|
|
||||||
public GroupInfo() {
|
public GroupInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupInfo(String name, String protocol, String kind, Set<InetSocketAddress> addrs) {
|
public GroupInfo(String name, String protocol, String subprotocol, Set<InetSocketAddress> addrs) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
this.kind = kind;
|
this.subprotocol = subprotocol;
|
||||||
this.addrs = addrs;
|
this.addrs = addrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,12 +53,12 @@ public class GroupInfo {
|
|||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKind() {
|
public String getSubprotocol() {
|
||||||
return kind;
|
return subprotocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKind(String kind) {
|
public void setSubprotocol(String subprotocol) {
|
||||||
this.kind = kind;
|
this.subprotocol = subprotocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<InetSocketAddress> getAddrs() {
|
public Set<InetSocketAddress> getAddrs() {
|
||||||
@@ -71,4 +73,8 @@ public class GroupInfo {
|
|||||||
this.addrs = addrs;
|
this.addrs = addrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return JsonConvert.root().convertTo(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,9 +84,13 @@ public class LogFileHandler extends Handler {
|
|||||||
|
|
||||||
private String pattern;
|
private String pattern;
|
||||||
|
|
||||||
|
private String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中
|
||||||
|
|
||||||
private int limit; //文件大小限制
|
private int limit; //文件大小限制
|
||||||
|
|
||||||
private final AtomicInteger index = new AtomicInteger();
|
private final AtomicInteger logindex = new AtomicInteger();
|
||||||
|
|
||||||
|
private final AtomicInteger logunusualindex = new AtomicInteger();
|
||||||
|
|
||||||
private int count = 1; //文件限制
|
private int count = 1; //文件限制
|
||||||
|
|
||||||
@@ -94,11 +98,17 @@ public class LogFileHandler extends Handler {
|
|||||||
|
|
||||||
private boolean append;
|
private boolean append;
|
||||||
|
|
||||||
private final AtomicLong length = new AtomicLong();
|
private final AtomicLong loglength = new AtomicLong();
|
||||||
|
|
||||||
|
private final AtomicLong logunusuallength = new AtomicLong();
|
||||||
|
|
||||||
private File logfile;
|
private File logfile;
|
||||||
|
|
||||||
private OutputStream stream;
|
private File logunusualfile;
|
||||||
|
|
||||||
|
private OutputStream logstream;
|
||||||
|
|
||||||
|
private OutputStream logunusualstream;
|
||||||
|
|
||||||
public LogFileHandler() {
|
public LogFileHandler() {
|
||||||
updateTomorrow();
|
updateTomorrow();
|
||||||
@@ -114,7 +124,7 @@ public class LogFileHandler extends Handler {
|
|||||||
cal.set(Calendar.MILLISECOND, 0);
|
cal.set(Calendar.MILLISECOND, 0);
|
||||||
cal.add(Calendar.DAY_OF_YEAR, 1);
|
cal.add(Calendar.DAY_OF_YEAR, 1);
|
||||||
long t = cal.getTimeInMillis();
|
long t = cal.getTimeInMillis();
|
||||||
if (this.tomorrow != t) index.set(0);
|
if (this.tomorrow != t) logindex.set(0);
|
||||||
this.tomorrow = t;
|
this.tomorrow = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,35 +141,59 @@ public class LogFileHandler extends Handler {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
LogRecord record = records.take();
|
LogRecord record = records.take();
|
||||||
final boolean bigger = (limit > 0 && limit <= length.get());
|
final boolean bigger = (limit > 0 && limit <= loglength.get());
|
||||||
if (bigger || tomorrow <= record.getMillis()) {
|
final boolean changeday = tomorrow <= record.getMillis();
|
||||||
|
if (bigger || changeday) {
|
||||||
updateTomorrow();
|
updateTomorrow();
|
||||||
if (stream != null) {
|
if (logstream != null) {
|
||||||
stream.close();
|
logstream.close();
|
||||||
if (bigger) {
|
if (bigger) {
|
||||||
for (int i = Math.min(count - 2, index.get() - 1); i > 0; i--) {
|
for (int i = Math.min(count - 2, logindex.get() - 1); i > 0; i--) {
|
||||||
File greater = new File(logfile.getPath() + "." + i);
|
File greater = new File(logfile.getPath() + "." + i);
|
||||||
if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||||
}
|
}
|
||||||
Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||||
}
|
}
|
||||||
stream = null;
|
logstream = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stream == null) {
|
if (unusual != null && changeday && logunusualstream != null) {
|
||||||
index.incrementAndGet();
|
logunusualstream.close();
|
||||||
|
if (limit > 0 && limit <= logunusuallength.get()) {
|
||||||
|
for (int i = Math.min(count - 2, logunusualindex.get() - 1); i > 0; i--) {
|
||||||
|
File greater = new File(logunusualfile.getPath() + "." + i);
|
||||||
|
if (greater.exists()) Files.move(greater.toPath(), new File(logunusualfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||||
|
}
|
||||||
|
Files.move(logunusualfile.toPath(), new File(logunusualfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE);
|
||||||
|
}
|
||||||
|
logunusualstream = null;
|
||||||
|
}
|
||||||
|
if (logstream == null) {
|
||||||
|
logindex.incrementAndGet();
|
||||||
java.time.LocalDate date = LocalDate.now();
|
java.time.LocalDate date = LocalDate.now();
|
||||||
logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||||
logfile.getParentFile().mkdirs();
|
logfile.getParentFile().mkdirs();
|
||||||
length.set(logfile.length());
|
loglength.set(logfile.length());
|
||||||
stream = new FileOutputStream(logfile, append);
|
logstream = new FileOutputStream(logfile, append);
|
||||||
|
}
|
||||||
|
if (unusual != null && logunusualstream == null) {
|
||||||
|
logunusualindex.incrementAndGet();
|
||||||
|
java.time.LocalDate date = LocalDate.now();
|
||||||
|
logunusualfile = new File(unusual.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth()))));
|
||||||
|
logunusualfile.getParentFile().mkdirs();
|
||||||
|
logunusuallength.set(logunusualfile.length());
|
||||||
|
logunusualstream = new FileOutputStream(logunusualfile, append);
|
||||||
}
|
}
|
||||||
//----------------------写日志-------------------------
|
//----------------------写日志-------------------------
|
||||||
String message = getFormatter().format(record);
|
String message = getFormatter().format(record);
|
||||||
String encoding = getEncoding();
|
String encoding = getEncoding();
|
||||||
byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
|
byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding);
|
||||||
stream.write(bytes);
|
logstream.write(bytes);
|
||||||
length.addAndGet(bytes.length);
|
loglength.addAndGet(bytes.length);
|
||||||
|
if (unusual != null && (record.getLevel() == Level.WARNING || record.getLevel() == Level.SEVERE)) {
|
||||||
|
logunusualstream.write(bytes);
|
||||||
|
logunusuallength.addAndGet(bytes.length);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorManager err = getErrorManager();
|
ErrorManager err = getErrorManager();
|
||||||
if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
|
if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE);
|
||||||
@@ -177,30 +211,39 @@ public class LogFileHandler extends Handler {
|
|||||||
private void configure() {
|
private void configure() {
|
||||||
LogManager manager = LogManager.getLogManager();
|
LogManager manager = LogManager.getLogManager();
|
||||||
String cname = LogFileHandler.class.getName();
|
String cname = LogFileHandler.class.getName();
|
||||||
pattern = manager.getProperty(cname + ".pattern");
|
this.pattern = manager.getProperty(cname + ".pattern");
|
||||||
if (pattern == null) {
|
if (this.pattern == null) {
|
||||||
pattern = "logs-%m/" + getPrefix() + "log-%d.log";
|
this.pattern = "logs-%m/" + getPrefix() + "log-%d.log";
|
||||||
} else {
|
} else {
|
||||||
int pos = pattern.lastIndexOf('/');
|
int pos = this.pattern.lastIndexOf('/');
|
||||||
if (pos > 0) {
|
if (pos > 0) {
|
||||||
pattern = pattern.substring(0, pos + 1) + getPrefix() + pattern.substring(pos + 1);
|
this.pattern = this.pattern.substring(0, pos + 1) + getPrefix() + this.pattern.substring(pos + 1);
|
||||||
} else {
|
} else {
|
||||||
pattern = getPrefix() + pattern;
|
this.pattern = getPrefix() + this.pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String unusualstr = manager.getProperty(cname + ".unusual");
|
||||||
|
if (unusualstr != null) {
|
||||||
|
int pos = unusualstr.lastIndexOf('/');
|
||||||
|
if (pos > 0) {
|
||||||
|
this.unusual = unusualstr.substring(0, pos + 1) + getPrefix() + unusualstr.substring(pos + 1);
|
||||||
|
} else {
|
||||||
|
this.unusual = getPrefix() + unusualstr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String limitstr = manager.getProperty(cname + ".limit");
|
String limitstr = manager.getProperty(cname + ".limit");
|
||||||
try {
|
try {
|
||||||
if (limitstr != null) limit = Math.abs(Integer.decode(limitstr));
|
if (limitstr != null) this.limit = Math.abs(Integer.decode(limitstr));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
}
|
}
|
||||||
String countstr = manager.getProperty(cname + ".count");
|
String countstr = manager.getProperty(cname + ".count");
|
||||||
try {
|
try {
|
||||||
if (countstr != null) count = Math.max(1, Math.abs(Integer.decode(countstr)));
|
if (countstr != null) this.count = Math.max(1, Math.abs(Integer.decode(countstr)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
}
|
}
|
||||||
String appendstr = manager.getProperty(cname + ".append");
|
String appendstr = manager.getProperty(cname + ".append");
|
||||||
try {
|
try {
|
||||||
if (appendstr != null) append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr);
|
if (appendstr != null) this.append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
}
|
}
|
||||||
String levelstr = manager.getProperty(cname + ".level");
|
String levelstr = manager.getProperty(cname + ".level");
|
||||||
@@ -256,7 +299,7 @@ public class LogFileHandler extends Handler {
|
|||||||
@Override
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
try {
|
try {
|
||||||
if (stream != null) stream.flush();
|
if (logstream != null) logstream.flush();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorManager err = getErrorManager();
|
ErrorManager err = getErrorManager();
|
||||||
if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE);
|
if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE);
|
||||||
@@ -266,7 +309,7 @@ public class LogFileHandler extends Handler {
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws SecurityException {
|
public void close() throws SecurityException {
|
||||||
try {
|
try {
|
||||||
if (stream != null) stream.close();
|
if (logstream != null) logstream.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ErrorManager err = getErrorManager();
|
ErrorManager err = getErrorManager();
|
||||||
if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE);
|
if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import org.redkale.util.*;
|
|||||||
@NodeProtocol({"HTTP"})
|
@NodeProtocol({"HTTP"})
|
||||||
public class NodeHttpServer extends NodeServer {
|
public class NodeHttpServer extends NodeServer {
|
||||||
|
|
||||||
protected final boolean rest;
|
protected final boolean rest; //是否加载REST服务, 为true加载rest节点信息并将所有可REST化的Service生成RestHttpServlet
|
||||||
|
|
||||||
protected final HttpServer httpServer;
|
protected final HttpServer httpServer;
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ public class NodeHttpServer extends NodeServer {
|
|||||||
synchronized (regFactory) {
|
synchronized (regFactory) {
|
||||||
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class);
|
||||||
if (nodeService == null) {
|
if (nodeService == null) {
|
||||||
nodeService = Sncp.createLocalService(resourceName, getExecutor(), application.getResourceFactory(), WebSocketNodeService.class, (InetSocketAddress) null, (Transport) null, (Collection<Transport>) null);
|
nodeService = Sncp.createLocalService(resourceName, getExecutor(), application.getResourceFactory(), WebSocketNodeService.class, (InetSocketAddress) null, (String) null, (Set<String>) null, (AnyValue) null, (Transport) null, (Collection<Transport>) null);
|
||||||
regFactory.register(resourceName, WebSocketNode.class, nodeService);
|
regFactory.register(resourceName, WebSocketNode.class, nodeService);
|
||||||
resourceFactory.inject(nodeService, self);
|
resourceFactory.inject(nodeService, self);
|
||||||
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
|
logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService);
|
||||||
@@ -109,13 +109,6 @@ public class NodeHttpServer extends NodeServer {
|
|||||||
final String[] mappings = ws.value();
|
final String[] mappings = ws.value();
|
||||||
String pref = ws.repair() ? prefix : "";
|
String pref = ws.repair() ? prefix : "";
|
||||||
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
|
DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty();
|
||||||
WebInitParam[] webparams = ws.initParams();
|
|
||||||
if (webparams.length > 0) {
|
|
||||||
if (servletConf == null) servletConf = new DefaultAnyValue();
|
|
||||||
for (WebInitParam webparam : webparams) {
|
|
||||||
servletConf.addValue(webparam.name(), webparam.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
|
this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings);
|
||||||
if (ss != null) {
|
if (ss != null) {
|
||||||
for (int i = 0; i < mappings.length; i++) {
|
for (int i = 0; i < mappings.length; i++) {
|
||||||
@@ -124,32 +117,31 @@ public class NodeHttpServer extends NodeServer {
|
|||||||
ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings));
|
ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int max = 0;
|
||||||
if (ss != null && sb != null) {
|
if (ss != null && sb != null) {
|
||||||
Collections.sort(ss, (AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
Collections.sort(ss, (AbstractMap.SimpleEntry<String, String[]> o1, AbstractMap.SimpleEntry<String, String[]> o2) -> o1.getKey().compareTo(o2.getKey()));
|
||||||
int max = 0;
|
|
||||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||||
if (as.getKey().length() > max) max = as.getKey().length();
|
if (as.getKey().length() > max) max = as.getKey().length();
|
||||||
}
|
}
|
||||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||||
sb.append(threadName).append(" Loaded ").append(as.getKey());
|
sb.append(threadName).append(" Load ").append(as.getKey());
|
||||||
for (int i = 0; i < max - as.getKey().length(); i++) {
|
for (int i = 0; i < max - as.getKey().length(); i++) {
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
}
|
}
|
||||||
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
|
||||||
if (rest && serverConf != null) {
|
if (rest && serverConf != null) {
|
||||||
for (AnyValue restConf : serverConf.getAnyValues("rest")) {
|
for (AnyValue restConf : serverConf.getAnyValues("rest")) {
|
||||||
loadRestServlet(prefix, restConf);
|
loadRestServlet(prefix, restConf, sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loadRestServlet(final String prefix, final AnyValue restConf) throws Exception {
|
protected void loadRestServlet(final String prefix, final AnyValue restConf, final StringBuilder sb) throws Exception {
|
||||||
if (!rest) return;
|
if (!rest) return;
|
||||||
if (restConf == null) return; //不存在REST服务
|
if (restConf == null) return; //不存在REST服务
|
||||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
|
||||||
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
final String threadName = "[" + Thread.currentThread().getName() + "] ";
|
||||||
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
|
final List<AbstractMap.SimpleEntry<String, String[]>> ss = sb == null ? null : new ArrayList<>();
|
||||||
|
|
||||||
@@ -170,8 +162,9 @@ public class NodeHttpServer extends NodeServer {
|
|||||||
|
|
||||||
final ClassFilter restFilter = ClassFilter.create(restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
|
final ClassFilter restFilter = ClassFilter.create(restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues);
|
||||||
|
|
||||||
super.interceptorServiceWrappers.forEach((wrapper) -> {
|
super.interceptorServices.forEach((service) -> {
|
||||||
final Class stype = wrapper.getType();
|
final Class stype = Sncp.getServiceType(service);
|
||||||
|
final String name = Sncp.getResourceName(service);
|
||||||
RestService rs = (RestService) stype.getAnnotation(RestService.class);
|
RestService rs = (RestService) stype.getAnnotation(RestService.class);
|
||||||
if (rs != null && rs.ignore()) return;
|
if (rs != null && rs.ignore()) return;
|
||||||
if (mustsign && rs == null) return;
|
if (mustsign && rs == null) return;
|
||||||
@@ -181,18 +174,16 @@ public class NodeHttpServer extends NodeServer {
|
|||||||
if (!autoload && !includeValues.contains(stypename)) return;
|
if (!autoload && !includeValues.contains(stypename)) return;
|
||||||
if (!restFilter.accept(stypename)) return;
|
if (!restFilter.accept(stypename)) return;
|
||||||
|
|
||||||
RestHttpServlet servlet = httpServer.addRestServlet(wrapper.getName(), stype, wrapper.getService(), baseServletClass, prefix, (AnyValue) null);
|
RestHttpServlet servlet = httpServer.addRestServlet(name, stype, service, baseServletClass, prefix, (AnyValue) null);
|
||||||
resourceFactory.inject(servlet, NodeHttpServer.this);
|
resourceFactory.inject(servlet, NodeHttpServer.this);
|
||||||
if (finest) logger.finest("Create RestServlet[resource=" + wrapper.getName() + "] = " + servlet);
|
if (finest) logger.finest(threadName + " Create RestServlet(resource.name='" + name + "') = " + servlet);
|
||||||
if (ss != null) {
|
if (ss != null) {
|
||||||
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value();
|
||||||
for (int i = 0; i < mappings.length; i++) {
|
for (int i = 0; i < mappings.length; i++) {
|
||||||
mappings[i] = prefix + mappings[i];
|
mappings[i] = prefix + mappings[i];
|
||||||
}
|
}
|
||||||
if (servlet.getClass().getSimpleName().charAt(0) != '_') {
|
|
||||||
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
|
ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
//输出信息
|
//输出信息
|
||||||
if (ss != null && sb != null) {
|
if (ss != null && sb != null) {
|
||||||
@@ -201,14 +192,14 @@ public class NodeHttpServer extends NodeServer {
|
|||||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||||
if (as.getKey().length() > max) max = as.getKey().length();
|
if (as.getKey().length() > max) max = as.getKey().length();
|
||||||
}
|
}
|
||||||
|
sb.append(threadName).append(" ").append(LINE_SEPARATOR);
|
||||||
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
for (AbstractMap.SimpleEntry<String, String[]> as : ss) {
|
||||||
sb.append(threadName).append(" Loaded ").append(as.getKey());
|
sb.append(threadName).append(" Load ").append(as.getKey());
|
||||||
for (int i = 0; i < max - as.getKey().length(); i++) {
|
for (int i = 0; i < max - as.getKey().length(); i++) {
|
||||||
sb.append(' ');
|
sb.append(' ');
|
||||||
}
|
}
|
||||||
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.redkale.boot;
|
package org.redkale.boot;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import org.redkale.service.Service;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* NodeServer的拦截类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -17,71 +15,24 @@ import org.redkale.service.Service;
|
|||||||
*/
|
*/
|
||||||
public class NodeInterceptor {
|
public class NodeInterceptor {
|
||||||
|
|
||||||
|
/** *
|
||||||
|
* Server.start之前调用 <br>
|
||||||
|
* NodeServer.start的部署是先执行NodeInterceptor.preStart,再执行 Server.start 方法
|
||||||
|
*
|
||||||
|
* @param server NodeServer
|
||||||
|
*/
|
||||||
public void preStart(NodeServer server) {
|
public void preStart(NodeServer server) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server.shutdown之前调用 <br>
|
||||||
|
* NodeServer.shutdown的部署是先执行NodeInterceptor.preShutdown,再执行 Server.sshutdown 方法
|
||||||
|
*
|
||||||
|
* @param server NodeServer
|
||||||
|
*/
|
||||||
public void preShutdown(NodeServer server) {
|
public void preShutdown(NodeServer server) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InterceptorServiceWrapper<T extends Service> {
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private Class<T> type;
|
|
||||||
|
|
||||||
private T service;
|
|
||||||
|
|
||||||
public InterceptorServiceWrapper() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public InterceptorServiceWrapper(String name, Class<T> type, T service) {
|
|
||||||
this.name = name;
|
|
||||||
this.type = type;
|
|
||||||
this.service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<T> getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(Class<T> type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getService() {
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setService(T service) {
|
|
||||||
this.service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int hash = 7;
|
|
||||||
hash = 97 * hash + Objects.hashCode(this.name);
|
|
||||||
hash = 97 * hash + Objects.hashCode(this.type);
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) return true;
|
|
||||||
if (obj == null) return false;
|
|
||||||
if (getClass() != obj.getClass()) return false;
|
|
||||||
final InterceptorServiceWrapper<?> other = (InterceptorServiceWrapper<?>) obj;
|
|
||||||
return Objects.equals(this.name, other.name) && Objects.equals(this.type, other.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package org.redkale.boot;
|
|||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据application.xml中的server节点中的protocol值来适配Server的加载逻辑
|
* 根据application.xml中的server节点中的protocol值来适配Server的加载逻辑, 只能注解在NodeServer子类上
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public abstract class NodeServer {
|
|||||||
private InetSocketAddress sncpAddress;
|
private InetSocketAddress sncpAddress;
|
||||||
|
|
||||||
//加载Service时的处理函数
|
//加载Service时的处理函数
|
||||||
protected Consumer<ServiceWrapper> consumer;
|
protected Consumer<Service> consumer;
|
||||||
|
|
||||||
//server节点的配置
|
//server节点的配置
|
||||||
protected AnyValue serverConf;
|
protected AnyValue serverConf;
|
||||||
@@ -78,13 +78,17 @@ public abstract class NodeServer {
|
|||||||
protected NodeInterceptor interceptor;
|
protected NodeInterceptor interceptor;
|
||||||
|
|
||||||
//供interceptor使用的Service对象集合
|
//供interceptor使用的Service对象集合
|
||||||
protected final Set<NodeInterceptor.InterceptorServiceWrapper> interceptorServiceWrappers = new LinkedHashSet<>();
|
protected final Set<Service> interceptorServices = new LinkedHashSet<>();
|
||||||
|
|
||||||
//本地模式的Service对象集合
|
//本地模式的Service对象集合
|
||||||
protected final Set<ServiceWrapper> localServiceWrappers = new LinkedHashSet<>();
|
protected final Set<Service> localServices = new LinkedHashSet<>();
|
||||||
|
|
||||||
//远程模式的Service对象集合
|
//远程模式的Service对象集合
|
||||||
protected final Set<ServiceWrapper> remoteServiceWrappers = new LinkedHashSet<>();
|
protected final Set<Service> remoteServices = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
private volatile int maxClassNameLength = 0;
|
||||||
|
|
||||||
|
private volatile int maxNameLength = 0;
|
||||||
|
|
||||||
public NodeServer(Application application, Server server) {
|
public NodeServer(Application application, Server server) {
|
||||||
this.application = application;
|
this.application = application;
|
||||||
@@ -116,7 +120,7 @@ public abstract class NodeServer {
|
|||||||
if (context == null) {
|
if (context == null) {
|
||||||
t.run();
|
t.run();
|
||||||
} else {
|
} else {
|
||||||
context.submit(t);
|
context.submitAsync(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,11 +195,36 @@ public abstract class NodeServer {
|
|||||||
final NodeServer self = this;
|
final NodeServer self = this;
|
||||||
//---------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------
|
||||||
final ResourceFactory appResFactory = application.getResourceFactory();
|
final ResourceFactory appResFactory = application.getResourceFactory();
|
||||||
|
//------------------------------------- 注册Resource --------------------------------------------------------
|
||||||
|
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
|
||||||
|
try {
|
||||||
|
Resource res = field.getAnnotation(Resource.class);
|
||||||
|
if (res == null || !res.name().startsWith("properties.")) return;
|
||||||
|
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
||||||
|
Class type = field.getType();
|
||||||
|
if (type != AnyValue.class && type != AnyValue[].class) return;
|
||||||
|
Object resource = null;
|
||||||
|
final AnyValue resources = application.config.getAnyValue("resources");
|
||||||
|
final AnyValue properties = resources == null ? null : resources.getAnyValue("properties");
|
||||||
|
if (properties != null && type == AnyValue.class) {
|
||||||
|
resource = properties.getAnyValue(res.name().substring("properties.".length()));
|
||||||
|
appResFactory.register(resourceName, AnyValue.class, resource);
|
||||||
|
} else if (properties != null && type == AnyValue[].class) {
|
||||||
|
resource = properties.getAnyValues(res.name().substring("properties.".length()));
|
||||||
|
appResFactory.register(resourceName, AnyValue[].class, resource);
|
||||||
|
}
|
||||||
|
field.set(src, resource);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.log(Level.SEVERE, "Resource inject error", e);
|
||||||
|
}
|
||||||
|
}, AnyValue.class, AnyValue[].class);
|
||||||
|
|
||||||
|
//------------------------------------- 注册DataSource --------------------------------------------------------
|
||||||
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
|
resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> {
|
||||||
try {
|
try {
|
||||||
if (field.getAnnotation(Resource.class) == null) return;
|
if (field.getAnnotation(Resource.class) == null) return;
|
||||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource
|
||||||
DataSource source = new DataDefaultSource(resourceName);
|
DataSource source = DataSources.createDataSource(resourceName);
|
||||||
application.dataSources.add(source);
|
application.dataSources.add(source);
|
||||||
appResFactory.register(resourceName, DataSource.class, source);
|
appResFactory.register(resourceName, DataSource.class, source);
|
||||||
|
|
||||||
@@ -204,33 +233,34 @@ public abstract class NodeServer {
|
|||||||
List<Transport> diffGroupTransports = Arrays.asList(Sncp.getDiffGroupTransports((Service) src));
|
List<Transport> diffGroupTransports = Arrays.asList(Sncp.getDiffGroupTransports((Service) src));
|
||||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||||
if ((src instanceof DataSource) && sncpAddr != null && resourceFactory.find(resourceName, DataCacheListener.class) == null) { //只有DataSourceService 才能赋值 DataCacheListener
|
if ((src instanceof DataSource) && sncpAddr != null && resourceFactory.find(resourceName, DataCacheListener.class) == null) { //只有DataSourceService 才能赋值 DataCacheListener
|
||||||
Service cacheListenerService = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, DataCacheListenerService.class, sncpAddr, sameGroupTransport, diffGroupTransports);
|
|
||||||
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
|
|
||||||
final NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
|
final NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
|
||||||
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports);
|
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports);
|
||||||
ServiceWrapper wrapper = new ServiceWrapper(DataCacheListenerService.class, cacheListenerService, resourceName, sncpServer.getSncpGroup(), gs, null);
|
Service cacheListenerService = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, DataCacheListenerService.class, sncpAddr, sncpServer.getSncpGroup(), gs, Sncp.getConf((Service) src), sameGroupTransport, diffGroupTransports);
|
||||||
localServiceWrappers.add(wrapper);
|
appResFactory.register(resourceName, DataCacheListener.class, cacheListenerService);
|
||||||
sncpServer.consumerAccept(wrapper);
|
localServices.add(cacheListenerService);
|
||||||
|
sncpServer.consumerAccept(cacheListenerService);
|
||||||
rf.inject(cacheListenerService, self);
|
rf.inject(cacheListenerService, self);
|
||||||
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
|
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + cacheListenerService);
|
||||||
}
|
}
|
||||||
field.set(src, source);
|
field.set(src, source);
|
||||||
rf.inject(source, self); // 给 "datasource.nodeid" 赋值;
|
rf.inject(source, self); // 给其可能包含@Resource的字段赋值;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, "DataSource inject error", e);
|
logger.log(Level.SEVERE, "DataSource inject error", e);
|
||||||
}
|
}
|
||||||
}, DataSource.class);
|
}, DataSource.class);
|
||||||
|
|
||||||
|
//------------------------------------- 注册CacheSource --------------------------------------------------------
|
||||||
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
|
resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
|
||||||
try {
|
try {
|
||||||
if (field.getAnnotation(Resource.class) == null) return;
|
if (field.getAnnotation(Resource.class) == null) return;
|
||||||
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 CacheSource
|
if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource
|
||||||
|
final Service srcService = (Service) src;
|
||||||
SncpClient client = Sncp.getSncpClient((Service) src);
|
SncpClient client = Sncp.getSncpClient(srcService);
|
||||||
Transport sameGroupTransport = Sncp.getSameGroupTransport((Service) src);
|
Transport sameGroupTransport = Sncp.getSameGroupTransport(srcService);
|
||||||
Transport[] dts = Sncp.getDiffGroupTransports((Service) src);
|
Transport[] dts = Sncp.getDiffGroupTransports((Service) src);
|
||||||
List<Transport> diffGroupTransports = dts == null ? new ArrayList<>() : Arrays.asList(dts);
|
List<Transport> diffGroupTransports = dts == null ? new ArrayList<>() : Arrays.asList(dts);
|
||||||
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress();
|
||||||
final CacheSourceService source = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, CacheSourceService.class, sncpAddr, sameGroupTransport, diffGroupTransports);
|
final CacheMemorySource source = Sncp.createLocalService(resourceName, getExecutor(), appResFactory, CacheMemorySource.class, sncpAddr, Sncp.getSncpGroup(srcService), Sncp.getGroups(srcService), Sncp.getConf(srcService), sameGroupTransport, diffGroupTransports);
|
||||||
Type genericType = field.getGenericType();
|
Type genericType = field.getGenericType();
|
||||||
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
|
ParameterizedType pt = (genericType instanceof ParameterizedType) ? (ParameterizedType) genericType : null;
|
||||||
Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
|
Type valType = pt == null ? null : pt.getActualTypeArguments()[1];
|
||||||
@@ -243,12 +273,11 @@ public abstract class NodeServer {
|
|||||||
rf.inject(source, self); //
|
rf.inject(source, self); //
|
||||||
((Service) source).init(null);
|
((Service) source).init(null);
|
||||||
|
|
||||||
if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheSourceService
|
if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource
|
||||||
NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
|
NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr);
|
||||||
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports);
|
Set<String> gs = application.findSncpGroups(sameGroupTransport, diffGroupTransports);
|
||||||
ServiceWrapper wrapper = new ServiceWrapper(CacheSourceService.class, (Service) source, resourceName, sncpServer.getSncpGroup(), gs, null);
|
sncpServer.getSncpServer().addSncpServlet((Service) source);
|
||||||
sncpServer.getSncpServer().addSncpServlet(wrapper);
|
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source);
|
||||||
logger.info("[" + Thread.currentThread().getName() + "] Load Service " + wrapper.getService());
|
|
||||||
}
|
}
|
||||||
logger.info("[" + Thread.currentThread().getName() + "] Load Source " + source);
|
logger.info("[" + Thread.currentThread().getName() + "] Load Source " + source);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -265,55 +294,55 @@ public abstract class NodeServer {
|
|||||||
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
|
ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory;
|
||||||
|
|
||||||
for (FilterEntry<Service> entry : entrys) { //service实现类
|
for (FilterEntry<Service> entry : entrys) { //service实现类
|
||||||
final Class<? extends Service> type = entry.getType();
|
final Class<? extends Service> serviceImplClass = entry.getType();
|
||||||
if (Modifier.isFinal(type.getModifiers())) continue; //修饰final的类跳过
|
if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过
|
||||||
if (!Modifier.isPublic(type.getModifiers())) continue;
|
if (!Modifier.isPublic(serviceImplClass.getModifiers())) continue;
|
||||||
if (entry.isExpect()) {
|
if (entry.isExpect()) {
|
||||||
if (Modifier.isAbstract(type.getModifiers())) continue; //修饰abstract的类跳过
|
if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过
|
||||||
if (DataSource.class.isAssignableFrom(type)) continue;
|
if (DataSource.class.isAssignableFrom(serviceImplClass)) continue;
|
||||||
if (CacheSource.class.isAssignableFrom(type)) continue;
|
if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue;
|
||||||
if (DataCacheListener.class.isAssignableFrom(type)) continue;
|
if (DataCacheListener.class.isAssignableFrom(serviceImplClass)) continue;
|
||||||
if (WebSocketNode.class.isAssignableFrom(type)) continue;
|
if (WebSocketNode.class.isAssignableFrom(serviceImplClass)) continue;
|
||||||
}
|
}
|
||||||
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
|
if (entry.getName().contains("$")) throw new RuntimeException("<name> value cannot contains '$' in " + entry.getProperty());
|
||||||
if (resourceFactory.find(entry.getName(), type) != null) { //Server加载Service时需要判断是否已经加载过了。
|
Service oldother = resourceFactory.find(entry.getName(), serviceImplClass);
|
||||||
Service oldother = resourceFactory.find(entry.getName(), type);
|
if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。
|
||||||
interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, oldother));
|
interceptorServices.add(oldother);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final HashSet<String> groups = entry.getGroups(); //groups.isEmpty()表示<services>没有配置groups属性。
|
final HashSet<String> groups = entry.getGroups(); //groups.isEmpty()表示<services>没有配置groups属性。
|
||||||
if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup);
|
if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup);
|
||||||
|
|
||||||
final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) //非SNCP的Server,通常是单点服务
|
final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !serviceImplClass.isInterface() && !Modifier.isAbstract(serviceImplClass.getModifiers())) //非SNCP的Server,通常是单点服务
|
||||||
|| groups.contains(this.sncpGroup) //本地IP含在内的
|
|| groups.contains(this.sncpGroup) //本地IP含在内的
|
||||||
|| (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置
|
|| (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置
|
||||||
|| type.getAnnotation(LocalService.class) != null;//本地模式
|
|| serviceImplClass.getAnnotation(LocalService.class) != null;//本地模式
|
||||||
if (localed && (type.isInterface() || Modifier.isAbstract(type.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类
|
if (localed && (serviceImplClass.isInterface() || Modifier.isAbstract(serviceImplClass.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类
|
||||||
final BiConsumer<ResourceFactory, Boolean> runner = (ResourceFactory rf, Boolean needinject) -> {
|
final BiConsumer<ResourceFactory, Boolean> runner = (ResourceFactory rf, Boolean needinject) -> {
|
||||||
try {
|
try {
|
||||||
Service service;
|
Service service;
|
||||||
if (localed) { //本地模式
|
if (localed) { //本地模式
|
||||||
service = Sncp.createLocalService(entry.getName(), getExecutor(), application.getResourceFactory(), type,
|
service = Sncp.createLocalService(entry.getName(), getExecutor(), application.getResourceFactory(), serviceImplClass,
|
||||||
NodeServer.this.sncpAddress, loadTransport(NodeServer.this.sncpGroup), loadTransports(groups));
|
NodeServer.this.sncpAddress, NodeServer.this.sncpGroup, groups, entry.getProperty(), loadTransport(NodeServer.this.sncpGroup), loadTransports(groups));
|
||||||
} else {
|
} else {
|
||||||
service = Sncp.createRemoteService(entry.getName(), getExecutor(), type, NodeServer.this.sncpAddress, loadTransport(groups));
|
service = Sncp.createRemoteService(entry.getName(), getExecutor(), serviceImplClass, NodeServer.this.sncpAddress, null, groups, entry.getProperty(), loadTransport(groups));
|
||||||
}
|
}
|
||||||
if (SncpClient.parseMethod(type).isEmpty()) return; //class没有可用的方法, 通常为BaseService
|
if (SncpClient.parseMethod(serviceImplClass).isEmpty()) return; //class没有可用的方法, 通常为BaseService
|
||||||
final ServiceWrapper wrapper = new ServiceWrapper(type, service, entry.getName(), localed ? NodeServer.this.sncpGroup : null, groups, entry.getProperty());
|
//final ServiceWrapper wrapper = new ServiceWrapper(serviceImplClass, service, entry.getName(), localed ? NodeServer.this.sncpGroup : null, groups, entry.getProperty());
|
||||||
for (final Class restype : wrapper.getTypes()) {
|
for (final Class restype : Sncp.getResourceTypes(service)) {
|
||||||
if (resourceFactory.find(wrapper.getName(), restype) == null) {
|
if (resourceFactory.find(entry.getName(), restype) == null) {
|
||||||
regFactory.register(wrapper.getName(), restype, wrapper.getService());
|
regFactory.register(entry.getName(), restype, service);
|
||||||
if (needinject) rf.inject(wrapper.getService()); //动态加载的Service也存在按需加载的注入资源
|
if (needinject) rf.inject(service); //动态加载的Service也存在按需加载的注入资源
|
||||||
} else if (isSNCP() && !entry.isAutoload()) {
|
} else if (isSNCP() && !entry.isAutoload()) {
|
||||||
throw new RuntimeException(ServiceWrapper.class.getSimpleName() + "(class:" + type.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
|
throw new RuntimeException(restype.getSimpleName() + "(class:" + serviceImplClass.getName() + ", name:" + entry.getName() + ", group:" + groups + ") is repeat.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wrapper.isRemote()) {
|
if (Sncp.isRemote(service)) {
|
||||||
remoteServiceWrappers.add(wrapper);
|
remoteServices.add(service);
|
||||||
} else {
|
} else {
|
||||||
localServiceWrappers.add(wrapper);
|
localServices.add(service);
|
||||||
interceptorServiceWrappers.add(new NodeInterceptor.InterceptorServiceWrapper(entry.getName(), type, service));
|
interceptorServices.add(service);
|
||||||
if (consumer != null) consumer.accept(wrapper);
|
if (consumer != null) consumer.accept(service);
|
||||||
}
|
}
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
@@ -325,7 +354,9 @@ public abstract class NodeServer {
|
|||||||
ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
|
ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> {
|
||||||
runner.accept(rf, true);
|
runner.accept(rf, true);
|
||||||
};
|
};
|
||||||
for (final Class restype : ServiceWrapper.parseTypes(entry.getType())) {
|
ResourceType rty = entry.getType().getAnnotation(ResourceType.class);
|
||||||
|
Class[] resTypes = rty == null ? new Class[]{} : rty.value();
|
||||||
|
for (final Class restype : resTypes) {
|
||||||
resourceFactory.register(resourceLoader, restype);
|
resourceFactory.register(resourceLoader, restype);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -339,30 +370,38 @@ public abstract class NodeServer {
|
|||||||
|
|
||||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||||
//---------------- inject ----------------
|
//---------------- inject ----------------
|
||||||
new ArrayList<>(localServiceWrappers).forEach(y -> {
|
new ArrayList<>(localServices).forEach(y -> {
|
||||||
resourceFactory.inject(y.getService(), NodeServer.this);
|
resourceFactory.inject(y, NodeServer.this);
|
||||||
|
calcMaxLength(y);
|
||||||
});
|
});
|
||||||
new ArrayList<>(remoteServiceWrappers).forEach(y -> {
|
new ArrayList<>(remoteServices).forEach(y -> {
|
||||||
resourceFactory.inject(y.getService(), NodeServer.this);
|
resourceFactory.inject(y, NodeServer.this);
|
||||||
|
calcMaxLength(y);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (sb != null) {
|
if (sb != null) {
|
||||||
remoteServiceWrappers.forEach(y -> {
|
remoteServices.forEach(y -> {
|
||||||
sb.append(threadName).append(y.toSimpleString()).append(" loaded and injected").append(LINE_SEPARATOR);
|
sb.append(threadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//----------------- init -----------------
|
//----------------- init -----------------
|
||||||
List<ServiceWrapper> swlist = new ArrayList<>(localServiceWrappers);
|
List<Service> swlist = new ArrayList<>(localServices);
|
||||||
Collections.sort(swlist);
|
Collections.sort(swlist, (o1, o2) -> {
|
||||||
localServiceWrappers.clear();
|
int rs = Sncp.getResourceTypes(o1)[0].getName().compareTo(Sncp.getResourceTypes(o2)[0].getName());
|
||||||
localServiceWrappers.addAll(swlist);
|
if (rs == 0) rs = Sncp.getResourceName(o1).compareTo(Sncp.getResourceName(o2));
|
||||||
|
return rs;
|
||||||
|
});
|
||||||
|
localServices.clear();
|
||||||
|
localServices.addAll(swlist);
|
||||||
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
|
final List<String> slist = sb == null ? null : new CopyOnWriteArrayList<>();
|
||||||
CountDownLatch clds = new CountDownLatch(localServiceWrappers.size());
|
CountDownLatch clds = new CountDownLatch(localServices.size());
|
||||||
localServiceWrappers.parallelStream().forEach(y -> {
|
localServices.parallelStream().forEach(y -> {
|
||||||
try {
|
try {
|
||||||
long s = System.currentTimeMillis();
|
long s = System.currentTimeMillis();
|
||||||
y.getService().init(y.getConf());
|
y.init(Sncp.getConf(y));
|
||||||
long e = System.currentTimeMillis() - s;
|
long e = System.currentTimeMillis() - s;
|
||||||
if (slist != null) slist.add(new StringBuilder().append(threadName).append(y.toSimpleString()).append(" loaded and inited ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength);
|
||||||
|
if (slist != null) slist.add(new StringBuilder().append(threadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString());
|
||||||
} finally {
|
} finally {
|
||||||
clds.countDown();
|
clds.countDown();
|
||||||
}
|
}
|
||||||
@@ -378,6 +417,20 @@ public abstract class NodeServer {
|
|||||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void calcMaxLength(Service y) { //计算toString中的长度
|
||||||
|
maxNameLength = Math.max(maxNameLength, Sncp.getResourceName(y).length());
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
Class[] types = Sncp.getResourceTypes(y);
|
||||||
|
if (types.length == 1) {
|
||||||
|
s.append(types[0].getName());
|
||||||
|
} else {
|
||||||
|
s.append('[');
|
||||||
|
s.append(Arrays.asList(types).stream().map((Class t) -> t.getName()).collect(Collectors.joining(",")));
|
||||||
|
s.append(']');
|
||||||
|
}
|
||||||
|
maxClassNameLength = Math.max(maxClassNameLength, s.length() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
protected List<Transport> loadTransports(final HashSet<String> groups) {
|
protected List<Transport> loadTransports(final HashSet<String> groups) {
|
||||||
if (groups == null) return null;
|
if (groups == null) return null;
|
||||||
final List<Transport> transports = new ArrayList<>();
|
final List<Transport> transports = new ArrayList<>();
|
||||||
@@ -403,7 +456,7 @@ public abstract class NodeServer {
|
|||||||
Transport first = transports.get(0);
|
Transport first = transports.get(0);
|
||||||
GroupInfo ginfo = application.findGroupInfo(first.getName());
|
GroupInfo ginfo = application.findGroupInfo(first.getName());
|
||||||
Transport newTransport = new Transport(groupid, ginfo.getProtocol(), application.getWatchFactory(),
|
Transport newTransport = new Transport(groupid, ginfo.getProtocol(), application.getWatchFactory(),
|
||||||
ginfo.getKind(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
||||||
synchronized (application.resourceFactory) {
|
synchronized (application.resourceFactory) {
|
||||||
transport = application.resourceFactory.find(groupid, Transport.class);
|
transport = application.resourceFactory.find(groupid, Transport.class);
|
||||||
if (transport == null) {
|
if (transport == null) {
|
||||||
@@ -429,7 +482,7 @@ public abstract class NodeServer {
|
|||||||
Set<InetSocketAddress> addrs = ginfo.copyAddrs();
|
Set<InetSocketAddress> addrs = ginfo.copyAddrs();
|
||||||
if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
|
if (addrs == null) throw new RuntimeException("Not found <group> = " + group + " on <resources> ");
|
||||||
transport = new Transport(group, ginfo.getProtocol(), application.getWatchFactory(),
|
transport = new Transport(group, ginfo.getProtocol(), application.getWatchFactory(),
|
||||||
ginfo.getKind(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
ginfo.getSubprotocol(), application.transportBufferPool, application.transportChannelGroup, this.sncpAddress, addrs);
|
||||||
application.resourceFactory.register(group, transport);
|
application.resourceFactory.register(group, transport);
|
||||||
}
|
}
|
||||||
return transport;
|
return transport;
|
||||||
@@ -532,12 +585,12 @@ public abstract class NodeServer {
|
|||||||
public void shutdown() throws IOException {
|
public void shutdown() throws IOException {
|
||||||
if (interceptor != null) interceptor.preShutdown(this);
|
if (interceptor != null) interceptor.preShutdown(this);
|
||||||
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null;
|
||||||
localServiceWrappers.forEach(y -> {
|
localServices.forEach(y -> {
|
||||||
long s = System.currentTimeMillis();
|
long s = System.currentTimeMillis();
|
||||||
y.getService().destroy(y.getConf());
|
y.destroy(Sncp.getConf(y));
|
||||||
long e = System.currentTimeMillis() - s;
|
long e = System.currentTimeMillis() - s;
|
||||||
if (e > 2 && sb != null) {
|
if (e > 2 && sb != null) {
|
||||||
sb.append(y.toSimpleString()).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);
|
sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString());
|
||||||
@@ -548,16 +601,16 @@ public abstract class NodeServer {
|
|||||||
return (T) server;
|
return (T) server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<NodeInterceptor.InterceptorServiceWrapper> getInterceptorServiceWrappers() {
|
public Set<Service> getInterceptorServices() {
|
||||||
return new LinkedHashSet<>(interceptorServiceWrappers);
|
return new LinkedHashSet<>(interceptorServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ServiceWrapper> getLocalServiceWrappers() {
|
public Set<Service> getLocalServices() {
|
||||||
return new LinkedHashSet<>(localServiceWrappers);
|
return new LinkedHashSet<>(localServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ServiceWrapper> getRemoteServiceWrappers() {
|
public Set<Service> getRemoteServices() {
|
||||||
return new LinkedHashSet<>(remoteServiceWrappers);
|
return new LinkedHashSet<>(remoteServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import java.util.*;
|
|||||||
import java.util.logging.*;
|
import java.util.logging.*;
|
||||||
import org.redkale.net.*;
|
import org.redkale.net.*;
|
||||||
import org.redkale.net.sncp.*;
|
import org.redkale.net.sncp.*;
|
||||||
|
import org.redkale.service.Service;
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* SNCP Server节点的配置Server
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -47,8 +49,8 @@ public class NodeSncpServer extends NodeServer {
|
|||||||
return sncpServer == null ? null : sncpServer.getSocketAddress();
|
return sncpServer == null ? null : sncpServer.getSocketAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void consumerAccept(ServiceWrapper wrapper) {
|
public void consumerAccept(Service service) {
|
||||||
if (this.consumer != null) this.consumer.accept(wrapper);
|
if (this.consumer != null) this.consumer.accept(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +63,7 @@ public class NodeSncpServer extends NodeServer {
|
|||||||
List<SncpServlet> servlets = sncpServer.getSncpServlets();
|
List<SncpServlet> servlets = sncpServer.getSncpServlets();
|
||||||
Collections.sort(servlets);
|
Collections.sort(servlets);
|
||||||
for (SncpServlet en : servlets) {
|
for (SncpServlet en : servlets) {
|
||||||
if (sb != null) sb.append(threadName).append(" Loaded ").append(en).append(LINE_SEPARATOR);
|
if (sb != null) sb.append(threadName).append(" Load ").append(en).append(LINE_SEPARATOR);
|
||||||
}
|
}
|
||||||
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"><title>Document</title>
|
<meta charset="UTF-8"><title>接口文档(apidoc生成)</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {text-align: center;margin:auto;}
|
body {text-align: center;margin:auto;}
|
||||||
a{text-decoration: none;}
|
a{text-decoration: none;}
|
||||||
.table {margin: auto;border-collapse: collapse;border-spacing: 0;display: block;width: 100%;overflow: auto;word-break: normal;word-break: keep-all;}
|
.table {margin: auto;border-collapse: collapse;border-spacing: 0;display: block;width: 100%;overflow: auto;word-break: normal;word-break: keep-all;}
|
||||||
.table td,.table th{padding: 0.4rem 1.2rem 0.4rem 1.2rem;border: 1px solid #aaa;}
|
.table td,.table th{padding: 0.2rem 0.8rem 0.2rem 0.8rem;border: 1px solid #aaa;}
|
||||||
.table td {text-align: left;}
|
.table td {text-align: left;}
|
||||||
.s {font-size: 0.8rem; vertical-align: middle;}
|
.s {font-size: 0.8rem; vertical-align: middle;}
|
||||||
.subtable {border-spacing: 0;border: 0;margin:0;}
|
.subtable {border-spacing: 0;border: 0;margin:0;}
|
||||||
@@ -25,13 +25,19 @@
|
|||||||
html.push('<div style="width:' + Math.floor(window.screen.width * 0.9) + 'px;margin:0 auto;text-align: center;">');
|
html.push('<div style="width:' + Math.floor(window.screen.width * 0.9) + 'px;margin:0 auto;text-align: center;">');
|
||||||
html.push('<br/><br/><table class="table" align="center">');
|
html.push('<br/><br/><table class="table" align="center">');
|
||||||
for (var i = 0; i < jsoncontent.servers.length; i++) {
|
for (var i = 0; i < jsoncontent.servers.length; i++) {
|
||||||
for (var j = 0; j < jsoncontent.servers[i].servlets.length; j++) {
|
var servlets = jsoncontent.servers[i].servlets;
|
||||||
var servlet = jsoncontent.servers[i].servlets[j];
|
if (servlets.length && (servlets[0].comment || "").indexOf("【") === 0) {
|
||||||
|
servlets.sort(function (a, b) {
|
||||||
|
return a.comment > b.comment ? -1 : (a.comment == b.comment ? 0 : 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (var j = 0; j < servlets.length; j++) {
|
||||||
|
var servlet = servlets[j];
|
||||||
if (html.length > 2) html.push(' <tr><th colspan="5" style="border-bottom:0;"> </th></tr>');
|
if (html.length > 2) html.push(' <tr><th colspan="5" style="border-bottom:0;"> </th></tr>');
|
||||||
html.push(' <tr><th colspan="5" style="border-top:' + ((html.length > 2) ? 0 : 1) + ';">' + (servlet.comment || '未知模块') + '</th></tr>');
|
html.push(' <tr><th colspan="5" style="border-top:' + ((html.length > 2) ? 0 : 1) + ';">' + (servlet.comment || '未知模块') + '</th></tr>');
|
||||||
html.push(' <tr><th>请求URL</th><th>描 述</th><th>鉴 权</th><th>参 数 <span style="font-size:12px;">(红色: Header; 蓝色: Cookie)</span></th><th>输 出</th></tr>');
|
html.push(' <tr><th>请求URL</th><th>描 述</th><th>鉴 权</th><th>参 数 <span style="font-size:12px;">(粗体: 必填项; 红色: Header; 蓝色: Cookie)</span></th><th>输 出</th></tr>');
|
||||||
for (var k = 0; k < servlet.actions.length; k++) {
|
for (var k = 0; k < servlet.mappings.length; k++) {
|
||||||
var action = servlet.actions[k];
|
var action = servlet.mappings[k];
|
||||||
html.push(' <tr>');
|
html.push(' <tr>');
|
||||||
html.push('<td style="color:#ff00ff;">' + action.url + '</td>');
|
html.push('<td style="color:#ff00ff;">' + action.url + '</td>');
|
||||||
html.push('<td>' + action.comment + '</td>');
|
html.push('<td>' + action.comment + '</td>');
|
||||||
@@ -47,15 +53,22 @@
|
|||||||
if (param.name == '&') {
|
if (param.name == '&') {
|
||||||
paramshtml.push('<tr><td style="font-size:12px;">内置 </td><td> ' + t + '</td><td> 当前用户</td></tr>');
|
paramshtml.push('<tr><td style="font-size:12px;">内置 </td><td> ' + t + '</td><td> 当前用户</td></tr>');
|
||||||
} else {
|
} else {
|
||||||
var c = ' style="font-weight:bold;"';
|
var w = param.required ? "font-weight:bold;" : "";
|
||||||
if (param.src == "HEADER") c = ' style="color:red;font-weight:bold;"';
|
var c = ' style="' + w + '"';
|
||||||
if (param.src == "COOKIE") c = ' style="color:blue;font-weight:bold;"';
|
if (param.src == "HEADER") c = ' style="color:red;' + w + '"';
|
||||||
|
if (param.src == "COOKIE") c = ' style="color:blue;' + w + '"';
|
||||||
paramshtml.push('<tr><td ' + c + '> ' + param.name + ' </td><td> ' + t + '</td><td> ' + param.comment + '</td></tr>');
|
paramshtml.push('<tr><td ' + c + '> ' + param.name + ' </td><td> ' + t + '</td><td> ' + param.comment + '</td></tr>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paramshtml.push('</table>');
|
paramshtml.push('</table>');
|
||||||
html.push('<td class="s" style="padding:0 5px;">' + paramshtml.join('') + '</td>');
|
html.push('<td class="s" style="padding:0 5px;">' + paramshtml.join('') + '</td>');
|
||||||
html.push('<td>' + action.result.replace(/</g,"<").replace(/>/g,">").replace(/([a-zA-Z0-9_\$]+\.)+/g, "") + '</td>');
|
var rs = [];
|
||||||
|
rs.push(action.result.replace(/</g, "<").replace(/>/g, ">").replace(/([a-zA-Z0-9_\$]+\.)+/g, ""));
|
||||||
|
var results = action.results || [];
|
||||||
|
for (var r = 0; r < results.length; r++) {
|
||||||
|
rs.push('<a href="#' + results[r].replace('[]', '') + '">' + results[r].replace(/([a-zA-Z0-9_\$]+\.)+/g, "") + '</a>');
|
||||||
|
}
|
||||||
|
html.push('<td>' + rs.join("<br/>") + '</td>');
|
||||||
html.push('</tr>');
|
html.push('</tr>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,8 +79,20 @@
|
|||||||
html.push(' <tr><td colspan="5"><table class="typetable">');
|
html.push(' <tr><td colspan="5"><table class="typetable">');
|
||||||
for (var fieldname in classmap[type]) {
|
for (var fieldname in classmap[type]) {
|
||||||
var field = classmap[type][fieldname];
|
var field = classmap[type][fieldname];
|
||||||
var t = field.type.replace(/</g,"<").replace(/>/g,">").replace(/([a-zA-Z0-9_\$]+\.)+/g, "");
|
var t = field.type.replace(/</g, "<").replace(/>/g, ">").replace(/\$/g, ".").replace(/([a-zA-Z0-9_\$]+\.)+/g, "");
|
||||||
html.push(' <tr class="l"><td>' + fieldname + '</td><td>' + t + '</td><td colspan="2">' + (field.comment || '') + '</td></tr>');
|
if (t == 'boolean' || t == 'short' || t == 'int' || t == 'long' || t == 'float' || t == 'double'
|
||||||
|
|| t == 'boolean[]' || t == 'short[]' || t == 'int[]' || t == 'long[]' || t == 'float[]' || t == 'double[]') {
|
||||||
|
t = '<font color=blue>' + t + '</font>';
|
||||||
|
} else if (t == 'String' || t == 'String[]' || t == 'LongRange' || t.indexOf('Map<') === 0) {
|
||||||
|
t = '<font color=red>' + t + '</font>';
|
||||||
|
}
|
||||||
|
var c = (field.comment || '');
|
||||||
|
if (field.primary) {
|
||||||
|
c = '【主键】 ' + c;
|
||||||
|
} else if (!field.updatable) {
|
||||||
|
c = '【只读】 ' + c;
|
||||||
|
}
|
||||||
|
html.push(' <tr class="l"><td>' + fieldname + '</td><td>' + t + '</td><td colspan="2">' + c + '</td></tr>');
|
||||||
}
|
}
|
||||||
html.push(' </table></td></tr>');
|
html.push(' </table></td></tr>');
|
||||||
}
|
}
|
||||||
@@ -78,7 +103,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var jsoncontent = ${content};
|
var jsoncontent = '${content}'; //这里必须要用单引号引起来
|
||||||
document.write(createhtml(jsoncontent));
|
document.write(createhtml(jsoncontent));
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import java.lang.reflect.*;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对象数组的序列化,不包含int[]、long[]这样的primitive class数组.
|
* 数组的反序列化操作类 <br>
|
||||||
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有采用int存储。
|
* 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组。 <br>
|
||||||
* 支持一定程度的泛型。
|
* 支持一定程度的泛型。 <br>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -28,10 +28,15 @@ public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
|||||||
|
|
||||||
private final Class componentClass;
|
private final Class componentClass;
|
||||||
|
|
||||||
private final Decodeable<Reader, T> decoder;
|
protected final Decodeable<Reader, T> decoder;
|
||||||
|
|
||||||
|
private boolean inited = false;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public ArrayDecoder(final ConvertFactory factory, final Type type) {
|
public ArrayDecoder(final ConvertFactory factory, final Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
try {
|
||||||
if (type instanceof GenericArrayType) {
|
if (type instanceof GenericArrayType) {
|
||||||
Type t = ((GenericArrayType) type).getGenericComponentType();
|
Type t = ((GenericArrayType) type).getGenericComponentType();
|
||||||
this.componentType = t instanceof TypeVariable ? Object.class : t;
|
this.componentType = t instanceof TypeVariable ? Object.class : t;
|
||||||
@@ -47,12 +52,29 @@ public final class ArrayDecoder<T> implements Decodeable<Reader, T[]> {
|
|||||||
}
|
}
|
||||||
factory.register(type, this);
|
factory.register(type, this);
|
||||||
this.decoder = factory.loadDecoder(this.componentType);
|
this.decoder = factory.loadDecoder(this.componentType);
|
||||||
|
} finally {
|
||||||
|
inited = true;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T[] convertFrom(Reader in) {
|
public T[] convertFrom(Reader in) {
|
||||||
final int len = in.readArrayB();
|
final int len = in.readArrayB();
|
||||||
if (len == Reader.SIGN_NULL) return null;
|
if (len == Reader.SIGN_NULL) return null;
|
||||||
|
if (this.decoder == null) {
|
||||||
|
if (!this.inited) {
|
||||||
|
synchronized (lock) {
|
||||||
|
try {
|
||||||
|
lock.wait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||||
final List<T> result = new ArrayList();
|
final List<T> result = new ArrayList();
|
||||||
if (len == Reader.SIGN_NOLENGTH) {
|
if (len == Reader.SIGN_NOLENGTH) {
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ package org.redkale.convert;
|
|||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组.
|
* 数组的序列化操作类 <br>
|
||||||
* 数组长度不能超过 32767。 在BSON中数组长度设定的是short,对于大于32767长度的数组传输会影响性能,所以没有必要采用int存储。
|
* 对象数组的序列化,不包含int[]、long[]这样的primitive class数组。 <br>
|
||||||
* 支持一定程度的泛型。
|
* 支持一定程度的泛型。 <br>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -29,8 +29,13 @@ public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
|||||||
|
|
||||||
private final Encodeable<Writer, Object> encoder;
|
private final Encodeable<Writer, Object> encoder;
|
||||||
|
|
||||||
|
private boolean inited = false;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public ArrayEncoder(final ConvertFactory factory, final Type type) {
|
public ArrayEncoder(final ConvertFactory factory, final Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
try {
|
||||||
if (type instanceof GenericArrayType) {
|
if (type instanceof GenericArrayType) {
|
||||||
Type t = ((GenericArrayType) type).getGenericComponentType();
|
Type t = ((GenericArrayType) type).getGenericComponentType();
|
||||||
this.componentType = t instanceof TypeVariable ? Object.class : t;
|
this.componentType = t instanceof TypeVariable ? Object.class : t;
|
||||||
@@ -42,6 +47,12 @@ public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
|||||||
factory.register(type, this);
|
factory.register(type, this);
|
||||||
this.encoder = factory.loadEncoder(this.componentType);
|
this.encoder = factory.loadEncoder(this.componentType);
|
||||||
this.anyEncoder = factory.getAnyEncoder();
|
this.anyEncoder = factory.getAnyEncoder();
|
||||||
|
} finally {
|
||||||
|
inited = true;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -55,6 +66,17 @@ public final class ArrayEncoder<T> implements Encodeable<Writer, T[]> {
|
|||||||
out.writeArrayE();
|
out.writeArrayE();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.encoder == null) {
|
||||||
|
if (!this.inited) {
|
||||||
|
synchronized (lock) {
|
||||||
|
try {
|
||||||
|
lock.wait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
out.writeArrayB(value.length);
|
out.writeArrayB(value.length);
|
||||||
final Type comp = this.componentType;
|
final Type comp = this.componentType;
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
|
|||||||
@@ -11,9 +11,8 @@ import java.lang.reflect.Type;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对象集合的反序列化.
|
* Collection的反序列化操作类 <br>
|
||||||
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
|
* 支持一定程度的泛型。 <br>
|
||||||
* 支持一定程度的泛型。
|
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -30,10 +29,15 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
|
|||||||
|
|
||||||
protected Creator<Collection<T>> creator;
|
protected Creator<Collection<T>> creator;
|
||||||
|
|
||||||
private final Decodeable<Reader, T> decoder;
|
protected final Decodeable<Reader, T> decoder;
|
||||||
|
|
||||||
|
private boolean inited = false;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public CollectionDecoder(final ConvertFactory factory, final Type type) {
|
public CollectionDecoder(final ConvertFactory factory, final Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
try {
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
final ParameterizedType pt = (ParameterizedType) type;
|
final ParameterizedType pt = (ParameterizedType) type;
|
||||||
this.componentType = pt.getActualTypeArguments()[0];
|
this.componentType = pt.getActualTypeArguments()[0];
|
||||||
@@ -43,12 +47,29 @@ public final class CollectionDecoder<T> implements Decodeable<Reader, Collection
|
|||||||
} else {
|
} else {
|
||||||
throw new ConvertException("collectiondecoder not support the type (" + type + ")");
|
throw new ConvertException("collectiondecoder not support the type (" + type + ")");
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
inited = true;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<T> convertFrom(Reader in) {
|
public Collection<T> convertFrom(Reader in) {
|
||||||
final int len = in.readArrayB();
|
final int len = in.readArrayB();
|
||||||
if (len == Reader.SIGN_NULL) return null;
|
if (len == Reader.SIGN_NULL) return null;
|
||||||
|
if (this.decoder == null) {
|
||||||
|
if (!this.inited) {
|
||||||
|
synchronized (lock) {
|
||||||
|
try {
|
||||||
|
lock.wait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
final Decodeable<Reader, T> localdecoder = this.decoder;
|
final Decodeable<Reader, T> localdecoder = this.decoder;
|
||||||
final Collection<T> result = this.creator.create();
|
final Collection<T> result = this.creator.create();
|
||||||
if (len == Reader.SIGN_NOLENGTH) {
|
if (len == Reader.SIGN_NOLENGTH) {
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import java.lang.reflect.*;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对象集合的序列化.
|
* Collection的序列化操作类 <br>
|
||||||
* 集合大小不能超过 32767。 在BSON中集合大小设定的是short,对于大于32767长度的集合传输会影响性能,所以没有采用int存储。
|
* 支持一定程度的泛型。 <br>
|
||||||
* 支持一定程度的泛型。
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
* @param <T> 序列化的集合元素类型
|
* @param <T> 序列化的集合元素类型
|
||||||
*/
|
*/
|
||||||
@@ -24,8 +25,13 @@ public final class CollectionEncoder<T> implements Encodeable<Writer, Collection
|
|||||||
|
|
||||||
private final Encodeable<Writer, Object> encoder;
|
private final Encodeable<Writer, Object> encoder;
|
||||||
|
|
||||||
|
private boolean inited = false;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public CollectionEncoder(final ConvertFactory factory, final Type type) {
|
public CollectionEncoder(final ConvertFactory factory, final Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
try {
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
|
Type t = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||||
if (t instanceof TypeVariable) {
|
if (t instanceof TypeVariable) {
|
||||||
@@ -36,6 +42,12 @@ public final class CollectionEncoder<T> implements Encodeable<Writer, Collection
|
|||||||
} else {
|
} else {
|
||||||
this.encoder = factory.getAnyEncoder();
|
this.encoder = factory.getAnyEncoder();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
inited = true;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -49,6 +61,17 @@ public final class CollectionEncoder<T> implements Encodeable<Writer, Collection
|
|||||||
out.writeArrayE();
|
out.writeArrayE();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.encoder == null) {
|
||||||
|
if (!this.inited) {
|
||||||
|
synchronized (lock) {
|
||||||
|
try {
|
||||||
|
lock.wait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
out.writeArrayB(value.size());
|
out.writeArrayB(value.size());
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (Object v : value) {
|
for (Object v : value) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
package org.redkale.convert;
|
package org.redkale.convert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 序列化操作类
|
* 序列化/反序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于类名的别名, 类似javax.persistence.Table
|
* 用于类名的别名, 该值必须是全局唯一 <br>
|
||||||
* 该值必须是全局唯一
|
|
||||||
* 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。
|
* 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
|
*
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
@Inherited
|
@Inherited
|
||||||
@@ -23,5 +24,10 @@ import java.lang.annotation.*;
|
|||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
public @interface ConvertEntity {
|
public @interface ConvertEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 别名值
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
String value();
|
String value();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
package org.redkale.convert;
|
package org.redkale.convert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 序列化自定义异常类
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
public class ConvertException extends RuntimeException {
|
public class ConvertException extends RuntimeException {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.redkale.convert.ext.*;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 序列化模块的工厂类,用于注册自定义的序列化类型,获取Convert
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -91,6 +92,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
|||||||
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
|
this.register(InetSocketAddress.class, InetSocketAddressSimpledCoder.instance);
|
||||||
this.register(Pattern.class, PatternSimpledCoder.instance);
|
this.register(Pattern.class, PatternSimpledCoder.instance);
|
||||||
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
|
this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance);
|
||||||
|
this.register(AsyncHandler.class, AsyncHandlerSimpledCoder.instance);
|
||||||
this.register(URL.class, URLSimpledCoder.instance);
|
this.register(URL.class, URLSimpledCoder.instance);
|
||||||
this.register(URI.class, URISimpledCoder.instance);
|
this.register(URI.class, URISimpledCoder.instance);
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
@@ -104,6 +106,7 @@ public abstract class ConvertFactory<R extends Reader, W extends Writer> {
|
|||||||
this.register(double[].class, DoubleArraySimpledCoder.instance);
|
this.register(double[].class, DoubleArraySimpledCoder.instance);
|
||||||
this.register(String[].class, StringArraySimpledCoder.instance);
|
this.register(String[].class, StringArraySimpledCoder.instance);
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class));
|
||||||
this.register(HttpCookie.class, new Creator<HttpCookie>() {
|
this.register(HttpCookie.class, new Creator<HttpCookie>() {
|
||||||
@Override
|
@Override
|
||||||
@Creator.ConstructorParameters({"name", "value"})
|
@Creator.ConstructorParameters({"name", "value"})
|
||||||
|
|||||||
@@ -6,8 +6,11 @@
|
|||||||
package org.redkale.convert;
|
package org.redkale.convert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 序列化类型枚举,结合@ConvertColumn使用
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
public enum ConvertType {
|
public enum ConvertType {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.lang.reflect.*;
|
|||||||
import org.redkale.util.Attribute;
|
import org.redkale.util.Attribute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 字段的反序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -88,6 +89,6 @@ public final class DeMember<R extends Reader, T, F> implements Comparable<DeMemb
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DeMember{" + "attribute=" + attribute.field() + ", decoder=" + decoder + '}';
|
return "DeMember{" + "attribute=" + attribute.field() + ", decoder=" + (decoder == null ? null : decoder.getClass().getName()) + '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.redkale.convert;
|
|||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 反序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.lang.reflect.*;
|
|||||||
import org.redkale.util.Attribute;
|
import org.redkale.util.Attribute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 字段的序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -81,6 +82,6 @@ public final class EnMember<W extends Writer, T, F> implements Comparable<EnMemb
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "EnMember{" + "attribute=" + attribute.field() + ", encoder=" + encoder + '}';
|
return "EnMember{" + "attribute=" + attribute.field() + ", encoder=" + (encoder == null ? null : encoder.getClass().getName()) + '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.redkale.convert;
|
|||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ import java.lang.reflect.Type;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Map的反序列化操作类 <br>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
* @param <K> Map key的数据类型
|
* @param <K> Map key的数据类型
|
||||||
* @param <V> Map value的数据类型
|
* @param <V> Map value的数据类型
|
||||||
@@ -28,12 +31,17 @@ public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
|||||||
|
|
||||||
protected Creator<Map<K, V>> creator;
|
protected Creator<Map<K, V>> creator;
|
||||||
|
|
||||||
private final Decodeable<Reader, K> keyDecoder;
|
protected final Decodeable<Reader, K> keyDecoder;
|
||||||
|
|
||||||
private final Decodeable<Reader, V> valueDecoder;
|
protected final Decodeable<Reader, V> valueDecoder;
|
||||||
|
|
||||||
|
private boolean inited = false;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public MapDecoder(final ConvertFactory factory, final Type type) {
|
public MapDecoder(final ConvertFactory factory, final Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
try {
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
final ParameterizedType pt = (ParameterizedType) type;
|
final ParameterizedType pt = (ParameterizedType) type;
|
||||||
this.keyType = pt.getActualTypeArguments()[0];
|
this.keyType = pt.getActualTypeArguments()[0];
|
||||||
@@ -45,10 +53,27 @@ public final class MapDecoder<K, V> implements Decodeable<Reader, Map<K, V>> {
|
|||||||
} else {
|
} else {
|
||||||
throw new ConvertException("mapdecoder not support the type (" + type + ")");
|
throw new ConvertException("mapdecoder not support the type (" + type + ")");
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
inited = true;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<K, V> convertFrom(Reader in) {
|
public Map<K, V> convertFrom(Reader in) {
|
||||||
|
if (this.keyDecoder == null || this.valueDecoder == null) {
|
||||||
|
if (!this.inited) {
|
||||||
|
synchronized (lock) {
|
||||||
|
try {
|
||||||
|
lock.wait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
final int len = in.readMapB();
|
final int len = in.readMapB();
|
||||||
if (len == Reader.SIGN_NULL) return null;
|
if (len == Reader.SIGN_NULL) return null;
|
||||||
final Map<K, V> result = this.creator.create();
|
final Map<K, V> result = this.creator.create();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.lang.reflect.Type;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Map的序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -27,8 +28,13 @@ public final class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
|||||||
|
|
||||||
private final Encodeable<Writer, V> valencoder;
|
private final Encodeable<Writer, V> valencoder;
|
||||||
|
|
||||||
|
private boolean inited = false;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public MapEncoder(final ConvertFactory factory, final Type type) {
|
public MapEncoder(final ConvertFactory factory, final Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
try {
|
||||||
if (type instanceof ParameterizedType) {
|
if (type instanceof ParameterizedType) {
|
||||||
final Type[] pt = ((ParameterizedType) type).getActualTypeArguments();
|
final Type[] pt = ((ParameterizedType) type).getActualTypeArguments();
|
||||||
this.keyencoder = factory.loadEncoder(pt[0]);
|
this.keyencoder = factory.loadEncoder(pt[0]);
|
||||||
@@ -37,6 +43,12 @@ public final class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
|||||||
this.keyencoder = factory.getAnyEncoder();
|
this.keyencoder = factory.getAnyEncoder();
|
||||||
this.valencoder = factory.getAnyEncoder();
|
this.valencoder = factory.getAnyEncoder();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
inited = true;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -46,6 +58,18 @@ public final class MapEncoder<K, V> implements Encodeable<Writer, Map<K, V>> {
|
|||||||
out.writeNull();
|
out.writeNull();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.keyencoder == null || this.valencoder == null) {
|
||||||
|
if (!this.inited) {
|
||||||
|
synchronized (lock) {
|
||||||
|
try {
|
||||||
|
lock.wait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
out.writeMapB(values.size());
|
out.writeMapB(values.size());
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (Map.Entry<K, V> en : values.entrySet()) {
|
for (Map.Entry<K, V> en : values.entrySet()) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.Set;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 自定义对象的反序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -66,6 +67,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
|||||||
clazz = (Class) type;
|
clazz = (Class) type;
|
||||||
}
|
}
|
||||||
this.creator = factory.loadCreator(clazz);
|
this.creator = factory.loadCreator(clazz);
|
||||||
|
if (this.creator == null) throw new ConvertException("Cannot create a creator for " + clazz);
|
||||||
|
|
||||||
final Set<DeMember> list = new HashSet();
|
final Set<DeMember> list = new HashSet();
|
||||||
final String[] cps = ObjectEncoder.findConstructorProperties(this.creator);
|
final String[] cps = ObjectEncoder.findConstructorProperties(this.creator);
|
||||||
@@ -75,7 +77,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
|||||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||||
ref = factory.findRef(field);
|
ref = factory.findRef(field);
|
||||||
if (ref != null && ref.ignore()) continue;
|
if (ref != null && ref.ignore()) continue;
|
||||||
Type t = ObjectEncoder.createClassType(field.getGenericType(), this.type);
|
Type t = TypeToken.createClassType(field.getGenericType(), this.type);
|
||||||
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t)));
|
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, field, null, null), factory.loadDecoder(t)));
|
||||||
}
|
}
|
||||||
final boolean reversible = factory.isReversible();
|
final boolean reversible = factory.isReversible();
|
||||||
@@ -85,6 +87,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
|||||||
if (method.isSynthetic()) continue;
|
if (method.isSynthetic()) continue;
|
||||||
if (method.getName().length() < 4) continue;
|
if (method.getName().length() < 4) continue;
|
||||||
if (!method.getName().startsWith("set")) continue;
|
if (!method.getName().startsWith("set")) continue;
|
||||||
|
if (method.getAnnotation(java.beans.Transient.class) != null) continue;
|
||||||
if (method.getParameterTypes().length != 1) continue;
|
if (method.getParameterTypes().length != 1) continue;
|
||||||
if (method.getReturnType() != void.class) continue;
|
if (method.getReturnType() != void.class) continue;
|
||||||
if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
|
if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
|
||||||
@@ -97,7 +100,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
|||||||
}
|
}
|
||||||
ref = factory.findRef(method);
|
ref = factory.findRef(method);
|
||||||
if (ref != null && ref.ignore()) continue;
|
if (ref != null && ref.ignore()) continue;
|
||||||
Type t = ObjectEncoder.createClassType(method.getGenericParameterTypes()[0], this.type);
|
Type t = TypeToken.createClassType(method.getGenericParameterTypes()[0], this.type);
|
||||||
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t)));
|
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, null, method), factory.loadDecoder(t)));
|
||||||
}
|
}
|
||||||
if (cps != null) { //可能存在某些构造函数中的字段名不存在setter方法
|
if (cps != null) { //可能存在某些构造函数中的字段名不存在setter方法
|
||||||
@@ -113,7 +116,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
|||||||
//不存在setter方法
|
//不存在setter方法
|
||||||
try {
|
try {
|
||||||
Field f = clazz.getDeclaredField(constructorField);
|
Field f = clazz.getDeclaredField(constructorField);
|
||||||
Type t = ObjectEncoder.createClassType(f.getGenericType(), this.type);
|
Type t = TypeToken.createClassType(f.getGenericType(), this.type);
|
||||||
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, f, null, null), factory.loadDecoder(t)));
|
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, f, null, null), factory.loadDecoder(t)));
|
||||||
} catch (NoSuchFieldException nsfe) { //不存在field, 可能存在getter方法
|
} catch (NoSuchFieldException nsfe) { //不存在field, 可能存在getter方法
|
||||||
char[] fs = constructorField.toCharArray();
|
char[] fs = constructorField.toCharArray();
|
||||||
@@ -125,7 +128,7 @@ public final class ObjectDecoder<R extends Reader, T> implements Decodeable<R, T
|
|||||||
} catch (NoSuchMethodException ex) {
|
} catch (NoSuchMethodException ex) {
|
||||||
getter = clazz.getMethod("is" + mn);
|
getter = clazz.getMethod("is" + mn);
|
||||||
}
|
}
|
||||||
Type t = ObjectEncoder.createClassType(getter.getGenericParameterTypes()[0], this.type);
|
Type t = TypeToken.createClassType(getter.getGenericParameterTypes()[0], this.type);
|
||||||
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, getter, null), factory.loadDecoder(t)));
|
list.add(new DeMember(ObjectEncoder.createAttribute(factory, clazz, null, getter, null), factory.loadDecoder(t)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.util.*;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 自定义对象的序列化操作类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -67,7 +68,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
|||||||
if (Modifier.isStatic(field.getModifiers())) continue;
|
if (Modifier.isStatic(field.getModifiers())) continue;
|
||||||
ref = factory.findRef(field);
|
ref = factory.findRef(field);
|
||||||
if (ref != null && ref.ignore()) continue;
|
if (ref != null && ref.ignore()) continue;
|
||||||
Type t = createClassType(field.getGenericType(), this.type);
|
Type t = TypeToken.createClassType(field.getGenericType(), this.type);
|
||||||
list.add(new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t)));
|
list.add(new EnMember(createAttribute(factory, clazz, field, null, null), factory.loadEncoder(t)));
|
||||||
}
|
}
|
||||||
for (final Method method : clazz.getMethods()) {
|
for (final Method method : clazz.getMethods()) {
|
||||||
@@ -77,6 +78,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
|||||||
if (method.getName().length() < 3) continue;
|
if (method.getName().length() < 3) continue;
|
||||||
if (method.getName().equals("getClass")) continue;
|
if (method.getName().equals("getClass")) continue;
|
||||||
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
|
if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue;
|
||||||
|
if (method.getAnnotation(java.beans.Transient.class) != null) continue;
|
||||||
if (method.getParameterTypes().length != 0) continue;
|
if (method.getParameterTypes().length != 0) continue;
|
||||||
if (method.getReturnType() == void.class) continue;
|
if (method.getReturnType() == void.class) continue;
|
||||||
if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
|
if (reversible && (cps == null || !contains(cps, ConvertFactory.readGetSetFieldName(method)))) {
|
||||||
@@ -89,7 +91,7 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
|||||||
}
|
}
|
||||||
ref = factory.findRef(method);
|
ref = factory.findRef(method);
|
||||||
if (ref != null && ref.ignore()) continue;
|
if (ref != null && ref.ignore()) continue;
|
||||||
Type t = createClassType(method.getGenericReturnType(), this.type);
|
Type t = TypeToken.createClassType(method.getGenericReturnType(), this.type);
|
||||||
list.add(new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t)));
|
list.add(new EnMember(createAttribute(factory, clazz, null, method, null), factory.loadEncoder(t)));
|
||||||
}
|
}
|
||||||
this.members = list.toArray(new EnMember[list.size()]);
|
this.members = list.toArray(new EnMember[list.size()]);
|
||||||
@@ -144,42 +146,6 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
|||||||
return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
|
return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
static Type createClassType(final Type type, final Type declaringType0) {
|
|
||||||
if (TypeToken.isClassType(type)) return type;
|
|
||||||
if (type instanceof ParameterizedType) { // e.g. Map<String, String>
|
|
||||||
final ParameterizedType pt = (ParameterizedType) type;
|
|
||||||
final Type[] paramTypes = pt.getActualTypeArguments();
|
|
||||||
for (int i = 0; i < paramTypes.length; i++) {
|
|
||||||
paramTypes[i] = createClassType(paramTypes[i], declaringType0);
|
|
||||||
}
|
|
||||||
return TypeToken.createParameterizedType(pt.getOwnerType(), pt.getRawType(), paramTypes);
|
|
||||||
}
|
|
||||||
Type declaringType = declaringType0;
|
|
||||||
if (declaringType instanceof Class) {
|
|
||||||
do {
|
|
||||||
declaringType = ((Class) declaringType).getGenericSuperclass();
|
|
||||||
if (declaringType == Object.class) return Object.class;
|
|
||||||
} while (declaringType instanceof Class);
|
|
||||||
}
|
|
||||||
//存在通配符则declaringType 必须是 ParameterizedType
|
|
||||||
if (!(declaringType instanceof ParameterizedType)) return Object.class;
|
|
||||||
final ParameterizedType declaringPType = (ParameterizedType) declaringType;
|
|
||||||
final Type[] virTypes = ((Class) declaringPType.getRawType()).getTypeParameters();
|
|
||||||
final Type[] desTypes = declaringPType.getActualTypeArguments();
|
|
||||||
if (type instanceof WildcardType) { // e.g. <? extends Serializable>
|
|
||||||
final WildcardType wt = (WildcardType) type;
|
|
||||||
for (Type f : wt.getUpperBounds()) {
|
|
||||||
for (int i = 0; i < virTypes.length; i++) {
|
|
||||||
if (virTypes[i].equals(f)) return desTypes.length <= i ? Object.class : desTypes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (type instanceof TypeVariable) { // e.g. <? extends E>
|
|
||||||
for (int i = 0; i < virTypes.length; i++) {
|
|
||||||
if (virTypes[i].equals(type)) return desTypes.length <= i ? Object.class : desTypes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
//
|
//
|
||||||
// static Type makeGenericType(final Type type, final Type[] virGenericTypes, final Type[] realGenericTypes) {
|
// static Type makeGenericType(final Type type, final Type[] virGenericTypes, final Type[] realGenericTypes) {
|
||||||
// if (type instanceof Class) { //e.g. String
|
// if (type instanceof Class) { //e.g. String
|
||||||
@@ -228,7 +194,6 @@ public final class ObjectEncoder<W extends Writer, T> implements Encodeable<W, T
|
|||||||
// }
|
// }
|
||||||
// return type;
|
// return type;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
static boolean contains(String[] values, String value) {
|
static boolean contains(String[] values, String value) {
|
||||||
for (String str : values) {
|
for (String str : values) {
|
||||||
if (str.equals(value)) return true;
|
if (str.equals(value)) return true;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.redkale.convert;
|
package org.redkale.convert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 反序列化的数据读取流
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -42,6 +43,7 @@ public abstract class Reader {
|
|||||||
* 读取对象的类名, 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。
|
* 读取对象的类名, 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。
|
||||||
*
|
*
|
||||||
* @param clazz 类名
|
* @param clazz 类名
|
||||||
|
*
|
||||||
* @return 返回字段数
|
* @return 返回字段数
|
||||||
*/
|
*/
|
||||||
public String readObjectB(final Class clazz) {
|
public String readObjectB(final Class clazz) {
|
||||||
@@ -86,6 +88,7 @@ public abstract class Reader {
|
|||||||
* 根据字段读取字段对应的DeMember
|
* 根据字段读取字段对应的DeMember
|
||||||
*
|
*
|
||||||
* @param members DeMember的全量集合
|
* @param members DeMember的全量集合
|
||||||
|
*
|
||||||
* @return 匹配的DeMember
|
* @return 匹配的DeMember
|
||||||
*/
|
*/
|
||||||
public abstract DeMember readFieldName(final DeMember[] members);
|
public abstract DeMember readFieldName(final DeMember[] members);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import java.lang.reflect.ParameterizedType;
|
|||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 简易类的序列化和反序列化操作类 <br>
|
||||||
|
* 能序列化为Boolean、Number或者字符串的类视为简易类 <br>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.redkale.convert;
|
|||||||
import org.redkale.util.Attribute;
|
import org.redkale.util.Attribute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 序列化的数据输出流
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import static org.redkale.convert.Reader.SIGN_NULL;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 以ByteBuffer为数据载体的BsonReader
|
||||||
*
|
*
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ package org.redkale.convert.bson;
|
|||||||
|
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
|
import org.redkale.util.Utility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 以ByteBuffer为数据载体的BsonWriter
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -78,20 +80,14 @@ public class BsonByteBufferWriter extends BsonWriter {
|
|||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
buffer = supplier.get();
|
buffer = supplier.get();
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
this.buffers = Utility.append(this.buffers, buffer);
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
this.index++;
|
this.index++;
|
||||||
}
|
}
|
||||||
int len = buffer.remaining();
|
int len = buffer.remaining();
|
||||||
int size = 0;
|
int size = 0;
|
||||||
while (len < byteLength) {
|
while (len < byteLength) {
|
||||||
buffer = supplier.get();
|
buffer = supplier.get();
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
this.buffers = Utility.append(this.buffers, buffer);
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
len += buffer.remaining();
|
len += buffer.remaining();
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,21 +15,21 @@ import org.redkale.util.*;
|
|||||||
/**
|
/**
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* BSON协议格式:
|
* BSON协议格式:
|
||||||
* 1). 基本数据类型: 直接转换成byte[]
|
* 1) 基本数据类型: 直接转换成byte[]
|
||||||
* 2). SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
|
* 2) SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
|
||||||
* 3). String: length(4 bytes) + byte[](utf8);
|
* 3) String: length(4 bytes) + byte[](utf8);
|
||||||
* 4). 数组: length(4 bytes) + byte[]...
|
* 4) 数组: length(4 bytes) + byte[]...
|
||||||
* 5). Object:
|
* 5) Object:
|
||||||
* 1. realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
|
* 1、 realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
|
||||||
* 2. 空字符串(SmallString)
|
* 2、 空字符串(SmallString)
|
||||||
* 3. SIGN_OBJECTB 标记位,值固定为0xBB (short)
|
* 3、 SIGN_OBJECTB 标记位,值固定为0xBB (short)
|
||||||
* 4. 循环字段值:
|
* 4、 循环字段值:
|
||||||
* 4.1 SIGN_HASNEXT 标记位,值固定为1 (byte)
|
* 4.1 SIGN_HASNEXT 标记位,值固定为1 (byte)
|
||||||
* 4.2 字段类型; 1-9为基本类型和字符串; 101-109为基本类型和字符串的数组; 127为Object
|
* 4.2 字段类型; 1-9为基本类型和字符串; 101-109为基本类型和字符串的数组; 127为Object
|
||||||
* 4.3 字段名 (SmallString)
|
* 4.3 字段名 (SmallString)
|
||||||
* 4.4 字段的值Object
|
* 4.4 字段的值Object
|
||||||
* 5. SIGN_NONEXT 标记位,值固定为0 (byte)
|
* 5、 SIGN_NONEXT 标记位,值固定为0 (byte)
|
||||||
* 6. SIGN_OBJECTE 标记位,值固定为0xEE (short)
|
* 6、 SIGN_OBJECTE 标记位,值固定为0xEE (short)
|
||||||
*
|
*
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ package org.redkale.convert.bson;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import org.redkale.convert.*;
|
import org.redkale.convert.*;
|
||||||
|
import org.redkale.util.AnyValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* BSON的ConvertFactory
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -27,6 +29,9 @@ public final class BsonFactory extends ConvertFactory<BsonReader, BsonWriter> {
|
|||||||
static {
|
static {
|
||||||
instance.register(Serializable.class, objectDecoder);
|
instance.register(Serializable.class, objectDecoder);
|
||||||
instance.register(Serializable.class, objectEncoder);
|
instance.register(Serializable.class, objectEncoder);
|
||||||
|
|
||||||
|
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class));
|
||||||
|
instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private BsonFactory(BsonFactory parent, boolean tiny) {
|
private BsonFactory(BsonFactory parent, boolean tiny) {
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.redkale.convert.bson;
|
package org.redkale.convert.bson;
|
||||||
|
|
||||||
import java.util.function.*;
|
|
||||||
import org.redkale.convert.*;
|
import org.redkale.convert.*;
|
||||||
import static org.redkale.convert.Reader.SIGN_NULL;
|
import static org.redkale.convert.Reader.SIGN_NULL;
|
||||||
import org.redkale.convert.ext.*;
|
import org.redkale.convert.ext.*;
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* BSON数据源
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -42,19 +42,7 @@ public class BsonReader extends Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ObjectPool<BsonReader> createPool(int max) {
|
public static ObjectPool<BsonReader> createPool(int max) {
|
||||||
return new ObjectPool<BsonReader>(max, new Creator<BsonReader>() {
|
return new ObjectPool<>(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle());
|
||||||
|
|
||||||
@Override
|
|
||||||
public BsonReader create(Object... params) {
|
|
||||||
return new BsonReader();
|
|
||||||
}
|
|
||||||
}, null, new Predicate<BsonReader>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(BsonReader t) {
|
|
||||||
return t.recycle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BsonReader(byte[] bytes) {
|
public BsonReader(byte[] bytes) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
package org.redkale.convert.bson;
|
package org.redkale.convert.bson;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.function.Predicate;
|
|
||||||
import org.redkale.convert.*;
|
import org.redkale.convert.*;
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
@@ -28,19 +27,7 @@ public class BsonWriter extends Writer {
|
|||||||
protected boolean tiny;
|
protected boolean tiny;
|
||||||
|
|
||||||
public static ObjectPool<BsonWriter> createPool(int max) {
|
public static ObjectPool<BsonWriter> createPool(int max) {
|
||||||
return new ObjectPool<BsonWriter>(max, new Creator<BsonWriter>() {
|
return new ObjectPool<>(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle());
|
||||||
|
|
||||||
@Override
|
|
||||||
public BsonWriter create(Object... params) {
|
|
||||||
return new BsonWriter();
|
|
||||||
}
|
|
||||||
}, null, new Predicate<BsonWriter>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean test(BsonWriter t) {
|
|
||||||
return t.recycle();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] toArray() {
|
public byte[] toArray() {
|
||||||
@@ -82,6 +69,7 @@ public class BsonWriter extends Writer {
|
|||||||
* 扩充指定长度的缓冲区
|
* 扩充指定长度的缓冲区
|
||||||
*
|
*
|
||||||
* @param len 扩容长度
|
* @param len 扩容长度
|
||||||
|
*
|
||||||
* @return 固定0
|
* @return 固定0
|
||||||
*/
|
*/
|
||||||
protected int expand(int len) {
|
protected int expand(int len) {
|
||||||
@@ -248,11 +236,11 @@ public class BsonWriter extends Writer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char[] chars = Utility.charArray(value);
|
char[] chars = Utility.charArray(value);
|
||||||
if (chars.length > 255) throw new ConvertException("'" + value + "' has very long length");
|
if (chars.length > 255) throw new ConvertException("'" + value + "' have very long length");
|
||||||
byte[] bytes = new byte[chars.length + 1];
|
byte[] bytes = new byte[chars.length + 1];
|
||||||
bytes[0] = (byte) chars.length;
|
bytes[0] = (byte) chars.length;
|
||||||
for (int i = 0; i < chars.length; i++) {
|
for (int i = 0; i < chars.length; i++) {
|
||||||
if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' has double-word");
|
if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' have double-word");
|
||||||
bytes[i + 1] = (byte) chars[i];
|
bytes[i + 1] = (byte) chars[i];
|
||||||
}
|
}
|
||||||
writeTo(bytes);
|
writeTo(bytes);
|
||||||
|
|||||||
36
src/org/redkale/convert/ext/AsyncHandlerSimpledCoder.java
Normal file
36
src/org/redkale/convert/ext/AsyncHandlerSimpledCoder.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.redkale.convert.ext;
|
||||||
|
|
||||||
|
import org.redkale.convert.*;
|
||||||
|
import org.redkale.util.AsyncHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AsyncHandlerSimpledCoder 的SimpledCoder实现, 只输出null
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
|
*
|
||||||
|
* @author zhangjx
|
||||||
|
* @param <R> Reader输入的子类型
|
||||||
|
* @param <W> Writer输出的子类型
|
||||||
|
*/
|
||||||
|
public final class AsyncHandlerSimpledCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, AsyncHandler> {
|
||||||
|
|
||||||
|
public static final AsyncHandlerSimpledCoder instance = new AsyncHandlerSimpledCoder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void convertTo(W out, AsyncHandler value) {
|
||||||
|
out.writeObjectNull(AsyncHandler.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AsyncHandler convertFrom(R in) {
|
||||||
|
in.readObjectB(AsyncHandler.class);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ public class TypeSimpledCoder<R extends Reader, W extends Writer> extends Simple
|
|||||||
if (str == null) return null;
|
if (str == null) return null;
|
||||||
try {
|
try {
|
||||||
return Class.forName(str);
|
return Class.forName(str);
|
||||||
} catch (Exception e) {
|
} catch (Throwable e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.redkale.convert.*;
|
|||||||
import static org.redkale.convert.Reader.*;
|
import static org.redkale.convert.Reader.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 以ByteBuffer为数据载体的JsonReader <br>
|
||||||
*
|
*
|
||||||
* 只支持UTF-8格式
|
* 只支持UTF-8格式
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.redkale.convert.*;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 以ByteBuffer为数据载体的JsonWriter
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -84,20 +85,14 @@ public class JsonByteBufferWriter extends JsonWriter {
|
|||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
buffer = supplier.get();
|
buffer = supplier.get();
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
this.buffers = Utility.append(this.buffers, buffer);
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
this.index++;
|
this.index++;
|
||||||
}
|
}
|
||||||
int len = buffer.remaining();
|
int len = buffer.remaining();
|
||||||
int size = 0;
|
int size = 0;
|
||||||
while (len < byteLength) {
|
while (len < byteLength) {
|
||||||
buffer = supplier.get();
|
buffer = supplier.get();
|
||||||
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
|
this.buffers = Utility.append(this.buffers, buffer);
|
||||||
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
|
|
||||||
bufs[this.buffers.length] = buffer;
|
|
||||||
this.buffers = bufs;
|
|
||||||
len += buffer.remaining();
|
len += buffer.remaining();
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import java.math.BigInteger;
|
|||||||
import java.net.*;
|
import java.net.*;
|
||||||
import org.redkale.convert.*;
|
import org.redkale.convert.*;
|
||||||
import org.redkale.convert.ext.*;
|
import org.redkale.convert.ext.*;
|
||||||
import org.redkale.util.DLong;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* JSON的ConvertFactory
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -29,6 +30,9 @@ public final class JsonFactory extends ConvertFactory<JsonReader, JsonWriter> {
|
|||||||
instance.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance);
|
instance.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance);
|
||||||
instance.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance);
|
instance.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance);
|
||||||
instance.register(Serializable.class, instance.loadEncoder(Object.class));
|
instance.register(Serializable.class, instance.loadEncoder(Object.class));
|
||||||
|
|
||||||
|
instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class));
|
||||||
|
instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonFactory(JsonFactory parent, boolean tiny) {
|
private JsonFactory(JsonFactory parent, boolean tiny) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import static org.redkale.convert.Reader.*;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* JSON数据源
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -17,42 +17,60 @@ import org.redkale.util.*;
|
|||||||
import org.redkale.watch.*;
|
import org.redkale.watch.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 服务器上下文对象
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
public class Context {
|
public class Context {
|
||||||
|
|
||||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||||
|
|
||||||
|
//服务启动时间
|
||||||
protected final long serverStartTime;
|
protected final long serverStartTime;
|
||||||
|
|
||||||
|
//Server的线程池
|
||||||
protected final ExecutorService executor;
|
protected final ExecutorService executor;
|
||||||
|
|
||||||
|
//ByteBuffer的容量,默认8K
|
||||||
protected final int bufferCapacity;
|
protected final int bufferCapacity;
|
||||||
|
|
||||||
|
//ByteBuffer对象池
|
||||||
protected final ObjectPool<ByteBuffer> bufferPool;
|
protected final ObjectPool<ByteBuffer> bufferPool;
|
||||||
|
|
||||||
|
//Response对象池
|
||||||
protected final ObjectPool<Response> responsePool;
|
protected final ObjectPool<Response> responsePool;
|
||||||
|
|
||||||
|
//服务的根Servlet
|
||||||
protected final PrepareServlet prepare;
|
protected final PrepareServlet prepare;
|
||||||
|
|
||||||
|
//服务的监听地址
|
||||||
private final InetSocketAddress address;
|
private final InetSocketAddress address;
|
||||||
|
|
||||||
|
//字符集
|
||||||
protected final Charset charset;
|
protected final Charset charset;
|
||||||
|
|
||||||
|
//请求内容的大小上限, 默认64K
|
||||||
protected final int maxbody;
|
protected final int maxbody;
|
||||||
|
|
||||||
|
//IO读取的超时时间
|
||||||
protected final int readTimeoutSecond;
|
protected final int readTimeoutSecond;
|
||||||
|
|
||||||
|
//IO写入的超时时间
|
||||||
protected final int writeTimeoutSecond;
|
protected final int writeTimeoutSecond;
|
||||||
|
|
||||||
|
//日志Logger
|
||||||
protected final Logger logger;
|
protected final Logger logger;
|
||||||
|
|
||||||
|
//BSON操作工厂
|
||||||
protected final BsonFactory bsonFactory;
|
protected final BsonFactory bsonFactory;
|
||||||
|
|
||||||
|
//JSON操作工厂
|
||||||
protected final JsonFactory jsonFactory;
|
protected final JsonFactory jsonFactory;
|
||||||
|
|
||||||
|
//监控对象
|
||||||
protected final WatchFactory watch;
|
protected final WatchFactory watch;
|
||||||
|
|
||||||
public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
|
public Context(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool<ByteBuffer> bufferPool, ObjectPool<Response> responsePool,
|
||||||
@@ -91,8 +109,12 @@ public class Context {
|
|||||||
return charset;
|
return charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submit(Runnable r) {
|
public Future<?> submitAsync(Runnable r) {
|
||||||
executor.submit(r);
|
return executor.submit(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runAsync(Runnable r) {
|
||||||
|
executor.execute(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBufferCapacity() {
|
public int getBufferCapacity() {
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ import java.util.logging.*;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 根Servlet的处理逻辑类
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ import java.util.logging.*;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 根Servlet, 一个Server只能存在一个根Servlet
|
||||||
|
*
|
||||||
|
* 用于分发Request请求
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -31,9 +34,33 @@ public abstract class PrepareServlet<K extends Serializable, C extends Context,
|
|||||||
|
|
||||||
protected final AtomicLong illRequestCounter = new AtomicLong(); //错误请求次数
|
protected final AtomicLong illRequestCounter = new AtomicLong(); //错误请求次数
|
||||||
|
|
||||||
protected final Set<S> servlets = new HashSet<>();
|
private final Object lock1 = new Object();
|
||||||
|
|
||||||
protected final Map<K, S> mappings = new HashMap<>();
|
private Set<S> servlets = new HashSet<>();
|
||||||
|
|
||||||
|
private final Object lock2 = new Object();
|
||||||
|
|
||||||
|
private Map<K, S> mappings = new HashMap<>();
|
||||||
|
|
||||||
|
protected void putServlet(S servlet) {
|
||||||
|
synchronized (lock1) {
|
||||||
|
Set<S> newservlets = new HashSet<>(servlets);
|
||||||
|
newservlets.add(servlet);
|
||||||
|
this.servlets = newservlets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void putMapping(K key, S value) {
|
||||||
|
synchronized (lock2) {
|
||||||
|
Map<K, S> newmappings = new HashMap<>(mappings);
|
||||||
|
newmappings.put(key, value);
|
||||||
|
this.mappings = newmappings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected S mappingServlet(K key) {
|
||||||
|
return mappings.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
|
public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.*;
|
|||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 协议底层Server
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
* <p> 详情见: https://redkale.org
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
@@ -91,7 +92,7 @@ public abstract class ProtocolServer {
|
|||||||
SocketAddress address = serchannel.receive(buffer);
|
SocketAddress address = serchannel.receive(buffer);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
AsyncConnection conn = AsyncConnection.create(serchannel, address, false, readTimeoutSecond, writeTimeoutSecond);
|
AsyncConnection conn = AsyncConnection.create(serchannel, address, false, readTimeoutSecond, writeTimeoutSecond);
|
||||||
context.submit(new PrepareRunner(context, conn, buffer));
|
context.runAsync(new PrepareRunner(context, conn, buffer));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
context.offerBuffer(buffer);
|
context.offerBuffer(buffer);
|
||||||
}
|
}
|
||||||
@@ -159,7 +160,7 @@ public abstract class ProtocolServer {
|
|||||||
@Override
|
@Override
|
||||||
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
|
public void completed(final AsynchronousSocketChannel channel, Void attachment) {
|
||||||
serchannel.accept(null, this);
|
serchannel.accept(null, this);
|
||||||
context.submit(new PrepareRunner(context, AsyncConnection.create(channel, null, context.readTimeoutSecond, context.writeTimeoutSecond), null));
|
context.submitAsync(new PrepareRunner(context, AsyncConnection.create(channel, null, context.readTimeoutSecond, context.writeTimeoutSecond), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.redkale.convert.bson.BsonConvert;
|
|||||||
import org.redkale.convert.json.JsonConvert;
|
import org.redkale.convert.json.JsonConvert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 协议请求对象
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.nio.channels.CompletionHandler;
|
|||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 协议响应对象
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -29,6 +30,8 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
|||||||
|
|
||||||
private boolean inited = true;
|
private boolean inited = true;
|
||||||
|
|
||||||
|
protected Object output; //输出的结果对象
|
||||||
|
|
||||||
protected BiConsumer<R, Response<C, R>> recycleListener;
|
protected BiConsumer<R, Response<C, R>> recycleListener;
|
||||||
|
|
||||||
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
|
private final CompletionHandler finishHandler = new CompletionHandler<Integer, ByteBuffer>() {
|
||||||
@@ -54,29 +57,26 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
|||||||
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
|
private final CompletionHandler finishHandler2 = new CompletionHandler<Integer, ByteBuffer[]>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void completed(Integer result, ByteBuffer[] attachments) {
|
public void completed(final Integer result, final ByteBuffer[] attachments) {
|
||||||
int index = -1;
|
int index = -1;
|
||||||
for (int i = 0; i < attachments.length; i++) {
|
for (int i = 0; i < attachments.length; i++) {
|
||||||
if (attachments[i].hasRemaining()) {
|
if (attachments[i].hasRemaining()) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
context.offerBuffer(attachments[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (index == 0) {
|
if (index >= 0) {
|
||||||
channel.write(attachments, attachments, this);
|
channel.write(attachments, index, attachments.length - index, attachments, this);
|
||||||
} else if (index > 0) {
|
|
||||||
ByteBuffer[] newattachs = new ByteBuffer[attachments.length - index];
|
|
||||||
System.arraycopy(attachments, index, newattachs, 0, newattachs.length);
|
|
||||||
channel.write(newattachs, newattachs, this);
|
|
||||||
} else {
|
} else {
|
||||||
|
for (ByteBuffer attachment : attachments) {
|
||||||
|
context.offerBuffer(attachment);
|
||||||
|
}
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failed(Throwable exc, ByteBuffer[] attachments) {
|
public void failed(Throwable exc, final ByteBuffer[] attachments) {
|
||||||
for (ByteBuffer attachment : attachments) {
|
for (ByteBuffer attachment : attachments) {
|
||||||
context.offerBuffer(attachment);
|
context.offerBuffer(attachment);
|
||||||
}
|
}
|
||||||
@@ -113,10 +113,11 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
|||||||
}
|
}
|
||||||
recycleListener = null;
|
recycleListener = null;
|
||||||
}
|
}
|
||||||
|
this.output = null;
|
||||||
request.recycle();
|
request.recycle();
|
||||||
if (channel != null) {
|
if (channel != null) {
|
||||||
if (keepAlive) {
|
if (keepAlive) {
|
||||||
this.context.submit(new PrepareRunner(context, channel, null));
|
this.context.runAsync(new PrepareRunner(context, channel, null));
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
if (channel.isOpen()) channel.close();
|
if (channel.isOpen()) channel.close();
|
||||||
@@ -143,30 +144,60 @@ public abstract class Response<C extends Context, R extends Request<C>> {
|
|||||||
this.recycleListener = recycleListener;
|
this.recycleListener = recycleListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getOutput() {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已关闭
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public boolean isClosed() {
|
||||||
|
return !this.inited;
|
||||||
|
}
|
||||||
|
|
||||||
public void finish() {
|
public void finish() {
|
||||||
this.finish(false);
|
this.finish(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish(boolean kill) {
|
public void finish(boolean kill) {
|
||||||
|
if (!this.inited) return; //避免重复关闭
|
||||||
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
|
//System.println("耗时: " + (System.currentTimeMillis() - request.createtime));
|
||||||
if (kill) refuseAlive();
|
if (kill) refuseAlive();
|
||||||
this.context.responsePool.offer(this);
|
this.context.responsePool.offer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void finish(final byte[] bs) {
|
||||||
|
if (!this.inited) return; //避免重复关闭
|
||||||
|
if (this.context.bufferCapacity == bs.length) {
|
||||||
|
ByteBuffer buffer = this.context.pollBuffer();
|
||||||
|
buffer.put(bs);
|
||||||
|
buffer.flip();
|
||||||
|
this.finish(buffer);
|
||||||
|
} else {
|
||||||
|
this.finish(ByteBuffer.wrap(bs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void finish(ByteBuffer buffer) {
|
public void finish(ByteBuffer buffer) {
|
||||||
|
if (!this.inited) return; //避免重复关闭
|
||||||
this.channel.write(buffer, buffer, finishHandler);
|
this.channel.write(buffer, buffer, finishHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish(boolean kill, ByteBuffer buffer) {
|
public void finish(boolean kill, ByteBuffer buffer) {
|
||||||
|
if (!this.inited) return; //避免重复关闭
|
||||||
if (kill) refuseAlive();
|
if (kill) refuseAlive();
|
||||||
this.channel.write(buffer, buffer, finishHandler);
|
this.channel.write(buffer, buffer, finishHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish(ByteBuffer... buffers) {
|
public void finish(ByteBuffer... buffers) {
|
||||||
|
if (!this.inited) return; //避免重复关闭
|
||||||
this.channel.write(buffers, buffers, finishHandler2);
|
this.channel.write(buffers, buffers, finishHandler2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish(boolean kill, ByteBuffer... buffers) {
|
public void finish(boolean kill, ByteBuffer... buffers) {
|
||||||
|
if (!this.inited) return; //避免重复关闭
|
||||||
if (kill) refuseAlive();
|
if (kill) refuseAlive();
|
||||||
this.channel.write(buffers, buffers, finishHandler2);
|
this.channel.write(buffers, buffers, finishHandler2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.redkale.util.AnyValue;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 协议请求处理类
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public final class Transport {
|
|||||||
|
|
||||||
protected final String name; //即<group>的name属性
|
protected final String name; //即<group>的name属性
|
||||||
|
|
||||||
protected final String kind; //即<group>的kind属性
|
protected final String subprotocol; //即<group>的subprotocol属性
|
||||||
|
|
||||||
protected final boolean tcp;
|
protected final boolean tcp;
|
||||||
|
|
||||||
@@ -62,16 +62,16 @@ public final class Transport {
|
|||||||
|
|
||||||
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
|
protected final ConcurrentHashMap<SocketAddress, BlockingQueue<AsyncConnection>> connPool = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public Transport(String name, WatchFactory watch, String kind, final ObjectPool<ByteBuffer> transportBufferPool,
|
public Transport(String name, WatchFactory watch, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||||
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||||
this(name, DEFAULT_PROTOCOL, watch, kind, transportBufferPool, transportChannelGroup, clientAddress, addresses);
|
this(name, DEFAULT_PROTOCOL, watch, subprotocol, transportBufferPool, transportChannelGroup, clientAddress, addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transport(String name, String protocol, WatchFactory watch, String kind, final ObjectPool<ByteBuffer> transportBufferPool,
|
public Transport(String name, String protocol, WatchFactory watch, String subprotocol, final ObjectPool<ByteBuffer> transportBufferPool,
|
||||||
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
final AsynchronousChannelGroup transportChannelGroup, final InetSocketAddress clientAddress, final Collection<InetSocketAddress> addresses) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.watch = watch;
|
this.watch = watch;
|
||||||
this.kind = kind == null ? "" : kind.trim();
|
this.subprotocol = subprotocol == null ? "" : subprotocol.trim();
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
this.tcp = "TCP".equalsIgnoreCase(protocol);
|
this.tcp = "TCP".equalsIgnoreCase(protocol);
|
||||||
this.group = transportChannelGroup;
|
this.group = transportChannelGroup;
|
||||||
@@ -83,14 +83,17 @@ public final class Transport {
|
|||||||
public Transport(final Collection<Transport> transports) {
|
public Transport(final Collection<Transport> transports) {
|
||||||
Transport first = null;
|
Transport first = null;
|
||||||
List<String> tmpgroup = new ArrayList<>();
|
List<String> tmpgroup = new ArrayList<>();
|
||||||
|
if (transports != null) {
|
||||||
for (Transport t : transports) {
|
for (Transport t : transports) {
|
||||||
if (first == null) first = t;
|
if (first == null) first = t;
|
||||||
tmpgroup.add(t.name);
|
tmpgroup.add(t.name);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (first == null) throw new NullPointerException("Collection<Transport> is null or empty");
|
||||||
//必须按字母排列顺序确保,相同内容的transport列表组合的name相同,而不会因为list的顺序不同产生不同的name
|
//必须按字母排列顺序确保,相同内容的transport列表组合的name相同,而不会因为list的顺序不同产生不同的name
|
||||||
this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";"));
|
this.name = tmpgroup.stream().sorted().collect(Collectors.joining(";"));
|
||||||
this.watch = first.watch;
|
this.watch = first.watch;
|
||||||
this.kind = first.kind;
|
this.subprotocol = first.subprotocol;
|
||||||
this.protocol = first.protocol;
|
this.protocol = first.protocol;
|
||||||
this.tcp = "TCP".equalsIgnoreCase(first.protocol);
|
this.tcp = "TCP".equalsIgnoreCase(first.protocol);
|
||||||
this.group = first.group;
|
this.group = first.group;
|
||||||
@@ -118,8 +121,8 @@ public final class Transport {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKind() {
|
public String getSubprotocol() {
|
||||||
return kind;
|
return subprotocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
@@ -162,7 +165,7 @@ public final class Transport {
|
|||||||
public AsyncConnection pollConnection(SocketAddress addr) {
|
public AsyncConnection pollConnection(SocketAddress addr) {
|
||||||
if (addr == null && remoteAddres.length == 1) addr = remoteAddres[0];
|
if (addr == null && remoteAddres.length == 1) addr = remoteAddres[0];
|
||||||
final boolean rand = addr == null;
|
final boolean rand = addr == null;
|
||||||
if (rand && remoteAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") has no remoteAddress list");
|
if (rand && remoteAddres.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list");
|
||||||
try {
|
try {
|
||||||
if (tcp) {
|
if (tcp) {
|
||||||
AsynchronousSocketChannel channel = null;
|
AsynchronousSocketChannel channel = null;
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ package org.redkale.net;
|
|||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 协议处理的自定义线程类
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
public class WorkThread extends Thread {
|
public class WorkThread extends Thread {
|
||||||
@@ -22,8 +25,8 @@ public class WorkThread extends Thread {
|
|||||||
this.setDaemon(true);
|
this.setDaemon(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submit(Runnable runner) {
|
public void runAsync(Runnable runner) {
|
||||||
executor.submit(runner);
|
executor.execute(runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExecutorService getExecutor() {
|
public ExecutorService getExecutor() {
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ public class DefaultRestServlet extends RestHttpServlet<Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
|
public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException {
|
||||||
return true;
|
next.execute(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配合 @WebAction 使用。
|
* 配合 @WebMapping 使用。
|
||||||
* 用于对@WebAction方法中参数描述
|
* 用于对@WebMapping方法中参数描述
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -89,6 +89,8 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
ParamSourceType src() default ParamSourceType.PARAMETER; //参数来源类型
|
ParamSourceType src() default ParamSourceType.PARAMETER; //参数来源类型
|
||||||
|
|
||||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||||
|
|
||||||
|
boolean required() default true; //参数是否必传
|
||||||
}
|
}
|
||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@@ -99,6 +101,34 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
WebParam[] value();
|
WebParam[] value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 WebMapping 替代。
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
|
*
|
||||||
|
* @author zhangjx
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
@Documented
|
||||||
|
@Target({METHOD})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
protected @interface WebAction {
|
||||||
|
|
||||||
|
int actionid() default 0;
|
||||||
|
|
||||||
|
String url();
|
||||||
|
|
||||||
|
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法
|
||||||
|
|
||||||
|
String comment() default ""; //备注描述
|
||||||
|
|
||||||
|
boolean inherited() default true; //是否能被继承, 当 HttpBaseServlet 被继承后该方法是否能被子类继承
|
||||||
|
|
||||||
|
String result() default "Object"; //输出结果的数据类型
|
||||||
|
|
||||||
|
Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配合 HttpBaseServlet 使用。
|
* 配合 HttpBaseServlet 使用。
|
||||||
* 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式
|
* 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式
|
||||||
@@ -111,7 +141,7 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
@Documented
|
@Documented
|
||||||
@Target({METHOD})
|
@Target({METHOD})
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
protected @interface WebAction {
|
protected @interface WebMapping {
|
||||||
|
|
||||||
int actionid() default 0;
|
int actionid() default 0;
|
||||||
|
|
||||||
@@ -121,13 +151,16 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
|
|
||||||
String comment() default ""; //备注描述
|
String comment() default ""; //备注描述
|
||||||
|
|
||||||
|
boolean inherited() default true; //是否能被继承, 当 HttpBaseServlet 被继承后该方法是否能被子类继承
|
||||||
|
|
||||||
String result() default "Object"; //输出结果的数据类型
|
String result() default "Object"; //输出结果的数据类型
|
||||||
|
|
||||||
|
Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配合 HttpBaseServlet 使用。
|
* 配合 HttpBaseServlet 使用。
|
||||||
* 当标记为 @HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值timeout=15秒)。
|
* 当标记为 @HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值 seconds=15秒)。
|
||||||
* 通常情况下 @HttpCacheable 需要与 @AuthIgnore 一起使用,没有标记@AuthIgnore的方法一般输出的结果与当前用户信息有关。
|
* 通常情况下 @HttpCacheable 需要与 @AuthIgnore 一起使用,没有标记@AuthIgnore的方法一般输出的结果与当前用户信息有关。
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
@@ -145,29 +178,18 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
*
|
*
|
||||||
* @return 超时秒数
|
* @return 超时秒数
|
||||||
*/
|
*/
|
||||||
int timeout() default 15;
|
int seconds() default 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map.Entry<String, Entry>[] actions;
|
private Map.Entry<String, Entry>[] mappings;
|
||||||
|
|
||||||
public boolean preExecute(HttpRequest request, HttpResponse response) throws IOException {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private final HttpServlet authSuccessServlet = new HttpServlet() {
|
||||||
@Override
|
@Override
|
||||||
public final void execute(HttpRequest request, HttpResponse response) throws IOException {
|
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||||
if (!preExecute(request, response)) return;
|
Entry entry = (Entry) request.attachment;
|
||||||
for (Map.Entry<String, Entry> en : actions) {
|
if (entry.cacheseconds > 0) {//有缓存设置
|
||||||
if (request.getRequestURI().startsWith(en.getKey())) {
|
|
||||||
Entry entry = en.getValue();
|
|
||||||
if (!entry.checkMethod(request.getMethod())) {
|
|
||||||
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (entry.ignore || authenticate(entry.moduleid, entry.actionid, request, response)) {
|
|
||||||
if (entry.cachetimeout > 0) {//有缓存设置
|
|
||||||
CacheEntry ce = entry.cache.get(request.getRequestURI());
|
CacheEntry ce = entry.cache.get(request.getRequestURI());
|
||||||
if (ce != null && ce.time + entry.cachetimeout > System.currentTimeMillis()) { //缓存有效
|
if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效
|
||||||
response.setStatus(ce.status);
|
response.setStatus(ce.status);
|
||||||
response.setContentType(ce.contentType);
|
response.setContentType(ce.contentType);
|
||||||
response.finish(ce.getBuffers());
|
response.finish(ce.getBuffers());
|
||||||
@@ -177,31 +199,125 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
entry.servlet.execute(request, response);
|
entry.servlet.execute(request, response);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final HttpServlet preSuccessServlet = new HttpServlet() {
|
||||||
|
@Override
|
||||||
|
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||||
|
for (Map.Entry<String, Entry> en : mappings) {
|
||||||
|
if (request.getRequestURI().startsWith(en.getKey())) {
|
||||||
|
Entry entry = en.getValue();
|
||||||
|
if (!entry.checkMethod(request.getMethod())) {
|
||||||
|
response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request.attachment = entry;
|
||||||
|
if (entry.ignore) {
|
||||||
|
authSuccessServlet.execute(request, response);
|
||||||
|
} else {
|
||||||
|
authenticate(entry.moduleid, entry.actionid, request, response, authSuccessServlet);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
|
throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")");
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 预执行方法,在execute方法之前运行,通常用于常规统计或基础检测,例如 : <br>
|
||||||
|
* <blockquote><pre>
|
||||||
|
* @Override
|
||||||
|
* public void preExecute(final HttpRequest request, final HttpResponse response, HttpServlet next) throws IOException {
|
||||||
|
* if (finer) response.setRecycleListener((req, resp) -> { //记录处理时间比较长的请求
|
||||||
|
* long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
|
||||||
|
* if (e > 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
|
||||||
|
* });
|
||||||
|
* next.execute(request, response);
|
||||||
|
* }
|
||||||
|
* </pre></blockquote>
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param request HttpRequest
|
||||||
|
* @param response HttpResponse
|
||||||
|
* @param next HttpServlet
|
||||||
|
*
|
||||||
|
* @throws IOException IOException
|
||||||
|
*/
|
||||||
|
public void preExecute(HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
|
||||||
|
next.execute(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException 代替
|
||||||
|
*
|
||||||
|
* @param moduleid int
|
||||||
|
* @param actionid int
|
||||||
|
* @param request HttpRequest
|
||||||
|
* @param response HttpResponse
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
* @throws IOException IOException
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 用户登录或权限验证, 没有注解为@AuthIgnore 的方法会执行authenticate方法, 若验证成功则必须调用next.execute(request, response);进行下一步操作, 例如: <br>
|
||||||
|
* <blockquote><pre>
|
||||||
|
* @Override
|
||||||
|
* public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException {
|
||||||
|
* UserInfo info = currentUser(request);
|
||||||
|
* if (info == null) {
|
||||||
|
* response.finishJson(RET_UNLOGIN);
|
||||||
|
* return;
|
||||||
|
* } else if (!info.checkAuth(module, actionid)) {
|
||||||
|
* response.finishJson(RET_AUTHILLEGAL);
|
||||||
|
* return;
|
||||||
|
* }
|
||||||
|
* next.execute(request, response);
|
||||||
|
* }
|
||||||
|
* </pre></blockquote>
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param moduleid 模块ID,来自@WebServlet.moduleid()
|
||||||
|
* @param actionid 操作ID,来自@WebMapping.actionid()
|
||||||
|
* @param request HttpRequest
|
||||||
|
* @param response HttpResponse
|
||||||
|
* @param next HttpServlet
|
||||||
|
*
|
||||||
|
* @throws IOException IOException
|
||||||
|
*/
|
||||||
|
public abstract void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||||
|
preExecute(request, response, preSuccessServlet);
|
||||||
|
}
|
||||||
|
|
||||||
public final void preInit(HttpContext context, AnyValue config) {
|
public final void preInit(HttpContext context, AnyValue config) {
|
||||||
String path = _prefix == null ? "" : _prefix;
|
String path = _prefix == null ? "" : _prefix;
|
||||||
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
|
WebServlet ws = this.getClass().getAnnotation(WebServlet.class);
|
||||||
if (ws != null && !ws.repair()) path = "";
|
if (ws != null && !ws.repair()) path = "";
|
||||||
HashMap<String, Entry> map = load();
|
HashMap<String, Entry> map = load();
|
||||||
this.actions = new Map.Entry[map.size()];
|
this.mappings = new Map.Entry[map.size()];
|
||||||
int i = -1;
|
int i = -1;
|
||||||
for (Map.Entry<String, Entry> en : map.entrySet()) {
|
for (Map.Entry<String, Entry> en : map.entrySet()) {
|
||||||
actions[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
|
mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue());
|
||||||
}
|
}
|
||||||
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
|
//必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query
|
||||||
Arrays.sort(actions, (o1, o2) -> o2.getKey().compareTo(o1.getKey()));
|
Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void postDestroy(HttpContext context, AnyValue config) {
|
public final void postDestroy(HttpContext context, AnyValue config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response) throws IOException;
|
|
||||||
|
|
||||||
protected void setHeader(HttpRequest request, String name, Serializable value) {
|
protected void setHeader(HttpRequest request, String name, Serializable value) {
|
||||||
request.header.setValue(name, String.valueOf(value));
|
request.header.setValue(name, String.valueOf(value));
|
||||||
}
|
}
|
||||||
@@ -219,8 +335,12 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
|
WebServlet module = this.getClass().getAnnotation(WebServlet.class);
|
||||||
final int serviceid = module == null ? 0 : module.moduleid();
|
final int serviceid = module == null ? 0 : module.moduleid();
|
||||||
final HashMap<String, Entry> map = new HashMap<>();
|
final HashMap<String, Entry> map = new HashMap<>();
|
||||||
Set<String> nameset = new HashSet<>();
|
HashMap<String, Class> nameset = new HashMap<>();
|
||||||
for (final Method method : this.getClass().getMethods()) {
|
final Class selfClz = this.getClass();
|
||||||
|
Class clz = this.getClass();
|
||||||
|
do {
|
||||||
|
if (Modifier.isAbstract(clz.getModifiers())) break;
|
||||||
|
for (final Method method : clz.getMethods()) {
|
||||||
//-----------------------------------------------
|
//-----------------------------------------------
|
||||||
String methodname = method.getName();
|
String methodname = method.getName();
|
||||||
if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue;
|
if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue;
|
||||||
@@ -233,15 +353,22 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
|
if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue;
|
||||||
//-----------------------------------------------
|
//-----------------------------------------------
|
||||||
|
|
||||||
|
final WebMapping mapping = method.getAnnotation(WebMapping.class);
|
||||||
final WebAction action = method.getAnnotation(WebAction.class);
|
final WebAction action = method.getAnnotation(WebAction.class);
|
||||||
if (action == null) continue;
|
if (mapping == null && action == null) continue;
|
||||||
final int actionid = action.actionid();
|
final boolean inherited = mapping == null ? action.inherited() : mapping.inherited();
|
||||||
final String name = action.url().trim();
|
if (!inherited && selfClz != clz) continue; //忽略不被继承的方法
|
||||||
|
final int actionid = mapping == null ? action.actionid() : mapping.actionid();
|
||||||
if (nameset.contains(name)) throw new RuntimeException(this.getClass().getSimpleName() + " has two same " + WebAction.class.getSimpleName() + "(" + name + ")");
|
final String name = mapping == null ? action.url().trim() : mapping.url().trim();
|
||||||
nameset.add(name);
|
final String[] methods = mapping == null ? action.methods() : mapping.methods();
|
||||||
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, action.methods(), method, createHttpServlet(method)));
|
if (nameset.containsKey(name)) {
|
||||||
|
if (nameset.get(name) != clz) continue;
|
||||||
|
throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + WebMapping.class.getSimpleName() + "(" + name + ")");
|
||||||
}
|
}
|
||||||
|
nameset.put(name, clz);
|
||||||
|
map.put(name, new Entry(typeIgnore, serviceid, actionid, name, methods, method, createHttpServlet(method)));
|
||||||
|
}
|
||||||
|
} while ((clz = clz.getSuperclass()) != HttpBaseServlet.class);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +387,7 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
try {
|
try {
|
||||||
Class.forName(newDynName.replace('/', '.'));
|
Class.forName(newDynName.replace('/', '.'));
|
||||||
newDynName += "_" + (++i);
|
newDynName += "_" + (++i);
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,9 +462,9 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
this.servlet = servlet;
|
this.servlet = servlet;
|
||||||
this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null;
|
this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null;
|
||||||
HttpCacheable hc = method.getAnnotation(HttpCacheable.class);
|
HttpCacheable hc = method.getAnnotation(HttpCacheable.class);
|
||||||
this.cachetimeout = hc == null ? 0 : hc.timeout() * 1000;
|
this.cacheseconds = hc == null ? 0 : hc.seconds() * 1000;
|
||||||
this.cache = cachetimeout > 0 ? new ConcurrentHashMap() : null;
|
this.cache = cacheseconds > 0 ? new ConcurrentHashMap() : null;
|
||||||
this.cacheHandler = cachetimeout > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
|
this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> {
|
||||||
int status = response.getStatus();
|
int status = response.getStatus();
|
||||||
if (status != 200) return null;
|
if (status != 200) return null;
|
||||||
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
|
CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers);
|
||||||
@@ -362,7 +489,7 @@ public abstract class HttpBaseServlet extends HttpServlet {
|
|||||||
|
|
||||||
public final ConcurrentHashMap<String, CacheEntry> cache;
|
public final ConcurrentHashMap<String, CacheEntry> cache;
|
||||||
|
|
||||||
public final int cachetimeout;
|
public final int cacheseconds;
|
||||||
|
|
||||||
public final boolean ignore;
|
public final boolean ignore;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.redkale.util.*;
|
|||||||
import org.redkale.watch.*;
|
import org.redkale.watch.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* HTTP服务的上下文对象
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import org.redkale.util.*;
|
|||||||
import org.redkale.watch.*;
|
import org.redkale.watch.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* HTTP Servlet的总入口,请求在HttpPrepareServlet中进行分流。 <br>
|
||||||
|
* 一个HttpServer只有一个HttpPrepareServlet, 用于管理所有HttpServlet。 <br>
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -35,7 +37,8 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(HttpContext context, AnyValue config) {
|
public void init(HttpContext context, AnyValue config) {
|
||||||
this.servlets.forEach(s -> {
|
Collection<HttpServlet> servlets = getServlets();
|
||||||
|
servlets.forEach(s -> {
|
||||||
if (s instanceof WebSocketServlet) {
|
if (s instanceof WebSocketServlet) {
|
||||||
((WebSocketServlet) s).preInit(context, getServletConf(s));
|
((WebSocketServlet) s).preInit(context, getServletConf(s));
|
||||||
} else if (s instanceof HttpBaseServlet) {
|
} else if (s instanceof HttpBaseServlet) {
|
||||||
@@ -45,7 +48,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
});
|
});
|
||||||
final WatchFactory watch = context.getWatchFactory();
|
final WatchFactory watch = context.getWatchFactory();
|
||||||
if (watch != null) {
|
if (watch != null) {
|
||||||
this.servlets.forEach(s -> {
|
servlets.forEach(s -> {
|
||||||
watch.inject(s);
|
watch.inject(s);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -69,7 +72,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
|
String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName());
|
||||||
try {
|
try {
|
||||||
this.resourceHttpServlet = (HttpServlet) Class.forName(resServlet).newInstance();
|
this.resourceHttpServlet = (HttpServlet) Class.forName(resServlet).newInstance();
|
||||||
} catch (Exception e) {
|
} catch (Throwable e) {
|
||||||
this.resourceHttpServlet = new HttpResourceServlet();
|
this.resourceHttpServlet = new HttpResourceServlet();
|
||||||
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);
|
logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e);
|
||||||
}
|
}
|
||||||
@@ -80,7 +83,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
public void execute(HttpRequest request, HttpResponse response) throws IOException {
|
||||||
try {
|
try {
|
||||||
final String uri = request.getRequestURI();
|
final String uri = request.getRequestURI();
|
||||||
Servlet<HttpContext, HttpRequest, HttpResponse> servlet = this.mappings.isEmpty() ? null : this.mappings.get(uri);
|
Servlet<HttpContext, HttpRequest, HttpResponse> servlet = mappingServlet(uri);
|
||||||
if (servlet == null && this.regArray != null) {
|
if (servlet == null && this.regArray != null) {
|
||||||
for (SimpleEntry<Predicate<String>, HttpServlet> en : regArray) {
|
for (SimpleEntry<Predicate<String>, HttpServlet> en : regArray) {
|
||||||
if (en.getKey().test(uri)) {
|
if (en.getKey().test(uri)) {
|
||||||
@@ -89,6 +92,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//找不到匹配的HttpServlet则使用静态资源HttpResourceServlet
|
||||||
if (servlet == null) servlet = this.resourceHttpServlet;
|
if (servlet == null) servlet = this.resourceHttpServlet;
|
||||||
servlet.execute(request, response);
|
servlet.execute(request, response);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -97,6 +101,14 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加HttpServlet
|
||||||
|
*
|
||||||
|
* @param servlet HttpServlet
|
||||||
|
* @param prefix url前缀
|
||||||
|
* @param conf 配置信息
|
||||||
|
* @param mappings 匹配规则
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappings) {
|
public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappings) {
|
||||||
if (prefix == null) prefix = "";
|
if (prefix == null) prefix = "";
|
||||||
@@ -107,12 +119,12 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix
|
if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronized (allMapStrings) {
|
synchronized (allMapStrings) { //需要整段锁住
|
||||||
for (String mapping : mappings) {
|
for (String mapping : mappings) {
|
||||||
if (mapping == null) continue;
|
if (mapping == null) continue;
|
||||||
if (!prefix.toString().isEmpty()) mapping = prefix + mapping;
|
if (!prefix.toString().isEmpty()) mapping = prefix + mapping;
|
||||||
|
|
||||||
if (contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
if (Utility.contains(mapping, '.', '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式))
|
||||||
if (mapping.charAt(0) != '^') mapping = '^' + mapping;
|
if (mapping.charAt(0) != '^') mapping = '^' + mapping;
|
||||||
if (mapping.endsWith("/*")) {
|
if (mapping.endsWith("/*")) {
|
||||||
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
|
mapping = mapping.substring(0, mapping.length() - 1) + ".*";
|
||||||
@@ -127,7 +139,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
regArray[regArray.length - 1] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
|
regArray[regArray.length - 1] = new SimpleEntry<>(Pattern.compile(mapping).asPredicate(), servlet);
|
||||||
}
|
}
|
||||||
} else if (mapping != null && !mapping.isEmpty()) {
|
} else if (mapping != null && !mapping.isEmpty()) {
|
||||||
super.mappings.put(mapping, servlet);
|
putMapping(mapping, servlet);
|
||||||
}
|
}
|
||||||
if (this.allMapStrings.containsKey(mapping)) {
|
if (this.allMapStrings.containsKey(mapping)) {
|
||||||
Class old = this.allMapStrings.get(mapping);
|
Class old = this.allMapStrings.get(mapping);
|
||||||
@@ -137,26 +149,26 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
}
|
}
|
||||||
setServletConf(servlet, conf);
|
setServletConf(servlet, conf);
|
||||||
servlet._prefix = prefix.toString();
|
servlet._prefix = prefix.toString();
|
||||||
this.servlets.add(servlet);
|
putServlet(servlet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean contains(String string, char... values) {
|
/**
|
||||||
if (string == null) return false;
|
* 设置静态资源HttpServlet
|
||||||
for (char ch : Utility.charArray(string)) {
|
*
|
||||||
for (char ch2 : values) {
|
* @param servlet HttpServlet
|
||||||
if (ch == ch2) return true;
|
*/
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResourceServlet(HttpServlet servlet) {
|
public void setResourceServlet(HttpServlet servlet) {
|
||||||
if (servlet != null) {
|
if (servlet != null) {
|
||||||
this.resourceHttpServlet = servlet;
|
this.resourceHttpServlet = servlet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取静态资源HttpServlet
|
||||||
|
*
|
||||||
|
* @return HttpServlet
|
||||||
|
*/
|
||||||
public HttpServlet getResourceServlet() {
|
public HttpServlet getResourceServlet() {
|
||||||
return this.resourceHttpServlet;
|
return this.resourceHttpServlet;
|
||||||
}
|
}
|
||||||
@@ -164,7 +176,7 @@ public class HttpPrepareServlet extends PrepareServlet<String, HttpContext, Http
|
|||||||
@Override
|
@Override
|
||||||
public void destroy(HttpContext context, AnyValue config) {
|
public void destroy(HttpContext context, AnyValue config) {
|
||||||
this.resourceHttpServlet.destroy(context, config);
|
this.resourceHttpServlet.destroy(context, config);
|
||||||
this.servlets.forEach(s -> {
|
getServlets().forEach(s -> {
|
||||||
s.destroy(context, getServletConf(s));
|
s.destroy(context, getServletConf(s));
|
||||||
if (s instanceof WebSocketServlet) {
|
if (s instanceof WebSocketServlet) {
|
||||||
((WebSocketServlet) s).postDestroy(context, getServletConf(s));
|
((WebSocketServlet) s).postDestroy(context, getServletConf(s));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import org.redkale.util.ByteArray;
|
|||||||
* 例如简单的翻页查询 <br>
|
* 例如简单的翻页查询 <br>
|
||||||
* /pipes/record/query/offset:0/limit:20 <br>
|
* /pipes/record/query/offset:0/limit:20 <br>
|
||||||
* 获取页号: int offset = request.getRequstURIPath("offset:", 0); <br>
|
* 获取页号: int offset = request.getRequstURIPath("offset:", 0); <br>
|
||||||
* 获取行数: int limit = request.getRequstURIPath("limit:", 10);
|
* 获取行数: int limit = request.getRequstURIPath("limit:", 10); <br>
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
@@ -66,6 +66,8 @@ public class HttpRequest extends Request<HttpContext> {
|
|||||||
|
|
||||||
private final String remoteAddrHeader;
|
private final String remoteAddrHeader;
|
||||||
|
|
||||||
|
Object attachment; //供 HttpBaseServlet传递Entry使用
|
||||||
|
|
||||||
public HttpRequest(HttpContext context, String remoteAddrHeader) {
|
public HttpRequest(HttpContext context, String remoteAddrHeader) {
|
||||||
super(context);
|
super(context);
|
||||||
this.remoteAddrHeader = remoteAddrHeader;
|
this.remoteAddrHeader = remoteAddrHeader;
|
||||||
@@ -236,7 +238,7 @@ public class HttpRequest extends Request<HttpContext> {
|
|||||||
* @return 地址
|
* @return 地址
|
||||||
*/
|
*/
|
||||||
public SocketAddress getRemoteAddress() {
|
public SocketAddress getRemoteAddress() {
|
||||||
return this.channel.getRemoteAddress();
|
return this.channel == null || !this.channel.isOpen() ? null : this.channel.getRemoteAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -336,6 +338,8 @@ public class HttpRequest extends Request<HttpContext> {
|
|||||||
this.boundary = false;
|
this.boundary = false;
|
||||||
this.bodyparsed = false;
|
this.bodyparsed = false;
|
||||||
|
|
||||||
|
this.attachment = null;
|
||||||
|
|
||||||
this.header.clear();
|
this.header.clear();
|
||||||
this.params.clear();
|
this.params.clear();
|
||||||
this.array.clear();
|
this.array.clear();
|
||||||
@@ -352,7 +356,7 @@ public class HttpRequest extends Request<HttpContext> {
|
|||||||
public String getSessionid(boolean create) {
|
public String getSessionid(boolean create) {
|
||||||
String sessionid = getCookie(SESSIONID_NAME, null);
|
String sessionid = getCookie(SESSIONID_NAME, null);
|
||||||
if (create && (sessionid == null || sessionid.isEmpty())) {
|
if (create && (sessionid == null || sessionid.isEmpty())) {
|
||||||
sessionid = ((HttpContext) context).createSessionid();
|
sessionid = context.createSessionid();
|
||||||
this.newsessionid = sessionid;
|
this.newsessionid = sessionid;
|
||||||
}
|
}
|
||||||
return sessionid;
|
return sessionid;
|
||||||
@@ -368,6 +372,18 @@ public class HttpRequest extends Request<HttpContext> {
|
|||||||
return newsessionid;
|
return newsessionid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定值更新sessionid
|
||||||
|
*
|
||||||
|
* @param newsessionid 新sessionid值
|
||||||
|
*
|
||||||
|
* @return 新的sessionid值
|
||||||
|
*/
|
||||||
|
public String changeSessionid(String newsessionid) {
|
||||||
|
this.newsessionid = newsessionid == null ? context.createSessionid() : newsessionid.trim();
|
||||||
|
return newsessionid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使sessionid失效
|
* 使sessionid失效
|
||||||
*/
|
*/
|
||||||
@@ -500,6 +516,123 @@ public class HttpRequest extends Request<HttpContext> {
|
|||||||
return requestURI.substring(requestURI.lastIndexOf('/') + 1);
|
return requestURI.substring(requestURI.lastIndexOf('/') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的short值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: short type = request.getRequstURILastPath((short)0); //type = 2
|
||||||
|
*
|
||||||
|
* @param defvalue 默认short值
|
||||||
|
*
|
||||||
|
* @return short值
|
||||||
|
*/
|
||||||
|
public short getRequstURILastPath(short defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
if (val.isEmpty()) return defvalue;
|
||||||
|
return val.isEmpty() ? defvalue : Short.parseShort(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的short值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: short type = request.getRequstURILastPath(16, (short)0); //type = 2
|
||||||
|
*
|
||||||
|
* @param radix 进制数
|
||||||
|
* @param defvalue 默认short值
|
||||||
|
*
|
||||||
|
* @return short值
|
||||||
|
*/
|
||||||
|
public short getRequstURILastPath(int radix, short defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
if (val.isEmpty()) return defvalue;
|
||||||
|
return val.isEmpty() ? defvalue : Short.parseShort(val, radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的int值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: int type = request.getRequstURILastPath(0); //type = 2
|
||||||
|
*
|
||||||
|
* @param defvalue 默认int值
|
||||||
|
*
|
||||||
|
* @return int值
|
||||||
|
*/
|
||||||
|
public int getRequstURILastPath(int defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
return val.isEmpty() ? defvalue : Integer.parseInt(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的int值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: int type = request.getRequstURILastPath(16, 0); //type = 2
|
||||||
|
*
|
||||||
|
* @param radix 进制数
|
||||||
|
* @param defvalue 默认int值
|
||||||
|
*
|
||||||
|
* @return int值
|
||||||
|
*/
|
||||||
|
public int getRequstURILastPath(int radix, int defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
return val.isEmpty() ? defvalue : Integer.parseInt(val, radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的float值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: float type = request.getRequstURILastPath(0.0f); //type = 2.0f
|
||||||
|
*
|
||||||
|
* @param defvalue 默认float值
|
||||||
|
*
|
||||||
|
* @return float值
|
||||||
|
*/
|
||||||
|
public float getRequstURILastPath(float defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
return val.isEmpty() ? defvalue : Float.parseFloat(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的int值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: long type = request.getRequstURILastPath(0L); //type = 2
|
||||||
|
*
|
||||||
|
* @param defvalue 默认long值
|
||||||
|
*
|
||||||
|
* @return long值
|
||||||
|
*/
|
||||||
|
public long getRequstURILastPath(long defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
return val.isEmpty() ? defvalue : Long.parseLong(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的int值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: long type = request.getRequstURILastPath(16, 0L); //type = 2
|
||||||
|
*
|
||||||
|
* @param radix 进制数
|
||||||
|
* @param defvalue 默认long值
|
||||||
|
*
|
||||||
|
* @return long值
|
||||||
|
*/
|
||||||
|
public long getRequstURILastPath(int radix, long defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
return val.isEmpty() ? defvalue : Long.parseLong(val, radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL最后的一个/后面的部分的double值 <br>
|
||||||
|
* 例如请求URL /pipes/record/query/2 <br>
|
||||||
|
* 获取type参数: double type = request.getRequstURILastPath(0.0); //type = 2.0
|
||||||
|
*
|
||||||
|
* @param defvalue 默认double值
|
||||||
|
*
|
||||||
|
* @return double值
|
||||||
|
*/
|
||||||
|
public double getRequstURILastPath(double defvalue) {
|
||||||
|
String val = getRequstURILastPath();
|
||||||
|
return val.isEmpty() ? defvalue : Double.parseDouble(val);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* 从prefix之后截取getRequestURI再对"/"进行分隔
|
* 从prefix之后截取getRequestURI再对"/"进行分隔
|
||||||
@@ -1109,17 +1242,18 @@ public class HttpRequest extends Request<HttpContext> {
|
|||||||
* @return Flipper翻页对象
|
* @return Flipper翻页对象
|
||||||
*/
|
*/
|
||||||
public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit) {
|
public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit) {
|
||||||
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
|
||||||
org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
|
org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name);
|
||||||
if (flipper == null) {
|
if (flipper == null) {
|
||||||
|
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
||||||
int limit = getRequstURIPath("limit:", maxLimit);
|
int limit = getRequstURIPath("limit:", maxLimit);
|
||||||
int offset = getRequstURIPath("offset:", 0);
|
int offset = getRequstURIPath("offset:", 0);
|
||||||
String sort = getRequstURIPath("sort:", "");
|
String sort = getRequstURIPath("sort:", "");
|
||||||
if (limit > 0) flipper = new org.redkale.source.Flipper(limit, offset, sort);
|
if (limit > 0) flipper = new org.redkale.source.Flipper(limit, offset, sort);
|
||||||
} else if (flipper.getLimit() < 1 || flipper.getLimit() > maxLimit) {
|
} else if (flipper.getLimit() < 1 || (maxLimit > 0 && flipper.getLimit() > maxLimit)) {
|
||||||
flipper.setLimit(maxLimit);
|
flipper.setLimit(maxLimit);
|
||||||
}
|
}
|
||||||
if (flipper != null || !needcreate) return flipper;
|
if (flipper != null || !needcreate) return flipper;
|
||||||
|
if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT;
|
||||||
return new org.redkale.source.Flipper(maxLimit);
|
return new org.redkale.source.Flipper(maxLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import java.util.regex.*;
|
|||||||
import org.redkale.util.AnyValue;
|
import org.redkale.util.AnyValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 静态资源HttpServlet
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import java.nio.channels.*;
|
|||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
import java.text.*;
|
import java.text.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.logging.Level;
|
||||||
import org.redkale.convert.json.JsonConvert;
|
import org.redkale.convert.json.JsonConvert;
|
||||||
import org.redkale.net.*;
|
import org.redkale.net.*;
|
||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||||
@@ -21,9 +23,9 @@ import org.redkale.util.AnyValue.Entry;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。
|
* Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。 <br>
|
||||||
* 同时提供发送json的系列接口: public void finishJson(Type type, Object obj)
|
* 同时提供发送json的系列接口: public void finishJson(Type type, Object obj) <br>
|
||||||
* Redkale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。
|
* Redkale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。 <br>
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
@@ -184,14 +186,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
* @return HttpResponse
|
* @return HttpResponse
|
||||||
*/
|
*/
|
||||||
public HttpResponse addCookie(HttpCookie... cookies) {
|
public HttpResponse addCookie(HttpCookie... cookies) {
|
||||||
if (this.cookies == null) {
|
this.cookies = Utility.append(this.cookies, cookies);
|
||||||
this.cookies = cookies;
|
|
||||||
} else {
|
|
||||||
HttpCookie[] news = new HttpCookie[this.cookies.length + cookies.length];
|
|
||||||
System.arraycopy(this.cookies, 0, news, 0, this.cookies.length);
|
|
||||||
System.arraycopy(cookies, 0, news, this.cookies.length, cookies.length);
|
|
||||||
this.cookies = news;
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,21 +198,30 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
* @return HttpResponse
|
* @return HttpResponse
|
||||||
*/
|
*/
|
||||||
public HttpResponse addCookie(Collection<HttpCookie> cookies) {
|
public HttpResponse addCookie(Collection<HttpCookie> cookies) {
|
||||||
if (cookies == null || cookies.isEmpty()) return this;
|
this.cookies = Utility.append(this.cookies, cookies);
|
||||||
if (this.cookies == null) {
|
|
||||||
this.cookies = cookies.toArray(new HttpCookie[cookies.size()]);
|
|
||||||
} else {
|
|
||||||
HttpCookie[] news = new HttpCookie[this.cookies.length + cookies.size()];
|
|
||||||
System.arraycopy(this.cookies, 0, news, 0, this.cookies.length);
|
|
||||||
int i = this.cookies.length;
|
|
||||||
for (HttpCookie cookie : cookies) {
|
|
||||||
news[i++] = cookie;
|
|
||||||
}
|
|
||||||
this.cookies = news;
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建AsyncHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出
|
||||||
|
*
|
||||||
|
* @return AsyncHandler
|
||||||
|
*/
|
||||||
|
public AsyncHandler createAsyncHandler() {
|
||||||
|
return AsyncHandler.create((v, a) -> {
|
||||||
|
if (v instanceof org.redkale.service.RetResult) {
|
||||||
|
finishJson((org.redkale.service.RetResult) v);
|
||||||
|
} else if (v instanceof CharSequence) {
|
||||||
|
finish(String.valueOf(v));
|
||||||
|
} else {
|
||||||
|
finishJson(v);
|
||||||
|
}
|
||||||
|
}, (t, a) -> {
|
||||||
|
request.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, t);
|
||||||
|
finish(500, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将对象以JSON格式输出
|
* 将对象以JSON格式输出
|
||||||
*
|
*
|
||||||
@@ -225,6 +229,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public void finishJson(final Object obj) {
|
public void finishJson(final Object obj) {
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
this.contentType = "text/plain; charset=utf-8";
|
||||||
|
if (this.recycleListener != null) this.output = obj;
|
||||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), obj));
|
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +241,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public void finishJson(final JsonConvert convert, final Object obj) {
|
public void finishJson(final JsonConvert convert, final Object obj) {
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
this.contentType = "text/plain; charset=utf-8";
|
||||||
|
if (this.recycleListener != null) this.output = obj;
|
||||||
finish(convert.convertTo(context.getBufferSupplier(), obj));
|
finish(convert.convertTo(context.getBufferSupplier(), obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,6 +253,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public void finishJson(final Type type, final Object obj) {
|
public void finishJson(final Type type, final Object obj) {
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
this.contentType = "text/plain; charset=utf-8";
|
||||||
|
this.output = obj;
|
||||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), type, obj));
|
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), type, obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +266,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
|
public void finishJson(final JsonConvert convert, final Type type, final Object obj) {
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
this.contentType = "text/plain; charset=utf-8";
|
||||||
|
if (this.recycleListener != null) this.output = obj;
|
||||||
finish(convert.convertTo(context.getBufferSupplier(), type, obj));
|
finish(convert.convertTo(context.getBufferSupplier(), type, obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,6 +277,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public void finishJson(final Object... objs) {
|
public void finishJson(final Object... objs) {
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
this.contentType = "text/plain; charset=utf-8";
|
||||||
|
if (this.recycleListener != null) this.output = objs;
|
||||||
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), objs));
|
finish(request.getJsonConvert().convertTo(context.getBufferSupplier(), objs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +288,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public void finishJson(final org.redkale.service.RetResult ret) {
|
public void finishJson(final org.redkale.service.RetResult ret) {
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
this.contentType = "text/plain; charset=utf-8";
|
||||||
|
if (this.recycleListener != null) this.output = ret;
|
||||||
if (ret != null && !ret.isSuccess()) {
|
if (ret != null && !ret.isSuccess()) {
|
||||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||||
this.header.addValue("retinfo", ret.getRetinfo());
|
this.header.addValue("retinfo", ret.getRetinfo());
|
||||||
@@ -294,6 +304,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
|
public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) {
|
||||||
this.contentType = "text/plain; charset=utf-8";
|
this.contentType = "text/plain; charset=utf-8";
|
||||||
|
if (this.recycleListener != null) this.output = ret;
|
||||||
if (ret != null && !ret.isSuccess()) {
|
if (ret != null && !ret.isSuccess()) {
|
||||||
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
|
||||||
this.header.addValue("retinfo", ret.getRetinfo());
|
this.header.addValue("retinfo", ret.getRetinfo());
|
||||||
@@ -302,26 +313,59 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将对象以JavaScript格式输出
|
* 将CompletableFuture的结果对象以JSON格式输出
|
||||||
*
|
*
|
||||||
* @param var js变量名
|
* @param future 输出对象的句柄
|
||||||
* @param result 输出对象
|
|
||||||
*/
|
*/
|
||||||
public void finishJsResult(String var, Object result) {
|
public void finishJson(final CompletableFuture future) {
|
||||||
this.contentType = "application/javascript; charset=utf-8";
|
finishJson(request.getJsonConvert(), future);
|
||||||
finish("var " + var + " = " + request.getJsonConvert().convertTo(result) + ";");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将对象以JavaScript格式输出
|
* 将CompletableFuture的结果对象以JSON格式输出
|
||||||
*
|
*
|
||||||
* @param jsonConvert 指定的JsonConvert
|
* @param convert 指定的JsonConvert
|
||||||
* @param var js变量名
|
* @param future 输出对象的句柄
|
||||||
* @param result 输出对象
|
|
||||||
*/
|
*/
|
||||||
public void finishJsResult(JsonConvert jsonConvert, String var, Object result) {
|
public void finishJson(final JsonConvert convert, final CompletableFuture future) {
|
||||||
this.contentType = "application/javascript; charset=utf-8";
|
future.whenComplete((v, e) -> {
|
||||||
finish("var " + var + " = " + jsonConvert.convertTo(result) + ";");
|
if (e != null) {
|
||||||
|
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
|
||||||
|
finish(500, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (v instanceof CharSequence) {
|
||||||
|
finish(v.toString());
|
||||||
|
} else if (v instanceof org.redkale.service.RetResult) {
|
||||||
|
finishJson(convert, (org.redkale.service.RetResult) v);
|
||||||
|
} else {
|
||||||
|
finishJson(convert, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将CompletableFuture的结果对象以JSON格式输出
|
||||||
|
*
|
||||||
|
* @param convert 指定的JsonConvert
|
||||||
|
* @param type 指定的类型
|
||||||
|
* @param future 输出对象的句柄
|
||||||
|
*/
|
||||||
|
public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future) {
|
||||||
|
future.whenComplete((v, e) -> {
|
||||||
|
if (e != null) {
|
||||||
|
context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request, e);
|
||||||
|
finish(500, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (v instanceof CharSequence) {
|
||||||
|
finish(v.toString());
|
||||||
|
} else if (v instanceof org.redkale.service.RetResult) {
|
||||||
|
finishJson(convert, (org.redkale.service.RetResult) v);
|
||||||
|
} else {
|
||||||
|
finishJson(convert, type, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -330,6 +374,8 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
* @param obj 输出内容
|
* @param obj 输出内容
|
||||||
*/
|
*/
|
||||||
public void finish(String obj) {
|
public void finish(String obj) {
|
||||||
|
if (isClosed()) return;
|
||||||
|
if (this.recycleListener != null) this.output = obj;
|
||||||
if (obj == null || obj.isEmpty()) {
|
if (obj == null || obj.isEmpty()) {
|
||||||
final ByteBuffer headbuf = createHeader();
|
final ByteBuffer headbuf = createHeader();
|
||||||
headbuf.flip();
|
headbuf.flip();
|
||||||
@@ -370,6 +416,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
* @param message 输出内容
|
* @param message 输出内容
|
||||||
*/
|
*/
|
||||||
public void finish(int status, String message) {
|
public void finish(int status, String message) {
|
||||||
|
if (isClosed()) return;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
if (status != 200) super.refuseAlive();
|
if (status != 200) super.refuseAlive();
|
||||||
finish(message);
|
finish(message);
|
||||||
@@ -389,6 +436,24 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
super.finish(buffer404.duplicate());
|
super.finish(buffer404.duplicate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定byte[]按响应结果输出
|
||||||
|
*
|
||||||
|
* @param bs 输出内容
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void finish(final byte[] bs) {
|
||||||
|
if (isClosed()) return; //避免重复关闭
|
||||||
|
if (this.context.getBufferCapacity() == bs.length) {
|
||||||
|
ByteBuffer buffer = this.context.pollBuffer();
|
||||||
|
buffer.put(bs);
|
||||||
|
buffer.flip();
|
||||||
|
this.finish(false, buffer);
|
||||||
|
} else {
|
||||||
|
this.finish(false, ByteBuffer.wrap(bs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将指定ByteBuffer按响应结果输出
|
* 将指定ByteBuffer按响应结果输出
|
||||||
*
|
*
|
||||||
@@ -407,6 +472,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void finish(boolean kill, ByteBuffer buffer) {
|
public void finish(boolean kill, ByteBuffer buffer) {
|
||||||
|
if (isClosed()) return; //避免重复关闭
|
||||||
if (!this.headsended) {
|
if (!this.headsended) {
|
||||||
this.contentLength = buffer == null ? 0 : buffer.remaining();
|
this.contentLength = buffer == null ? 0 : buffer.remaining();
|
||||||
ByteBuffer headbuf = createHeader();
|
ByteBuffer headbuf = createHeader();
|
||||||
@@ -439,6 +505,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void finish(boolean kill, ByteBuffer... buffers) {
|
public void finish(boolean kill, ByteBuffer... buffers) {
|
||||||
|
if (isClosed()) return; //避免重复关闭
|
||||||
if (bufferHandler != null) {
|
if (bufferHandler != null) {
|
||||||
ByteBuffer[] bufs = bufferHandler.execute(this, buffers);
|
ByteBuffer[] bufs = bufferHandler.execute(this, buffers);
|
||||||
if (bufs != null) buffers = bufs;
|
if (bufs != null) buffers = bufs;
|
||||||
@@ -473,7 +540,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
* @param attachment 异步回调参数
|
* @param attachment 异步回调参数
|
||||||
* @param handler 异步回调函数
|
* @param handler 异步回调函数
|
||||||
*/
|
*/
|
||||||
public <A> void sendBody(ByteBuffer buffer, A attachment, CompletionHandler<Integer, A> handler) {
|
public <A> void sendBody(ByteBuffer buffer, A attachment, AsyncHandler<Integer, A> handler) {
|
||||||
if (!this.headsended) {
|
if (!this.headsended) {
|
||||||
if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining();
|
if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining();
|
||||||
ByteBuffer headbuf = createHeader();
|
ByteBuffer headbuf = createHeader();
|
||||||
@@ -488,6 +555,37 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步输出指定内容
|
||||||
|
*
|
||||||
|
* @param <A> 泛型
|
||||||
|
* @param buffers 输出内容
|
||||||
|
* @param attachment 异步回调参数
|
||||||
|
* @param handler 异步回调函数
|
||||||
|
*/
|
||||||
|
public <A> void sendBody(ByteBuffer[] buffers, A attachment, AsyncHandler<Integer, A> handler) {
|
||||||
|
if (!this.headsended) {
|
||||||
|
if (this.contentLength < 0) {
|
||||||
|
int len = 0;
|
||||||
|
if (buffers != null && buffers.length > 0) {
|
||||||
|
for (ByteBuffer b : buffers) {
|
||||||
|
len += b.remaining();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.contentLength = len;
|
||||||
|
}
|
||||||
|
ByteBuffer headbuf = createHeader();
|
||||||
|
headbuf.flip();
|
||||||
|
if (buffers == null || buffers.length == 0) {
|
||||||
|
super.send(headbuf, attachment, handler);
|
||||||
|
} else {
|
||||||
|
super.send(Utility.unshift(buffers, headbuf), attachment, handler);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.send(buffers, attachment, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将指定文件按响应结果输出
|
* 将指定文件按响应结果输出
|
||||||
*
|
*
|
||||||
@@ -812,7 +910,7 @@ public class HttpResponse extends Response<HttpContext, HttpRequest> {
|
|||||||
this.bufferHandler = bufferHandler;
|
this.bufferHandler = bufferHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final class TransferFileHandler implements CompletionHandler<Integer, ByteBuffer> {
|
protected final class TransferFileHandler implements AsyncHandler<Integer, ByteBuffer> {
|
||||||
|
|
||||||
private final AsynchronousFileChannel filechannel;
|
private final AsynchronousFileChannel filechannel;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.redkale.util.*;
|
|||||||
import org.redkale.watch.WatchFactory;
|
import org.redkale.watch.WatchFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Http服务器
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -37,24 +38,88 @@ public final class HttpServer extends Server<String, HttpContext, HttpRequest, H
|
|||||||
super.init(config);
|
super.init(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取静态资源HttpServlet
|
||||||
|
*
|
||||||
|
* @return HttpServlet
|
||||||
|
*/
|
||||||
|
public HttpServlet getResourceServlet() {
|
||||||
|
return ((HttpPrepareServlet) this.prepare).resourceHttpServlet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加HttpServlet
|
||||||
|
*
|
||||||
|
* @param prefix url前缀
|
||||||
|
* @param servlet HttpServlet
|
||||||
|
* @param mappings 匹配规则
|
||||||
|
*
|
||||||
|
* @return HttpServer
|
||||||
|
*/
|
||||||
public HttpServer addHttpServlet(String prefix, HttpServlet servlet, String... mappings) {
|
public HttpServer addHttpServlet(String prefix, HttpServlet servlet, String... mappings) {
|
||||||
this.prepare.addServlet(servlet, prefix, null, mappings);
|
this.prepare.addServlet(servlet, prefix, null, mappings);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加HttpServlet
|
||||||
|
*
|
||||||
|
* @param servlet HttpServlet
|
||||||
|
* @param mappings 匹配规则
|
||||||
|
*
|
||||||
|
* @return HttpServer
|
||||||
|
*/
|
||||||
public HttpServer addHttpServlet(HttpServlet servlet, String... mappings) {
|
public HttpServer addHttpServlet(HttpServlet servlet, String... mappings) {
|
||||||
this.prepare.addServlet(servlet, null, null, mappings);
|
this.prepare.addServlet(servlet, null, null, mappings);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHttpServlet(HttpServlet servlet, final String prefix, AnyValue conf, String... mappings) {
|
/**
|
||||||
|
* 添加HttpServlet
|
||||||
|
*
|
||||||
|
* @param prefix url前缀
|
||||||
|
* @param servlet HttpServlet
|
||||||
|
* @param conf 配置信息
|
||||||
|
* @param mappings 匹配规则
|
||||||
|
*
|
||||||
|
* @return HttpServer
|
||||||
|
*/
|
||||||
|
public HttpServer addHttpServlet(HttpServlet servlet, final String prefix, AnyValue conf, String... mappings) {
|
||||||
this.prepare.addServlet(servlet, prefix, conf, mappings);
|
this.prepare.addServlet(servlet, prefix, conf, mappings);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加RestHttpServlet
|
||||||
|
*
|
||||||
|
* @param <S> Service
|
||||||
|
* @param <T> RestHttpServlet
|
||||||
|
* @param name Service的资源名
|
||||||
|
* @param serviceType Service的类型
|
||||||
|
* @param service Service对象
|
||||||
|
* @param baseServletClass RestHttpServlet基类
|
||||||
|
* @param prefix url前缀
|
||||||
|
*
|
||||||
|
* @return RestHttpServlet
|
||||||
|
*/
|
||||||
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet(String name, Class<S> serviceType, S service, Class<T> baseServletClass, String prefix) {
|
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet(String name, Class<S> serviceType, S service, Class<T> baseServletClass, String prefix) {
|
||||||
return addRestServlet(name, serviceType, service, baseServletClass, prefix, null);
|
return addRestServlet(name, serviceType, service, baseServletClass, prefix, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加RestHttpServlet
|
||||||
|
*
|
||||||
|
* @param <S> Service
|
||||||
|
* @param <T> RestHttpServlet
|
||||||
|
* @param name Service的资源名
|
||||||
|
* @param serviceType Service的类型
|
||||||
|
* @param service Service对象
|
||||||
|
* @param baseServletClass RestHttpServlet基类
|
||||||
|
* @param prefix url前缀
|
||||||
|
* @param conf 配置信息
|
||||||
|
*
|
||||||
|
* @return RestHttpServlet
|
||||||
|
*/
|
||||||
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet(
|
public <S extends Service, T extends RestHttpServlet> RestHttpServlet addRestServlet(
|
||||||
final String name, Class<S> serviceType, final S service, final Class<T> baseServletClass, final String prefix, AnyValue conf) {
|
final String name, Class<S> serviceType, final S service, final Class<T> baseServletClass, final String prefix, AnyValue conf) {
|
||||||
RestHttpServlet servlet = null;
|
RestHttpServlet servlet = null;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package org.redkale.net.http;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* MimeType
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -100,6 +101,7 @@ public class MimeType {
|
|||||||
contentTypes.put("oda", "application/oda");
|
contentTypes.put("oda", "application/oda");
|
||||||
contentTypes.put("ogg", "application/ogg");
|
contentTypes.put("ogg", "application/ogg");
|
||||||
contentTypes.put("out", "text/plain");
|
contentTypes.put("out", "text/plain");
|
||||||
|
contentTypes.put("pac", "application/x-javascript-config");
|
||||||
contentTypes.put("pbm", "image/x-portable-bitmap");
|
contentTypes.put("pbm", "image/x-portable-bitmap");
|
||||||
contentTypes.put("pct", "image/pict");
|
contentTypes.put("pct", "image/pict");
|
||||||
contentTypes.put("pdf", "application/pdf");
|
contentTypes.put("pdf", "application/pdf");
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import java.util.regex.*;
|
|||||||
import org.redkale.util.AnyValue.DefaultAnyValue;
|
import org.redkale.util.AnyValue.DefaultAnyValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* HTTP的文件上传请求的上下文对象
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -77,10 +78,21 @@ public final class MultiContext {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断请求是否包含上传文件
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public boolean isMultipart() {
|
public boolean isMultipart() {
|
||||||
return this.boundary != null;
|
return this.boundary != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取上传文件信息列表
|
||||||
|
*
|
||||||
|
* @return Iterable
|
||||||
|
* @throws IOException IOException
|
||||||
|
*/
|
||||||
public Iterable<MultiPart> parts() throws IOException {
|
public Iterable<MultiPart> parts() throws IOException {
|
||||||
if (!isMultipart()) return emptyIterable;
|
if (!isMultipart()) return emptyIterable;
|
||||||
final String boundarystr = "--" + this.boundary;
|
final String boundarystr = "--" + this.boundary;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import static java.lang.annotation.ElementType.*;
|
|||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import jdk.internal.org.objectweb.asm.*;
|
import jdk.internal.org.objectweb.asm.*;
|
||||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
@@ -36,6 +37,8 @@ public final class Rest {
|
|||||||
|
|
||||||
static final String REST_SERVICEMAP_FIELD_NAME = "_servicemap"; //如果只有name=""的Service资源,则实例中_servicemap必须为null
|
static final String REST_SERVICEMAP_FIELD_NAME = "_servicemap"; //如果只有name=""的Service资源,则实例中_servicemap必须为null
|
||||||
|
|
||||||
|
private static final String REST_PARAMTYPES_FIELD_NAME = "_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标, 第二维度是参数的下标
|
||||||
|
|
||||||
private static final Set<String> EXCLUDERMETHODS = new HashSet<>();
|
private static final Set<String> EXCLUDERMETHODS = new HashSet<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -58,6 +61,42 @@ public final class Rest {
|
|||||||
private Rest() {
|
private Rest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class MethodParamClassVisitor extends ClassVisitor {
|
||||||
|
|
||||||
|
private final Map<String, List<String>> fieldmap;
|
||||||
|
|
||||||
|
public MethodParamClassVisitor(int api, final Map<String, List<String>> fieldmap) {
|
||||||
|
super(api);
|
||||||
|
this.fieldmap = fieldmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||||
|
if (java.lang.reflect.Modifier.isStatic(access)) return null;
|
||||||
|
List<String> fieldnames = new ArrayList<>();
|
||||||
|
fieldmap.put(name + ":" + desc, fieldnames);
|
||||||
|
return new MethodVisitor(Opcodes.ASM5) {
|
||||||
|
@Override
|
||||||
|
public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
|
||||||
|
if (index > 0) fieldnames.add(name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多
|
||||||
|
public static Map<String, List<String>> getMethodParamNames(Class clazz) {
|
||||||
|
String n = clazz.getName();
|
||||||
|
InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class");
|
||||||
|
Map<String, List<String>> map = new HashMap<>();
|
||||||
|
if (in == null) return map;
|
||||||
|
try {
|
||||||
|
new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM5, map), 0);
|
||||||
|
} catch (Exception e) { //无需理会
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static String getWebModuleName(Class<? extends Service> serviceType) {
|
static String getWebModuleName(Class<? extends Service> serviceType) {
|
||||||
final RestService controller = serviceType.getAnnotation(RestService.class);
|
final RestService controller = serviceType.getAnnotation(RestService.class);
|
||||||
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
|
if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase();
|
||||||
@@ -72,17 +111,20 @@ public final class Rest {
|
|||||||
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
|
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
|
||||||
if (java.lang.reflect.Modifier.isAbstract(mod)) return null;
|
if (java.lang.reflect.Modifier.isAbstract(mod)) return null;
|
||||||
|
|
||||||
|
final java.lang.reflect.Type futureRestOutputType = new TypeToken<CompletableFuture<RestOutput>>() {
|
||||||
|
}.getType();
|
||||||
final String serviceDesc = Type.getDescriptor(serviceType);
|
final String serviceDesc = Type.getDescriptor(serviceType);
|
||||||
final String webServletDesc = Type.getDescriptor(WebServlet.class);
|
final String webServletDesc = Type.getDescriptor(WebServlet.class);
|
||||||
final String reqDesc = Type.getDescriptor(HttpRequest.class);
|
final String reqDesc = Type.getDescriptor(HttpRequest.class);
|
||||||
final String respDesc = Type.getDescriptor(HttpResponse.class);
|
final String respDesc = Type.getDescriptor(HttpResponse.class);
|
||||||
final String retDesc = Type.getDescriptor(RetResult.class);
|
final String retDesc = Type.getDescriptor(RetResult.class);
|
||||||
|
final String futureDesc = Type.getDescriptor(CompletableFuture.class);
|
||||||
final String flipperDesc = Type.getDescriptor(Flipper.class);
|
final String flipperDesc = Type.getDescriptor(Flipper.class);
|
||||||
final String restoutputDesc = Type.getDescriptor(RestOutput.class);
|
final String restoutputDesc = Type.getDescriptor(RestOutput.class);
|
||||||
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
|
final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class);
|
||||||
final String authDesc = Type.getDescriptor(HttpBaseServlet.AuthIgnore.class);
|
final String authDesc = Type.getDescriptor(HttpBaseServlet.AuthIgnore.class);
|
||||||
final String cacheDesc = Type.getDescriptor(HttpBaseServlet.HttpCacheable.class);
|
final String cacheDesc = Type.getDescriptor(HttpBaseServlet.HttpCacheable.class);
|
||||||
final String actionDesc = Type.getDescriptor(HttpBaseServlet.WebAction.class);
|
final String mappingDesc = Type.getDescriptor(HttpBaseServlet.WebMapping.class);
|
||||||
final String webparamDesc = Type.getDescriptor(HttpBaseServlet.WebParam.class);
|
final String webparamDesc = Type.getDescriptor(HttpBaseServlet.WebParam.class);
|
||||||
final String webparamsDesc = Type.getDescriptor(HttpBaseServlet.WebParams.class);
|
final String webparamsDesc = Type.getDescriptor(HttpBaseServlet.WebParams.class);
|
||||||
final String sourcetypeDesc = Type.getDescriptor(HttpBaseServlet.ParamSourceType.class);
|
final String sourcetypeDesc = Type.getDescriptor(HttpBaseServlet.ParamSourceType.class);
|
||||||
@@ -99,10 +141,7 @@ public final class Rest {
|
|||||||
if (controller != null && controller.ignore()) return null; //标记为ignore=true不创建Servlet
|
if (controller != null && controller.ignore()) return null; //标记为ignore=true不创建Servlet
|
||||||
ClassLoader loader = Sncp.class.getClassLoader();
|
ClassLoader loader = Sncp.class.getClassLoader();
|
||||||
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
|
String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + serviceType.getSimpleName().replaceAll("Service.*$", "") + "RestServlet";
|
||||||
try {
|
|
||||||
return ((Class<T>) Class.forName(newDynName.replace('/', '.'))).newInstance();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
}
|
|
||||||
Method currentUserMethod = null;
|
Method currentUserMethod = null;
|
||||||
try {
|
try {
|
||||||
currentUserMethod = baseServletClass.getDeclaredMethod("currentUser", HttpRequest.class);
|
currentUserMethod = baseServletClass.getDeclaredMethod("currentUser", HttpRequest.class);
|
||||||
@@ -113,7 +152,7 @@ public final class Rest {
|
|||||||
final String defmodulename = getWebModuleName(serviceType);
|
final String defmodulename = getWebModuleName(serviceType);
|
||||||
for (char ch : defmodulename.toCharArray()) {
|
for (char ch : defmodulename.toCharArray()) {
|
||||||
if (!((ch >= '0' && ch <= '9') || ch == '$' || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
|
if (!((ch >= '0' && ch <= '9') || ch == '$' || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
|
||||||
throw new RuntimeException(serviceType.getName() + " has illeal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ $");
|
throw new RuntimeException(serviceType.getName() + " have illeal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ $");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +161,7 @@ public final class Rest {
|
|||||||
AsmMethodVisitor mv;
|
AsmMethodVisitor mv;
|
||||||
AnnotationVisitor av0;
|
AnnotationVisitor av0;
|
||||||
Map<String, Object> classMap = new LinkedHashMap<>();
|
Map<String, Object> classMap = new LinkedHashMap<>();
|
||||||
List<Map<String, Object>> actionMaps = new ArrayList<>();
|
List<Map<String, Object>> mappingMaps = new ArrayList<>();
|
||||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
|
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null);
|
||||||
|
|
||||||
{ //RestDynamic
|
{ //RestDynamic
|
||||||
@@ -163,6 +202,10 @@ public final class Rest {
|
|||||||
fv = cw.visitField(ACC_PRIVATE, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;" + serviceDesc + ">;", null);
|
fv = cw.visitField(ACC_PRIVATE, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;" + serviceDesc + ">;", null);
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
{ //_paramtypes字段 java.lang.reflect.Type[][]
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;", null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
{ //构造函数
|
{ //构造函数
|
||||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
||||||
//mv.setDebug(true);
|
//mv.setDebug(true);
|
||||||
@@ -175,7 +218,9 @@ public final class Rest {
|
|||||||
|
|
||||||
final List<MappingEntry> entrys = new ArrayList<>();
|
final List<MappingEntry> entrys = new ArrayList<>();
|
||||||
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
|
final Map<String, org.redkale.util.Attribute> restAttributes = new LinkedHashMap<>();
|
||||||
|
//获取所有可以转换成WebMapping的方法
|
||||||
|
int methodidex = 0;
|
||||||
|
final List<java.lang.reflect.Type[]> paramtypes = new ArrayList<>();
|
||||||
for (final Method method : serviceType.getMethods()) {
|
for (final Method method : serviceType.getMethods()) {
|
||||||
if (Modifier.isStatic(method.getModifiers())) continue;
|
if (Modifier.isStatic(method.getModifiers())) continue;
|
||||||
Class[] extypes = method.getExceptionTypes();
|
Class[] extypes = method.getExceptionTypes();
|
||||||
@@ -195,22 +240,28 @@ public final class Rest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ignore) continue;
|
if (ignore) continue;
|
||||||
|
paramtypes.add(method.getGenericParameterTypes());
|
||||||
if (mappings.length == 0) { //没有Mapping,设置一个默认值
|
if (mappings.length == 0) { //没有Mapping,设置一个默认值
|
||||||
MappingEntry entry = new MappingEntry(null, defmodulename, method);
|
MappingEntry entry = new MappingEntry(methodidex, null, defmodulename, method);
|
||||||
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
||||||
entrys.add(entry);
|
entrys.add(entry);
|
||||||
} else {
|
} else {
|
||||||
for (RestMapping mapping : mappings) {
|
for (RestMapping mapping : mappings) {
|
||||||
MappingEntry entry = new MappingEntry(mapping, defmodulename, method);
|
MappingEntry entry = new MappingEntry(methodidex, mapping, defmodulename, method);
|
||||||
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat");
|
||||||
entrys.add(entry);
|
entrys.add(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
methodidex++;
|
||||||
}
|
}
|
||||||
if (entrys.isEmpty()) return null; //没有可WebAction的方法
|
if (entrys.isEmpty()) return null; //没有可WebMapping的方法
|
||||||
|
|
||||||
|
//将每个Service可转换的方法生成HttpServlet对应的WebMapping方法
|
||||||
|
final Map<String, List<String>> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType);
|
||||||
for (final MappingEntry entry : entrys) {
|
for (final MappingEntry entry : entrys) {
|
||||||
final Method method = entry.mappingMethod;
|
final Method method = entry.mappingMethod;
|
||||||
final Class returnType = method.getReturnType();
|
final Class returnType = method.getReturnType();
|
||||||
|
final java.lang.reflect.Type returnGenericType = method.getGenericReturnType();
|
||||||
final String methodDesc = Type.getMethodDescriptor(method);
|
final String methodDesc = Type.getMethodDescriptor(method);
|
||||||
final Parameter[] params = method.getParameters();
|
final Parameter[] params = method.getParameters();
|
||||||
|
|
||||||
@@ -221,9 +272,9 @@ public final class Rest {
|
|||||||
av0 = mv.visitAnnotation(authDesc, true);
|
av0 = mv.visitAnnotation(authDesc, true);
|
||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
}
|
}
|
||||||
if (entry.cachetimeout > 0) { //设置 HttpCacheable
|
if (entry.cacheseconds > 0) { //设置 HttpCacheable
|
||||||
av0 = mv.visitAnnotation(cacheDesc, true);
|
av0 = mv.visitAnnotation(cacheDesc, true);
|
||||||
av0.visit("timeout", entry.cachetimeout);
|
av0.visit("seconds", entry.cacheseconds);
|
||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,14 +305,16 @@ public final class Rest {
|
|||||||
final int maxStack = 3 + params.length;
|
final int maxStack = 3 + params.length;
|
||||||
List<int[]> varInsns = new ArrayList<>();
|
List<int[]> varInsns = new ArrayList<>();
|
||||||
int maxLocals = 4;
|
int maxLocals = 4;
|
||||||
final String jsvar = entry.jsvar.isEmpty() ? null : entry.jsvar;
|
|
||||||
int argIndex = 0;
|
|
||||||
|
|
||||||
|
List<String> asmParamNames = asmParamMap == null ? null : asmParamMap.get(method.getName() + ":" + Type.getMethodDescriptor(method));
|
||||||
List<Object[]> paramlist = new ArrayList<>();
|
List<Object[]> paramlist = new ArrayList<>();
|
||||||
for (final Parameter param : params) {
|
//解析方法中的每个参数
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
final Parameter param = params[i];
|
||||||
final Class ptype = param.getType();
|
final Class ptype = param.getType();
|
||||||
String n = null;
|
String n = null;
|
||||||
String comment = "";
|
String comment = "";
|
||||||
|
boolean required = true;
|
||||||
int radix = 10;
|
int radix = 10;
|
||||||
|
|
||||||
RestHeader annhead = param.getAnnotation(RestHeader.class);
|
RestHeader annhead = param.getAnnotation(RestHeader.class);
|
||||||
@@ -297,27 +350,29 @@ public final class Rest {
|
|||||||
RestParam annpara = param.getAnnotation(RestParam.class);
|
RestParam annpara = param.getAnnotation(RestParam.class);
|
||||||
if (annpara != null) radix = annpara.radix();
|
if (annpara != null) radix = annpara.radix();
|
||||||
if (annpara != null) comment = annpara.comment();
|
if (annpara != null) comment = annpara.comment();
|
||||||
|
if (annpara != null) required = annpara.required();
|
||||||
if (n == null) n = (annpara == null || annpara.name().isEmpty()) ? null : annpara.name();
|
if (n == null) n = (annpara == null || annpara.name().isEmpty()) ? null : annpara.name();
|
||||||
if (n == null && ptype == userType) n = "&"; //用户类型特殊处理
|
if (n == null && ptype == userType) n = "&"; //用户类型特殊处理
|
||||||
|
if (n == null && asmParamNames != null && asmParamNames.size() > i) n = asmParamNames.get(i);
|
||||||
if (n == null) {
|
if (n == null) {
|
||||||
if (param.isNamePresent()) {
|
if (param.isNamePresent()) {
|
||||||
n = param.getName();
|
n = param.getName();
|
||||||
} else if (ptype == Flipper.class) {
|
} else if (ptype == Flipper.class) {
|
||||||
n = "flipper";
|
n = "flipper";
|
||||||
} else {
|
} else {
|
||||||
n = (++argIndex > 1) ? ("bean" + argIndex) : "bean";
|
n = ("bean" + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (annhead == null && anncookie == null && annaddr == null
|
if (annhead == null && anncookie == null && annaddr == null
|
||||||
&& (entry.name.startsWith("find") || entry.name.startsWith("delete")) && params.length == 1) {
|
&& (entry.name.startsWith("find") || entry.name.startsWith("delete")) && params.length == 1) {
|
||||||
if (ptype.isPrimitive() || ptype == String.class) n = "#";
|
if (ptype.isPrimitive() || ptype == String.class) n = "#";
|
||||||
}
|
}
|
||||||
paramlist.add(new Object[]{param, n, ptype, radix, comment, annpara, annsid, annaddr, annhead, anncookie});
|
paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie});
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> actionMap = new LinkedHashMap<>();
|
Map<String, Object> mappingMap = new LinkedHashMap<>();
|
||||||
{
|
{ // 设置 Annotation
|
||||||
//设置 WebAction
|
//设置 WebMapping
|
||||||
boolean reqpath = false;
|
boolean reqpath = false;
|
||||||
for (Object[] ps : paramlist) {
|
for (Object[] ps : paramlist) {
|
||||||
if ("#".equals((String) ps[1])) {
|
if ("#".equals((String) ps[1])) {
|
||||||
@@ -325,7 +380,7 @@ public final class Rest {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
av0 = mv.visitAnnotation(actionDesc, true);
|
av0 = mv.visitAnnotation(mappingDesc, true);
|
||||||
String url = "/" + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : "");
|
String url = "/" + defmodulename.toLowerCase() + "/" + entry.name + (reqpath ? "/" : "");
|
||||||
av0.visit("url", url);
|
av0.visit("url", url);
|
||||||
av0.visit("actionid", entry.actionid);
|
av0.visit("actionid", entry.actionid);
|
||||||
@@ -341,22 +396,22 @@ public final class Rest {
|
|||||||
av0.visit("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
|
av0.visit("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
|
||||||
|
|
||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
actionMap.put("url", url);
|
mappingMap.put("url", url);
|
||||||
actionMap.put("auth", entry.auth);
|
mappingMap.put("auth", entry.auth);
|
||||||
actionMap.put("cachetimeout", entry.cachetimeout);
|
mappingMap.put("cachetimeout", entry.cacheseconds);
|
||||||
actionMap.put("actionid", entry.actionid);
|
mappingMap.put("actionid", entry.actionid);
|
||||||
actionMap.put("comment", entry.comment);
|
mappingMap.put("comment", entry.comment);
|
||||||
actionMap.put("methods", entry.methods);
|
mappingMap.put("methods", entry.methods);
|
||||||
actionMap.put("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
|
mappingMap.put("result", grt == returnType ? returnType.getName() : String.valueOf(grt));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{ // 设置 Annotation
|
||||||
av0 = mv.visitAnnotation(webparamsDesc, true);
|
av0 = mv.visitAnnotation(webparamsDesc, true);
|
||||||
AnnotationVisitor av1 = av0.visitArray("value");
|
AnnotationVisitor av1 = av0.visitArray("value");
|
||||||
//设置 WebParam
|
//设置 WebParam
|
||||||
for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, annpara, annsid, annaddr, annhead, anncookie}
|
for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie}
|
||||||
final boolean ishead = ((RestHeader) ps[8]) != null; //是否取getHeader 而不是 getParameter
|
final boolean ishead = ((RestHeader) ps[9]) != null; //是否取getHeader 而不是 getParameter
|
||||||
final boolean iscookie = ((RestCookie) ps[9]) != null; //是否取getCookie
|
final boolean iscookie = ((RestCookie) ps[10]) != null; //是否取getCookie
|
||||||
|
|
||||||
AnnotationVisitor av2 = av1.visitAnnotation(null, webparamDesc);
|
AnnotationVisitor av2 = av1.visitAnnotation(null, webparamDesc);
|
||||||
av2.visit("name", (String) ps[1]);
|
av2.visit("name", (String) ps[1]);
|
||||||
@@ -365,30 +420,51 @@ public final class Rest {
|
|||||||
av2.visitEnum("src", sourcetypeDesc, ishead ? HttpBaseServlet.ParamSourceType.HEADER.name()
|
av2.visitEnum("src", sourcetypeDesc, ishead ? HttpBaseServlet.ParamSourceType.HEADER.name()
|
||||||
: (iscookie ? HttpBaseServlet.ParamSourceType.COOKIE.name() : HttpBaseServlet.ParamSourceType.PARAMETER.name()));
|
: (iscookie ? HttpBaseServlet.ParamSourceType.COOKIE.name() : HttpBaseServlet.ParamSourceType.PARAMETER.name()));
|
||||||
av2.visit("comment", (String) ps[4]);
|
av2.visit("comment", (String) ps[4]);
|
||||||
|
av2.visit("required", (Boolean) ps[5]);
|
||||||
av2.visitEnd();
|
av2.visitEnd();
|
||||||
}
|
}
|
||||||
av1.visitEnd();
|
av1.visitEnd();
|
||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
}
|
}
|
||||||
List<Map<String, Object>> paramMaps = new ArrayList<>();
|
List<Map<String, Object>> paramMaps = new ArrayList<>();
|
||||||
|
//获取每个参数的值
|
||||||
|
boolean hasAsyncHandler = false;
|
||||||
for (Object[] ps : paramlist) {
|
for (Object[] ps : paramlist) {
|
||||||
Map<String, Object> paramMap = new LinkedHashMap<>();
|
Map<String, Object> paramMap = new LinkedHashMap<>();
|
||||||
|
final Parameter param = (Parameter) ps[0]; //参数类型
|
||||||
String pname = (String) ps[1]; //参数名
|
String pname = (String) ps[1]; //参数名
|
||||||
Class ptype = (Class) ps[2];
|
Class ptype = (Class) ps[2]; //参数类型
|
||||||
int radix = (Integer) ps[3];
|
int radix = (Integer) ps[3];
|
||||||
String comment = (String) ps[4];
|
String comment = (String) ps[4];
|
||||||
RestParam annpara = (RestParam) ps[5];
|
boolean required = (Boolean) ps[5];
|
||||||
RestSessionid annsid = (RestSessionid) ps[6];
|
RestParam annpara = (RestParam) ps[6];
|
||||||
RestAddress annaddr = (RestAddress) ps[7];
|
RestSessionid annsid = (RestSessionid) ps[7];
|
||||||
RestHeader annhead = (RestHeader) ps[8];
|
RestAddress annaddr = (RestAddress) ps[8];
|
||||||
RestCookie anncookie = (RestCookie) ps[9];
|
RestHeader annhead = (RestHeader) ps[9];
|
||||||
|
RestCookie anncookie = (RestCookie) ps[10];
|
||||||
|
|
||||||
final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter
|
final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter
|
||||||
final boolean iscookie = anncookie != null; //是否取getCookie
|
final boolean iscookie = anncookie != null; //是否取getCookie
|
||||||
|
|
||||||
paramMap.put("name", pname);
|
paramMap.put("name", pname);
|
||||||
paramMap.put("type", ptype.getName());
|
paramMap.put("type", ptype.getName());
|
||||||
if (annsid != null) { //HttpRequest.getSessionid(true|false)
|
if (AsyncHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class)
|
||||||
|
if (ptype == AsyncHandler.class) {
|
||||||
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Lorg/redkale/util/AsyncHandler;", false);
|
||||||
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
|
varInsns.add(new int[]{ALOAD, maxLocals});
|
||||||
|
} else {
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
|
mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype)));
|
||||||
|
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "createAsyncHandler", "(Lorg/redkale/net/http/HttpResponse;Ljava/lang/Class;)Lorg/redkale/util/AsyncHandler;", false);
|
||||||
|
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
|
||||||
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
|
varInsns.add(new int[]{ALOAD, maxLocals});
|
||||||
|
}
|
||||||
|
hasAsyncHandler = true;
|
||||||
|
} else if (annsid != null) { //HttpRequest.getSessionid(true|false)
|
||||||
mv.visitVarInsn(ALOAD, 1);
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
mv.visitInsn(annsid.create() ? ICONST_1 : ICONST_0);
|
mv.visitInsn(annsid.create() ? ICONST_1 : ICONST_0);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false);
|
||||||
@@ -630,7 +706,31 @@ public final class Rest {
|
|||||||
varInsns.add(new int[]{ALOAD, maxLocals});
|
varInsns.add(new int[]{ALOAD, maxLocals});
|
||||||
} else { //其他Json对象
|
} else { //其他Json对象
|
||||||
mv.visitVarInsn(ALOAD, 1);
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
if (param.getType() == param.getParameterizedType()) {
|
||||||
mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype)));
|
mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype)));
|
||||||
|
} else {
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;");
|
||||||
|
if (entry.methodidx <= 5) { //方法下标
|
||||||
|
mv.visitInsn(ICONST_0 + entry.methodidx);
|
||||||
|
} else {
|
||||||
|
mv.visitIntInsn(BIPUSH, entry.methodidx);
|
||||||
|
}
|
||||||
|
mv.visitInsn(AALOAD);
|
||||||
|
int paramidx = 0;
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
if (params[i] == param) {
|
||||||
|
paramidx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paramidx <= 5) { //参数下标
|
||||||
|
mv.visitInsn(ICONST_0 + paramidx);
|
||||||
|
} else {
|
||||||
|
mv.visitIntInsn(BIPUSH, paramidx);
|
||||||
|
}
|
||||||
|
mv.visitInsn(AALOAD);
|
||||||
|
}
|
||||||
mv.visitLdcInsn(pname);
|
mv.visitLdcInsn(pname);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getJsonHeader" : "getJsonParameter", "(Ljava/lang/reflect/Type;Ljava/lang/String;)Ljava/lang/Object;", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getJsonHeader" : "getJsonParameter", "(Ljava/lang/reflect/Type;Ljava/lang/String;)Ljava/lang/Object;", false);
|
||||||
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
|
mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/'));
|
||||||
@@ -720,227 +820,140 @@ public final class Rest {
|
|||||||
mv.visitVarInsn(ins[0], ins[1]);
|
mv.visitVarInsn(ins[0], ins[1]);
|
||||||
}
|
}
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, serviceTypeInternalName, method.getName(), methodDesc, false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, serviceTypeInternalName, method.getName(), methodDesc, false);
|
||||||
if (returnType == void.class) {
|
if (hasAsyncHandler) {
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
} else if (returnType == void.class) {
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, retInternalName, "success", "()" + retDesc, false);
|
mv.visitMethodInsn(INVOKESTATIC, retInternalName, "success", "()" + retDesc, false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
} else if (returnType == boolean.class) {
|
} else if (returnType == boolean.class) {
|
||||||
mv.visitVarInsn(ISTORE, maxLocals);
|
mv.visitVarInsn(ISTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
mv.visitVarInsn(ILOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (returnType == byte.class) {
|
} else if (returnType == byte.class) {
|
||||||
mv.visitVarInsn(ISTORE, maxLocals);
|
mv.visitVarInsn(ISTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
mv.visitVarInsn(ILOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (returnType == short.class) {
|
} else if (returnType == short.class) {
|
||||||
mv.visitVarInsn(ISTORE, maxLocals);
|
mv.visitVarInsn(ISTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
mv.visitVarInsn(ILOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (returnType == char.class) {
|
} else if (returnType == char.class) {
|
||||||
mv.visitVarInsn(ISTORE, maxLocals);
|
mv.visitVarInsn(ISTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
mv.visitVarInsn(ILOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (returnType == int.class) {
|
} else if (returnType == int.class) {
|
||||||
mv.visitVarInsn(ISTORE, maxLocals);
|
mv.visitVarInsn(ISTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
mv.visitVarInsn(ILOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ILOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (returnType == float.class) {
|
} else if (returnType == float.class) {
|
||||||
mv.visitVarInsn(FSTORE, maxLocals);
|
mv.visitVarInsn(FSTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(FLOAD, maxLocals);
|
mv.visitVarInsn(FLOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(FLOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (returnType == long.class) {
|
} else if (returnType == long.class) {
|
||||||
mv.visitVarInsn(LSTORE, maxLocals);
|
mv.visitVarInsn(LSTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(LLOAD, maxLocals);
|
mv.visitVarInsn(LLOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(LLOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals += 2;
|
maxLocals += 2;
|
||||||
} else if (returnType == double.class) {
|
} else if (returnType == double.class) {
|
||||||
mv.visitVarInsn(DSTORE, maxLocals);
|
mv.visitVarInsn(DSTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(DLOAD, maxLocals);
|
mv.visitVarInsn(DLOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(DLOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals += 2;
|
maxLocals += 2;
|
||||||
} else if (returnType == String.class) {
|
} else if (returnType == String.class) {
|
||||||
mv.visitVarInsn(ASTORE, maxLocals);
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (returnType == File.class) {
|
} else if (returnType == File.class) {
|
||||||
mv.visitVarInsn(ASTORE, maxLocals);
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false);
|
||||||
} else {
|
|
||||||
throw new RuntimeException(method + " cannot set return Type (java.io.File) to jsvar");
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (RetResult.class.isAssignableFrom(returnType)) {
|
} else if (RetResult.class.isAssignableFrom(returnType)) {
|
||||||
mv.visitVarInsn(ASTORE, maxLocals);
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (RestOutput.class.isAssignableFrom(returnType)) {
|
} else if (RestOutput.class.isAssignableFrom(returnType)) {
|
||||||
mv.visitVarInsn(ASTORE, maxLocals);
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJson", "(" + respDesc + restoutputDesc + ")V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJson", "(" + respDesc + restoutputDesc + ")V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJsResult", "(" + respDesc + "Ljava/lang/String;" + restoutputDesc + ")V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
|
} else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面
|
||||||
mv.visitVarInsn(ASTORE, maxLocals);
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false);
|
||||||
} else {
|
mv.visitInsn(RETURN);
|
||||||
|
maxLocals++;
|
||||||
|
} else if (futureRestOutputType == returnGenericType) {
|
||||||
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "finishJson", "(" + respDesc + futureDesc + ")V", false);
|
||||||
}
|
mv.visitInsn(RETURN);
|
||||||
|
maxLocals++;
|
||||||
|
} else if (CompletableFuture.class.isAssignableFrom(returnType)) {
|
||||||
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
|
mv.visitVarInsn(ALOAD, 2);//response
|
||||||
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + futureDesc + ")V", false);
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
} else {
|
} else {
|
||||||
mv.visitVarInsn(ASTORE, maxLocals);
|
mv.visitVarInsn(ASTORE, maxLocals);
|
||||||
if (jsvar == null) {
|
|
||||||
mv.visitVarInsn(ALOAD, 2); //response
|
mv.visitVarInsn(ALOAD, 2); //response
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
mv.visitVarInsn(ALOAD, maxLocals);
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false);
|
||||||
} else {
|
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
|
||||||
mv.visitLdcInsn(jsvar);
|
|
||||||
mv.visitVarInsn(ALOAD, maxLocals);
|
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJsResult", "(Ljava/lang/String;Ljava/lang/Object;)V", false);
|
|
||||||
}
|
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxLocals++;
|
maxLocals++;
|
||||||
}
|
}
|
||||||
mv.visitMaxs(maxStack, maxLocals);
|
mv.visitMaxs(maxStack, maxLocals);
|
||||||
actionMap.put("params", paramMaps);
|
mappingMap.put("params", paramMaps);
|
||||||
actionMaps.add(actionMap);
|
mappingMaps.add(mappingMap);
|
||||||
} // end for each
|
} // end for each
|
||||||
|
|
||||||
for (String attrname : restAttributes.keySet()) {
|
for (String attrname : restAttributes.keySet()) {
|
||||||
@@ -948,7 +961,7 @@ public final class Rest {
|
|||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
classMap.put("actions", actionMaps);
|
classMap.put("mappings", mappingMaps);
|
||||||
|
|
||||||
{ //toString函数
|
{ //toString函数
|
||||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null));
|
||||||
@@ -973,6 +986,12 @@ public final class Rest {
|
|||||||
attrField.setAccessible(true);
|
attrField.setAccessible(true);
|
||||||
attrField.set(obj, en.getValue());
|
attrField.set(obj, en.getValue());
|
||||||
}
|
}
|
||||||
|
Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME);
|
||||||
|
typesfield.setAccessible(true);
|
||||||
|
java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][];
|
||||||
|
paramtypeArray = paramtypes.toArray(paramtypeArray);
|
||||||
|
typesfield.set(obj, paramtypeArray);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@@ -1004,8 +1023,9 @@ public final class Rest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MappingEntry(RestMapping mapping, final String defmodulename, Method method) {
|
public MappingEntry(int methodidx, RestMapping mapping, final String defmodulename, Method method) {
|
||||||
if (mapping == null) mapping = DEFAULT__MAPPING;
|
if (mapping == null) mapping = DEFAULT__MAPPING;
|
||||||
|
this.methodidx = methodidx;
|
||||||
this.ignore = mapping.ignore();
|
this.ignore = mapping.ignore();
|
||||||
String n = mapping.name().toLowerCase();
|
String n = mapping.name().toLowerCase();
|
||||||
if (n.isEmpty()) n = method.getName().toLowerCase().replace(defmodulename.toLowerCase(), "");
|
if (n.isEmpty()) n = method.getName().toLowerCase().replace(defmodulename.toLowerCase(), "");
|
||||||
@@ -1014,12 +1034,12 @@ public final class Rest {
|
|||||||
this.methods = mapping.methods();
|
this.methods = mapping.methods();
|
||||||
this.auth = mapping.auth();
|
this.auth = mapping.auth();
|
||||||
this.actionid = mapping.actionid();
|
this.actionid = mapping.actionid();
|
||||||
this.cachetimeout = mapping.cachetimeout();
|
this.cacheseconds = mapping.cacheseconds();
|
||||||
//this.contentType = mapping.contentType();
|
|
||||||
this.comment = mapping.comment();
|
this.comment = mapping.comment();
|
||||||
this.jsvar = mapping.jsvar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final int methodidx; // _paramtypes 的下标,从0开始
|
||||||
|
|
||||||
public final Method mappingMethod;
|
public final Method mappingMethod;
|
||||||
|
|
||||||
public final boolean ignore;
|
public final boolean ignore;
|
||||||
@@ -1034,10 +1054,7 @@ public final class Rest {
|
|||||||
|
|
||||||
public final int actionid;
|
public final int actionid;
|
||||||
|
|
||||||
public final int cachetimeout;
|
public final int cacheseconds;
|
||||||
|
|
||||||
//public final String contentType;
|
|
||||||
public final String jsvar;
|
|
||||||
|
|
||||||
@RestMapping()
|
@RestMapping()
|
||||||
void mapping() { //用于获取Mapping 默认值
|
void mapping() { //用于获取Mapping 默认值
|
||||||
|
|||||||
@@ -22,9 +22,24 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
public @interface RestCookie {
|
public @interface RestCookie {
|
||||||
|
|
||||||
String name(); //cookie名
|
/**
|
||||||
|
* cookie名
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String name();
|
||||||
|
|
||||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
/**
|
||||||
|
* 转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int radix() default 10;
|
||||||
|
|
||||||
String comment() default ""; //备注描述
|
/**
|
||||||
|
* 备注描述
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String comment() default "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,24 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
public @interface RestHeader {
|
public @interface RestHeader {
|
||||||
|
|
||||||
String name(); //参数名
|
/**
|
||||||
|
* Header参数名
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String name();
|
||||||
|
|
||||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
/**
|
||||||
|
* 转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int radix() default 10;
|
||||||
|
|
||||||
String comment() default ""; //备注描述
|
/**
|
||||||
|
* 备注描述
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String comment() default "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
package org.redkale.net.http;
|
package org.redkale.net.http;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import jdk.internal.org.objectweb.asm.*;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -18,27 +23,163 @@ public abstract class RestHttpServlet<T> extends HttpBaseServlet {
|
|||||||
|
|
||||||
protected abstract T currentUser(HttpRequest req) throws IOException;
|
protected abstract T currentUser(HttpRequest req) throws IOException;
|
||||||
|
|
||||||
|
protected void finishJson(final HttpResponse response, CompletableFuture<RestOutput> future) throws IOException {
|
||||||
|
future.whenComplete((output, e) -> {
|
||||||
|
if (e != null) {
|
||||||
|
response.getContext().getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + response.getRequest(), e);
|
||||||
|
response.finish(500, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
finishJson(response, output);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
response.getContext().getLogger().log(Level.WARNING, "Servlet finish RestOutput occur, forece to close channel. request = " + response.getRequest(), ioe);
|
||||||
|
response.finish(500, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected void finishJson(final HttpResponse response, RestOutput output) throws IOException {
|
protected void finishJson(final HttpResponse response, RestOutput output) throws IOException {
|
||||||
if (output == null) {
|
if (output == null) {
|
||||||
response.finishJson(output);
|
response.finishJson(output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (output.getContentType() != null) response.setContentType(output.getContentType());
|
||||||
response.addHeader(output.getHeaders());
|
response.addHeader(output.getHeaders());
|
||||||
response.addCookie(output.getCookies());
|
response.addCookie(output.getCookies());
|
||||||
|
response.setStatus(output.getStatus() < 1 ? 200 : output.getStatus());
|
||||||
if (output.getResult() instanceof File) {
|
if (output.getResult() instanceof File) {
|
||||||
response.finish((File) output.getResult());
|
response.finish((File) output.getResult());
|
||||||
|
} else if (output.getResult() instanceof String) {
|
||||||
|
response.finish((String) output.getResult());
|
||||||
|
} else if (output.getResult() == null) {
|
||||||
|
response.finish(output.getMessage());
|
||||||
} else {
|
} else {
|
||||||
response.finishJson(output.getResult());
|
response.finishJson(output.getResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void finishJsResult(final HttpResponse response, final String var, RestOutput output) throws IOException {
|
/**
|
||||||
if (output != null) {
|
* 创建AsyncHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出 <br>
|
||||||
response.addHeader(output.getHeaders());
|
*
|
||||||
response.addCookie(output.getCookies());
|
* 传入的AsyncHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
|
||||||
|
*
|
||||||
|
* @param <H> AsyncHandler泛型
|
||||||
|
* @param response HttpResponse
|
||||||
|
* @param handlerClass Class
|
||||||
|
*
|
||||||
|
* @return AsyncHandler
|
||||||
|
*/
|
||||||
|
protected final <H extends AsyncHandler> H createAsyncHandler(HttpResponse response, final Class<H> handlerClass) {
|
||||||
|
if (handlerClass == null || handlerClass == AsyncHandler.class) return (H) response.createAsyncHandler();
|
||||||
|
Creator<H> creator = creators.get(handlerClass);
|
||||||
|
if (creator == null) {
|
||||||
|
creator = createCreator(handlerClass);
|
||||||
|
creators.put(handlerClass, creator);
|
||||||
}
|
}
|
||||||
response.finishJsResult(var, output == null ? null : output.getResult());
|
return (H) creator.create(response.createAsyncHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ConcurrentHashMap<Class, Creator> creators = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static <H extends AsyncHandler> Creator<H> createCreator(Class<H> handlerClass) {
|
||||||
|
//生成规则与SncpAsyncHandler.Factory 很类似
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
final boolean handlerinterface = handlerClass.isInterface();
|
||||||
|
final String handlerClassName = handlerClass.getName().replace('.', '/');
|
||||||
|
final String handlerName = AsyncHandler.class.getName().replace('.', '/');
|
||||||
|
final String handlerDesc = Type.getDescriptor(AsyncHandler.class);
|
||||||
|
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + AsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
|
||||||
|
|
||||||
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||||
|
FieldVisitor fv;
|
||||||
|
AsmMethodVisitor mv;
|
||||||
|
AnnotationVisitor av0;
|
||||||
|
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName} : new String[]{handlerName});
|
||||||
|
|
||||||
|
{ //handler 属性
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, "handler", handlerDesc, null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
{//构造方法
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + handlerDesc + ")V", null, null));
|
||||||
|
//mv.setDebug(true);
|
||||||
|
{
|
||||||
|
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
|
||||||
|
{
|
||||||
|
AnnotationVisitor av1 = av0.visitArray("value");
|
||||||
|
av1.visit(null, "handler");
|
||||||
|
av1.visitEnd();
|
||||||
|
}
|
||||||
|
av0.visitEnd();
|
||||||
|
}
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "<init>", "()V", false);
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitFieldInsn(PUTFIELD, newDynName, "handler", handlerDesc);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(2, 2);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (java.lang.reflect.Method method : handlerClass.getMethods()) { //
|
||||||
|
if ("completed".equals(method.getName()) && method.getParameterCount() == 2) {
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(3, 3);
|
||||||
|
mv.visitEnd();
|
||||||
|
} else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) {
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(3, 3);
|
||||||
|
mv.visitEnd();
|
||||||
|
} else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) {
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
|
||||||
|
Class returnType = method.getReturnType();
|
||||||
|
if (returnType == void.class) {
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(0, 1);
|
||||||
|
} else if (returnType.isPrimitive()) {
|
||||||
|
mv.visitInsn(ICONST_0);
|
||||||
|
if (returnType == long.class) {
|
||||||
|
mv.visitInsn(LRETURN);
|
||||||
|
mv.visitMaxs(2, 1);
|
||||||
|
} else if (returnType == float.class) {
|
||||||
|
mv.visitInsn(FRETURN);
|
||||||
|
mv.visitMaxs(2, 1);
|
||||||
|
} else if (returnType == double.class) {
|
||||||
|
mv.visitInsn(DRETURN);
|
||||||
|
mv.visitMaxs(2, 1);
|
||||||
|
} else {
|
||||||
|
mv.visitInsn(IRETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mv.visitInsn(ACONST_NULL);
|
||||||
|
mv.visitInsn(ARETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
}
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cw.visitEnd();
|
||||||
|
byte[] bytes = cw.toByteArray();
|
||||||
|
Class<AsyncHandler> newHandlerClazz = (Class<AsyncHandler>) new ClassLoader(handlerClass.getClassLoader()) {
|
||||||
|
public final Class<?> loadClass(String name, byte[] b) {
|
||||||
|
return defineClass(name, b, 0, b.length);
|
||||||
|
}
|
||||||
|
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||||
|
return (Creator<H>) Creator.create(newHandlerClazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import static java.lang.annotation.ElementType.*;
|
|||||||
import static java.lang.annotation.RetentionPolicy.*;
|
import static java.lang.annotation.RetentionPolicy.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 只能依附在Service实现类的public方法上
|
* 只能依附在Service实现类的public方法上 <br>
|
||||||
* value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService,的默认路径为/hello)。
|
* value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService,的默认路径为/hello)。 <br>
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
@@ -24,26 +24,54 @@ import static java.lang.annotation.RetentionPolicy.*;
|
|||||||
@Repeatable(RestMappings.class)
|
@Repeatable(RestMappings.class)
|
||||||
public @interface RestMapping {
|
public @interface RestMapping {
|
||||||
|
|
||||||
boolean ignore() default false; //是否屏蔽该方法的转换
|
/**
|
||||||
|
* 是否屏蔽该方法的转换
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
boolean ignore() default false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求的方法名, 不能含特殊字符
|
* 请求的方法名, 不能含特殊字符
|
||||||
* 默认为方法名的小写(若方法名以createXXX、updateXXX、deleteXXX、queryXXX、findXXX且XXXService为Service的类名将只截取XXX之前)
|
* 默认为方法名的小写(若方法名以createXXX、updateXXX、deleteXXX、queryXXX、findXXX、existsXXX且XXXService为Service的类名将只截取XXX之前)
|
||||||
*
|
*
|
||||||
* @return name
|
* @return String
|
||||||
*/
|
*/
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
String comment() default ""; //备注描述, 对应@WebAction.comment
|
/**
|
||||||
|
* 备注描述, 对应@WebMapping.comment
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String comment() default "";
|
||||||
|
|
||||||
boolean auth() default false; //是否鉴权,默认不鉴权, 对应@AuthIgnore
|
/**
|
||||||
|
* 是否鉴权,默认不鉴权, 对应@AuthIgnore
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
boolean auth() default false;
|
||||||
|
|
||||||
int actionid() default 0; //操作ID值,鉴权时用到, 对应@WebAction.actionid
|
/**
|
||||||
|
* 操作ID值,鉴权时用到, 对应@WebMapping.actionid
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int actionid() default 0;
|
||||||
|
|
||||||
int cachetimeout() default 0; // 结果缓存的秒数, 为0表示不缓存, 对应@HttpCacheable.timeout
|
/**
|
||||||
|
* 结果缓存的秒数, 为0表示不缓存, 对应@HttpCacheable.seconds
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int cacheseconds() default 0;
|
||||||
|
|
||||||
String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@WebAction.methods
|
/**
|
||||||
|
* 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@WebMapping.methods
|
||||||
|
*
|
||||||
|
* @return String[]
|
||||||
|
*/
|
||||||
|
String[] methods() default {};
|
||||||
|
|
||||||
//String contentType() default ""; //设置Response的ContentType 默认值为 text/plain; charset=utf-8
|
|
||||||
String jsvar() default ""; //以application/javascript输出对象是指明js的对象名,该值存在时则忽略contentType()的值
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,14 @@ public class RestOutput<T> {
|
|||||||
|
|
||||||
private List<HttpCookie> cookies;
|
private List<HttpCookie> cookies;
|
||||||
|
|
||||||
|
private String contentType;
|
||||||
|
|
||||||
private T result;
|
private T result;
|
||||||
|
|
||||||
|
private int status = 0; //不设置则为 200
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
public RestOutput() {
|
public RestOutput() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,14 +38,16 @@ public class RestOutput<T> {
|
|||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHeader(String name, Serializable value) {
|
public RestOutput<T> addHeader(String name, Serializable value) {
|
||||||
if (this.headers == null) this.headers = new HashMap<>();
|
if (this.headers == null) this.headers = new HashMap<>();
|
||||||
this.headers.put(name, String.valueOf(value));
|
this.headers.put(name, String.valueOf(value));
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addCookie(HttpCookie cookie) {
|
public RestOutput<T> addCookie(HttpCookie cookie) {
|
||||||
if (this.cookies == null) this.cookies = new ArrayList<>();
|
if (this.cookies == null) this.cookies = new ArrayList<>();
|
||||||
this.cookies.add(cookie);
|
this.cookies.add(cookie);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getHeaders() {
|
public Map<String, String> getHeaders() {
|
||||||
@@ -58,6 +66,14 @@ public class RestOutput<T> {
|
|||||||
this.cookies = cookies;
|
this.cookies = cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
public T getResult() {
|
public T getResult() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -66,4 +82,20 @@ public class RestOutput<T> {
|
|||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import static java.lang.annotation.ElementType.PARAMETER;
|
|||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 只能依附在Service类的方法的参数上, name值不能是'&'
|
*
|
||||||
* name='#'表示截取uri最后一段
|
* 依附在RestService类的方法的参数上 <br>
|
||||||
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值
|
* name='&' 表示当前用户 <br>
|
||||||
|
* name='#'表示截取uri最后一段 <br>
|
||||||
|
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br>
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
@@ -24,9 +26,36 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
public @interface RestParam {
|
public @interface RestParam {
|
||||||
|
|
||||||
String name(); //参数名
|
//name='&'表示当前用户;
|
||||||
|
/**
|
||||||
|
* 参数名 <br>
|
||||||
|
* name='&'表示当前用户; <br>
|
||||||
|
* name='#'表示截取uri最后一段; <br>
|
||||||
|
* name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值 <br>
|
||||||
|
* 若方法名以find、delete开头且方法的参数只有一个且参数类型是基本数据类型或String,则默认值为"#" <br>
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String name();
|
||||||
|
|
||||||
int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制
|
/**
|
||||||
|
* 转换数字byte/short/int/long时所用的进制数, 默认10进制
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int radix() default 10;
|
||||||
|
|
||||||
String comment() default ""; //备注描述
|
/**
|
||||||
|
* 参数是否必传
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
boolean required() default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注描述
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String comment() default "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.http;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebInitParam
|
|
||||||
*
|
|
||||||
* <p> 详情见: https://redkale.org
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@Target({ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Documented
|
|
||||||
public @interface WebInitParam {
|
|
||||||
|
|
||||||
String name();
|
|
||||||
|
|
||||||
String value();
|
|
||||||
|
|
||||||
String description() default "";
|
|
||||||
}
|
|
||||||
@@ -21,15 +21,38 @@ import java.lang.annotation.*;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface WebServlet {
|
public @interface WebServlet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpServlet资源名
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否自动添加url前缀, 对应application.xml中servlets节点的path属性
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
boolean repair() default true;
|
boolean repair() default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url匹配规则
|
||||||
|
*
|
||||||
|
* @return String[]
|
||||||
|
*/
|
||||||
String[] value() default {};
|
String[] value() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块ID,一个HttpServlet尽量只有提供一个模块的服务
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
int moduleid() default 0;
|
int moduleid() default 0;
|
||||||
|
|
||||||
WebInitParam[] initParams() default {};
|
/**
|
||||||
|
* 备注描述
|
||||||
String comment() default ""; //备注描述
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
String comment() default "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import java.io.*;
|
|||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import org.redkale.convert.json.JsonConvert;
|
||||||
import org.redkale.net.*;
|
import org.redkale.net.*;
|
||||||
|
import org.redkale.util.Comment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
@@ -22,15 +24,13 @@ import org.redkale.net.*;
|
|||||||
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
|
* 1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
|
||||||
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
|
* 1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
|
||||||
* 1.5 onClose WebSocket被关闭后回调此方法。
|
* 1.5 onClose WebSocket被关闭后回调此方法。
|
||||||
*
|
* 普通模式下 以上方法都应该被重载。
|
||||||
* 此模式下 以上方法都应该被重载。
|
|
||||||
*
|
*
|
||||||
* 2) 原始二进制模式: 此模式有别于HTML5规范,可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
|
* 2) 原始二进制模式: 此模式有别于HTML5规范,可以视为原始的TCP连接。通常用于音频视频通讯场景。其流程顺序如下:
|
||||||
* 2.1 onOpen 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断登录态。
|
* 2.1 onOpen 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断登录态。
|
||||||
* 2.2 createGroupid 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断用户权限是否符合。
|
* 2.2 createGroupid 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断用户权限是否符合。
|
||||||
* 2.3 onRead WebSocket成功连接后回调此方法, 由此方法处理原始的TCP连接, 同时业务代码去控制WebSocket的关闭。
|
* 2.3 onRead WebSocket成功连接后回调此方法, 由此方法处理原始的TCP连接, 需要业务代码去控制WebSocket的关闭。
|
||||||
*
|
* 二进制模式下 以上方法都应该被重载。
|
||||||
* 此模式下 以上方法都应该被重载。
|
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -39,24 +39,28 @@ import org.redkale.net.*;
|
|||||||
*/
|
*/
|
||||||
public abstract class WebSocket {
|
public abstract class WebSocket {
|
||||||
|
|
||||||
//消息不合法
|
@Comment("消息不合法")
|
||||||
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
|
public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2
|
||||||
|
|
||||||
//ws已经关闭
|
@Comment("WebSocket已经关闭")
|
||||||
public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4
|
public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4
|
||||||
|
|
||||||
//socket的buffer不合法
|
@Comment("Socket的buffer不合法")
|
||||||
public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8
|
public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8
|
||||||
|
|
||||||
//ws发送消息异常
|
@Comment("WebSocket发送消息异常")
|
||||||
public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16
|
public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16
|
||||||
|
|
||||||
|
@Comment("WebSocketEngine实例不存在")
|
||||||
public static final int RETCODE_ENGINE_NULL = 1 << 5; //32
|
public static final int RETCODE_ENGINE_NULL = 1 << 5; //32
|
||||||
|
|
||||||
|
@Comment("WebSocketNode实例不存在")
|
||||||
public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64
|
public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64
|
||||||
|
|
||||||
|
@Comment("WebSocket组为空, 表示无WebSocket连接")
|
||||||
public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128
|
public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128
|
||||||
|
|
||||||
|
@Comment("WebSocket已离线")
|
||||||
public static final int RETCODE_WSOFFLINE = 1 << 8; //256
|
public static final int RETCODE_WSOFFLINE = 1 << 8; //256
|
||||||
|
|
||||||
WebSocketRunner _runner; //不可能为空
|
WebSocketRunner _runner; //不可能为空
|
||||||
@@ -73,6 +77,8 @@ public abstract class WebSocket {
|
|||||||
|
|
||||||
String _remoteAddr;//不可能为空
|
String _remoteAddr;//不可能为空
|
||||||
|
|
||||||
|
JsonConvert _jsonConvert; //不可能为空
|
||||||
|
|
||||||
private final long createtime = System.currentTimeMillis();
|
private final long createtime = System.currentTimeMillis();
|
||||||
|
|
||||||
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
|
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
|
||||||
@@ -82,9 +88,10 @@ public abstract class WebSocket {
|
|||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* 发送消息体, 包含二进制/文本
|
* 给自身发送消息体, 包含二进制/文本
|
||||||
*
|
*
|
||||||
* @param packet WebSocketPacket
|
* @param packet WebSocketPacket
|
||||||
|
*
|
||||||
* @return 0表示成功, 非0表示错误码
|
* @return 0表示成功, 非0表示错误码
|
||||||
*/
|
*/
|
||||||
public final int send(WebSocketPacket packet) {
|
public final int send(WebSocketPacket packet) {
|
||||||
@@ -95,9 +102,10 @@ public abstract class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送单一的文本消息
|
* 给自身发送单一的文本消息
|
||||||
*
|
*
|
||||||
* @param text 不可为空
|
* @param text 不可为空
|
||||||
|
*
|
||||||
* @return 0表示成功, 非0表示错误码
|
* @return 0表示成功, 非0表示错误码
|
||||||
*/
|
*/
|
||||||
public final int send(String text) {
|
public final int send(String text) {
|
||||||
@@ -105,10 +113,11 @@ public abstract class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送文本消息
|
* 给自身发送文本消息
|
||||||
*
|
*
|
||||||
* @param text 不可为空
|
* @param text 不可为空
|
||||||
* @param last 是否最后一条
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
* @return 0表示成功, 非0表示错误码
|
* @return 0表示成功, 非0表示错误码
|
||||||
*/
|
*/
|
||||||
public final int send(String text, boolean last) {
|
public final int send(String text, boolean last) {
|
||||||
@@ -133,9 +142,10 @@ public abstract class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送单一的二进制消息
|
* 给自身发送单一的二进制消息
|
||||||
*
|
*
|
||||||
* @param data byte[]
|
* @param data byte[]
|
||||||
|
*
|
||||||
* @return 0表示成功, 非0表示错误码
|
* @return 0表示成功, 非0表示错误码
|
||||||
*/
|
*/
|
||||||
public final int send(byte[] data) {
|
public final int send(byte[] data) {
|
||||||
@@ -143,10 +153,11 @@ public abstract class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送二进制消息
|
* 给自身发送二进制消息
|
||||||
*
|
*
|
||||||
* @param data 不可为空
|
* @param data 不可为空
|
||||||
* @param last 是否最后一条
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
* @return 0表示成功, 非0表示错误码
|
* @return 0表示成功, 非0表示错误码
|
||||||
*/
|
*/
|
||||||
public final int send(byte[] data, boolean last) {
|
public final int send(byte[] data, boolean last) {
|
||||||
@@ -154,14 +165,30 @@ public abstract class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送消息, 消息类型是String或byte[]
|
* 给自身发送消息, 消息类型是String或byte[]或可JSON化对象
|
||||||
|
*
|
||||||
|
* @param message 不可为空, 只能是String或byte[]或可JSON化对象
|
||||||
*
|
*
|
||||||
* @param message 不可为空, 只能是String或者byte[]
|
|
||||||
* @param last 是否最后一条
|
|
||||||
* @return 0表示成功, 非0表示错误码
|
* @return 0表示成功, 非0表示错误码
|
||||||
*/
|
*/
|
||||||
public final int send(Serializable message, boolean last) {
|
public final int send(Object message) {
|
||||||
return send(new WebSocketPacket(message, last));
|
return send(message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给自身发送消息, 消息类型是String或byte[]或可JSON化对象
|
||||||
|
*
|
||||||
|
* @param message 不可为空, 只能是String或byte[]或可JSON化对象
|
||||||
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
|
* @return 0表示成功, 非0表示错误码
|
||||||
|
*/
|
||||||
|
public final int send(Object message, boolean last) {
|
||||||
|
if (message == null || message instanceof CharSequence || message instanceof byte[]) {
|
||||||
|
return send(new WebSocketPacket((Serializable) message, last));
|
||||||
|
} else {
|
||||||
|
return send(new WebSocketPacket(_jsonConvert.convertTo(message), last));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
@@ -170,6 +197,7 @@ public abstract class WebSocket {
|
|||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param text 不可为空
|
* @param text 不可为空
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendEachMessage(Serializable groupid, String text) {
|
public final int sendEachMessage(Serializable groupid, String text) {
|
||||||
@@ -181,10 +209,23 @@ public abstract class WebSocket {
|
|||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param data 不可为空
|
* @param data 不可为空
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendEachMessage(Serializable groupid, byte[] data) {
|
public final int sendEachMessage(Serializable groupid, byte[] data) {
|
||||||
return WebSocket.this.sendEachMessage(groupid, data, true);
|
return sendEachMessage(groupid, data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送可JSON化对象消息
|
||||||
|
*
|
||||||
|
* @param groupid groupid
|
||||||
|
* @param message 不可为空
|
||||||
|
*
|
||||||
|
* @return 为0表示成功, 其他值表示异常
|
||||||
|
*/
|
||||||
|
public final int sendEachMessage(Serializable groupid, Object message) {
|
||||||
|
return sendEachMessage(groupid, message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,6 +234,7 @@ public abstract class WebSocket {
|
|||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param text 不可为空
|
* @param text 不可为空
|
||||||
* @param last 是否最后一条
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendEachMessage(Serializable groupid, String text, boolean last) {
|
public final int sendEachMessage(Serializable groupid, String text, boolean last) {
|
||||||
@@ -205,17 +247,32 @@ public abstract class WebSocket {
|
|||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param data 不可为空
|
* @param data 不可为空
|
||||||
* @param last 是否最后一条
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
|
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
|
||||||
return sendMessage(groupid, false, data, last);
|
return sendMessage(groupid, false, data, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给指定groupid的WebSocketGroup下所有WebSocket节点发送可JSON化对象消息
|
||||||
|
*
|
||||||
|
* @param groupid groupid
|
||||||
|
* @param message 不可为空
|
||||||
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
|
* @return 为0表示成功, 其他值表示异常
|
||||||
|
*/
|
||||||
|
public final int sendEachMessage(Serializable groupid, Object message, boolean last) {
|
||||||
|
return sendMessage(groupid, false, message, last);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送文本消息
|
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送文本消息
|
||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param text 不可为空
|
* @param text 不可为空
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendRecentMessage(Serializable groupid, String text) {
|
public final int sendRecentMessage(Serializable groupid, String text) {
|
||||||
@@ -227,18 +284,32 @@ public abstract class WebSocket {
|
|||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param data 不可为空
|
* @param data 不可为空
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendRecentMessage(Serializable groupid, byte[] data) {
|
public final int sendRecentMessage(Serializable groupid, byte[] data) {
|
||||||
return sendRecentMessage(groupid, data, true);
|
return sendRecentMessage(groupid, data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送可JSON化对象消息
|
||||||
|
*
|
||||||
|
* @param groupid groupid
|
||||||
|
* @param message 不可为空
|
||||||
|
*
|
||||||
|
* @return 为0表示成功, 其他值表示异常
|
||||||
|
*/
|
||||||
|
public final int sendRecentMessage(Serializable groupid, Object message) {
|
||||||
|
return sendMessage(groupid, true, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送文本消息
|
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送文本消息
|
||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param text 不可为空
|
* @param text 不可为空
|
||||||
* @param last 是否最后一条
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
|
public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
|
||||||
@@ -251,12 +322,26 @@ public abstract class WebSocket {
|
|||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
* @param data 不可为空
|
* @param data 不可为空
|
||||||
* @param last 是否最后一条
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
* @return 为0表示成功, 其他值表示异常
|
* @return 为0表示成功, 其他值表示异常
|
||||||
*/
|
*/
|
||||||
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
|
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
|
||||||
return sendMessage(groupid, true, data, last);
|
return sendMessage(groupid, true, data, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给指定groupid的WebSocketGroup下最近接入的WebSocket节点发送可JSON化对象消息
|
||||||
|
*
|
||||||
|
* @param groupid groupid
|
||||||
|
* @param message 不可为空
|
||||||
|
* @param last 是否最后一条
|
||||||
|
*
|
||||||
|
* @return 为0表示成功, 其他值表示异常
|
||||||
|
*/
|
||||||
|
public final int sendRecentMessage(Serializable groupid, Object message, boolean last) {
|
||||||
|
return sendMessage(groupid, true, message, last);
|
||||||
|
}
|
||||||
|
|
||||||
private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
|
private int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
|
||||||
if (_engine.node == null) return RETCODE_NODESERVICE_NULL;
|
if (_engine.node == null) return RETCODE_NODESERVICE_NULL;
|
||||||
int rs = _engine.node.sendMessage(groupid, recent, text, last);
|
int rs = _engine.node.sendMessage(groupid, recent, text, last);
|
||||||
@@ -271,10 +356,18 @@ public abstract class WebSocket {
|
|||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int sendMessage(Serializable groupid, boolean recent, Object message, boolean last) {
|
||||||
|
if (_engine.node == null) return RETCODE_NODESERVICE_NULL;
|
||||||
|
int rs = _engine.node.sendMessage(groupid, recent, message, last);
|
||||||
|
if (_engine.finest) _engine.logger.finest("wsgroupid:" + groupid + " " + (recent ? "recent " : "") + "send websocket result is " + rs + " on " + this + " by message(" + _jsonConvert.convertTo(message) + ")");
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取在线用户的节点地址列表
|
* 获取指定groupid在线用户的节点地址列表
|
||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
|
*
|
||||||
* @return 地址列表
|
* @return 地址列表
|
||||||
*/
|
*/
|
||||||
protected final Collection<InetSocketAddress> getOnlineNodes(Serializable groupid) {
|
protected final Collection<InetSocketAddress> getOnlineNodes(Serializable groupid) {
|
||||||
@@ -282,9 +375,10 @@ public abstract class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取在线用户的详细连接信息
|
* 获取指定groupid在线用户的详细连接信息
|
||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
|
*
|
||||||
* @return 地址集合
|
* @return 地址集合
|
||||||
*/
|
*/
|
||||||
protected final Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(Serializable groupid) {
|
protected final Map<InetSocketAddress, List<String>> getOnlineRemoteAddress(Serializable groupid) {
|
||||||
@@ -296,6 +390,7 @@ public abstract class WebSocket {
|
|||||||
*
|
*
|
||||||
* @param <T> 属性值的类型
|
* @param <T> 属性值的类型
|
||||||
* @param name 属性名
|
* @param name 属性名
|
||||||
|
*
|
||||||
* @return 属性值
|
* @return 属性值
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -308,6 +403,7 @@ public abstract class WebSocket {
|
|||||||
*
|
*
|
||||||
* @param <T> 属性值的类型
|
* @param <T> 属性值的类型
|
||||||
* @param name 属性名
|
* @param name 属性名
|
||||||
|
*
|
||||||
* @return 属性值
|
* @return 属性值
|
||||||
*/
|
*/
|
||||||
public final <T> T removeAttribute(String name) {
|
public final <T> T removeAttribute(String name) {
|
||||||
@@ -374,6 +470,7 @@ public abstract class WebSocket {
|
|||||||
* 获取指定groupid的WebSocketGroup, 没有返回null
|
* 获取指定groupid的WebSocketGroup, 没有返回null
|
||||||
*
|
*
|
||||||
* @param groupid groupid
|
* @param groupid groupid
|
||||||
|
*
|
||||||
* @return WebSocketGroup
|
* @return WebSocketGroup
|
||||||
*/
|
*/
|
||||||
protected final WebSocketGroup getWebSocketGroup(Serializable groupid) {
|
protected final WebSocketGroup getWebSocketGroup(Serializable groupid) {
|
||||||
@@ -394,6 +491,7 @@ public abstract class WebSocket {
|
|||||||
* 返回sessionid, null表示连接不合法或异常,默认实现是request.getSessionid(false),通常需要重写该方法
|
* 返回sessionid, null表示连接不合法或异常,默认实现是request.getSessionid(false),通常需要重写该方法
|
||||||
*
|
*
|
||||||
* @param request HttpRequest
|
* @param request HttpRequest
|
||||||
|
*
|
||||||
* @return sessionid
|
* @return sessionid
|
||||||
*/
|
*/
|
||||||
public Serializable onOpen(final HttpRequest request) {
|
public Serializable onOpen(final HttpRequest request) {
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import org.redkale.util.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* <p> 详情见: https://redkale.org
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
|
*
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
public final class WebSocketEngine {
|
public final class WebSocketEngine {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public final class WebSocketGroup {
|
|||||||
attributes.put(name, value);
|
attributes.put(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int send(boolean recent, Serializable message, boolean last) {
|
public final int send(boolean recent, Object message, boolean last) {
|
||||||
if (recent) {
|
if (recent) {
|
||||||
return recentWebSocket.send(message, last);
|
return recentWebSocket.send(message, last);
|
||||||
} else {
|
} else {
|
||||||
@@ -91,7 +91,7 @@ public final class WebSocketGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendEach(Serializable message) {
|
public final int sendEach(Object message) {
|
||||||
return sendEach(message, true);
|
return sendEach(message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ public final class WebSocketGroup {
|
|||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendRecent(Serializable message) {
|
public final int sendRecent(Object message) {
|
||||||
return sendRecent(message, true);
|
return sendRecent(message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +119,10 @@ public final class WebSocketGroup {
|
|||||||
return recentWebSocket.send(packet);
|
return recentWebSocket.send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendEach(Serializable message, boolean last) {
|
public final int sendEach(Object message, boolean last) {
|
||||||
|
if (message != null && !(message instanceof byte[]) && !(message instanceof CharSequence)) {
|
||||||
|
message = recentWebSocket._jsonConvert.convertTo(message);
|
||||||
|
}
|
||||||
int rs = 0;
|
int rs = 0;
|
||||||
for (WebSocket s : list) {
|
for (WebSocket s : list) {
|
||||||
rs |= s.send(message, last);
|
rs |= s.send(message, last);
|
||||||
@@ -127,7 +130,7 @@ public final class WebSocketGroup {
|
|||||||
return rs;
|
return rs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendRecent(Serializable message, boolean last) {
|
public final int sendRecent(Object message, boolean last) {
|
||||||
return recentWebSocket.send(message, last);
|
return recentWebSocket.send(message, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public abstract class WebSocketNode {
|
|||||||
|
|
||||||
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合
|
//存放所有用户分布在节点上的队列信息,Set<InetSocketAddress> 为 sncpnode 的集合
|
||||||
@Resource(name = "$")
|
@Resource(name = "$")
|
||||||
protected CacheSource<Serializable, InetSocketAddress> source;
|
protected CacheSource<Serializable, InetSocketAddress> sncpNodes;
|
||||||
|
|
||||||
//存放本地节点上所有在线用户的队列信息,Set<String> 为 engineid 的集合
|
//存放本地节点上所有在线用户的队列信息,Set<String> 为 engineid 的集合
|
||||||
protected final ConcurrentHashMap<Serializable, Set<String>> localNodes = new ConcurrentHashMap();
|
protected final ConcurrentHashMap<Serializable, Set<String>> localNodes = new ConcurrentHashMap();
|
||||||
@@ -60,7 +60,7 @@ public abstract class WebSocketNode {
|
|||||||
|
|
||||||
protected abstract List<String> getOnlineRemoteAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid);
|
protected abstract List<String> getOnlineRemoteAddresses(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid);
|
||||||
|
|
||||||
protected abstract int sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid, boolean recent, Serializable message, boolean last);
|
protected abstract int sendMessage(@RpcTargetAddress InetSocketAddress targetAddress, Serializable groupid, boolean recent, Object message, boolean last);
|
||||||
|
|
||||||
protected abstract void connect(Serializable groupid, InetSocketAddress addr);
|
protected abstract void connect(Serializable groupid, InetSocketAddress addr);
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ public abstract class WebSocketNode {
|
|||||||
* @return 地址列表
|
* @return 地址列表
|
||||||
*/
|
*/
|
||||||
public Collection<InetSocketAddress> getOnlineNodes(final Serializable groupid) {
|
public Collection<InetSocketAddress> getOnlineNodes(final Serializable groupid) {
|
||||||
return source == null ? null : source.getCollection(groupid);
|
return sncpNodes == null ? null : sncpNodes.getCollection(groupid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,7 +133,7 @@ public abstract class WebSocketNode {
|
|||||||
engines.put(engine.getEngineid(), engine);
|
engines.put(engine.getEngineid(), engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendMessage(Serializable groupid, boolean recent, Serializable message, boolean last) {
|
public final int sendMessage(Serializable groupid, boolean recent, Object message, boolean last) {
|
||||||
final Set<String> engineids = localNodes.get(groupid);
|
final Set<String> engineids = localNodes.get(groupid);
|
||||||
if (finest) logger.finest("websocket want send message {groupid:" + groupid + ", content:'" + message + "'} from locale node to " + engineids);
|
if (finest) logger.finest("websocket want send message {groupid:" + groupid + ", content:'" + message + "'} from locale node to " + engineids);
|
||||||
int rscode = RETCODE_GROUP_EMPTY;
|
int rscode = RETCODE_GROUP_EMPTY;
|
||||||
@@ -152,7 +152,7 @@ public abstract class WebSocketNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((recent && rscode == 0) || remoteNode == null || source == null) {
|
if ((recent && rscode == 0) || remoteNode == null || sncpNodes == null) {
|
||||||
if (finest) {
|
if (finest) {
|
||||||
if ((recent && rscode == 0)) {
|
if ((recent && rscode == 0)) {
|
||||||
logger.finest("websocket want send recent message success");
|
logger.finest("websocket want send recent message success");
|
||||||
@@ -163,7 +163,7 @@ public abstract class WebSocketNode {
|
|||||||
return rscode;
|
return rscode;
|
||||||
}
|
}
|
||||||
//-----------------------发送远程的-----------------------------
|
//-----------------------发送远程的-----------------------------
|
||||||
Collection<InetSocketAddress> addrs = source.getCollection(groupid);
|
Collection<InetSocketAddress> addrs = sncpNodes.getCollection(groupid);
|
||||||
if (finest) logger.finest("websocket found groupid:" + groupid + " on " + addrs);
|
if (finest) logger.finest("websocket found groupid:" + groupid + " on " + addrs);
|
||||||
if (addrs != null && !addrs.isEmpty()) { //对方连接在远程节点(包含本地节点),所以正常情况下addrs不会为空。
|
if (addrs != null && !addrs.isEmpty()) { //对方连接在远程节点(包含本地节点),所以正常情况下addrs不会为空。
|
||||||
if (recent) {
|
if (recent) {
|
||||||
@@ -187,44 +187,44 @@ public abstract class WebSocketNode {
|
|||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
public final int sendEachMessage(Serializable groupid, String text) {
|
public final int sendEachMessage(Serializable groupid, String text) {
|
||||||
return sendMessage(groupid, false, text);
|
return sendMessage(groupid, false, (Object) text, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendEachMessage(Serializable groupid, String text, boolean last) {
|
public final int sendEachMessage(Serializable groupid, String text, boolean last) {
|
||||||
return sendMessage(groupid, false, text, last);
|
return sendMessage(groupid, false, (Object) text, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendRecentMessage(Serializable groupid, String text) {
|
public final int sendRecentMessage(Serializable groupid, String text) {
|
||||||
return sendMessage(groupid, true, text);
|
return sendMessage(groupid, true, (Object) text, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
|
public final int sendRecentMessage(Serializable groupid, String text, boolean last) {
|
||||||
return sendMessage(groupid, true, text, last);
|
return sendMessage(groupid, true, (Object) text, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendMessage(Serializable groupid, boolean recent, String text) {
|
public final int sendMessage(Serializable groupid, boolean recent, String text) {
|
||||||
return sendMessage(groupid, recent, text, true);
|
return sendMessage(groupid, recent, (Object) text, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
|
public final int sendMessage(Serializable groupid, boolean recent, String text, boolean last) {
|
||||||
return sendMessage(groupid, recent, (Serializable) text, last);
|
return sendMessage(groupid, recent, (Object) text, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
public final int sendEachMessage(Serializable groupid, byte[] data) {
|
public final int sendEachMessage(Serializable groupid, byte[] data) {
|
||||||
return sendMessage(groupid, false, data);
|
return sendMessage(groupid, false, (Object) data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
|
public final int sendEachMessage(Serializable groupid, byte[] data, boolean last) {
|
||||||
return sendMessage(groupid, false, data, last);
|
return sendMessage(groupid, false, (Object) data, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendRecentMessage(Serializable groupid, byte[] data) {
|
public final int sendRecentMessage(Serializable groupid, byte[] data) {
|
||||||
return sendMessage(groupid, true, data);
|
return sendMessage(groupid, true, (Object) data, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
|
public final int sendRecentMessage(Serializable groupid, byte[] data, boolean last) {
|
||||||
return sendMessage(groupid, true, data, last);
|
return sendMessage(groupid, true, (Object) data, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int sendMessage(Serializable groupid, boolean recent, byte[] data) {
|
public final int sendMessage(Serializable groupid, boolean recent, byte[] data) {
|
||||||
@@ -232,6 +232,28 @@ public abstract class WebSocketNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
|
public final int sendMessage(Serializable groupid, boolean recent, byte[] data, boolean last) {
|
||||||
return sendMessage(groupid, recent, (Serializable) data, last);
|
return sendMessage(groupid, recent, (Object) data, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
public final int sendEachMessage(Serializable groupid, Object message) {
|
||||||
|
return sendMessage(groupid, false, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int sendEachMessage(Serializable groupid, Object message, boolean last) {
|
||||||
|
return sendMessage(groupid, false, message, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int sendRecentMessage(Serializable groupid, Object message) {
|
||||||
|
return sendMessage(groupid, true, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int sendRecentMessage(Serializable groupid, Object message, boolean last) {
|
||||||
|
return sendMessage(groupid, true, message, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int sendMessage(Serializable groupid, boolean recent, Object message) {
|
||||||
|
return sendMessage(groupid, recent, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ package org.redkale.net.http;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.nio.channels.*;
|
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.*;
|
import java.util.logging.*;
|
||||||
import javax.annotation.*;
|
import javax.annotation.*;
|
||||||
|
import org.redkale.convert.json.JsonConvert;
|
||||||
import org.redkale.service.WebSocketNodeService;
|
import org.redkale.service.WebSocketNodeService;
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ import org.redkale.util.*;
|
|||||||
* WebSocketServlet
|
* WebSocketServlet
|
||||||
* |
|
* |
|
||||||
* |
|
* |
|
||||||
* WebSocketEngine & WebSocketNode
|
* WebSocketEngine
|
||||||
|
* WebSocketNode
|
||||||
* / \
|
* / \
|
||||||
* / \
|
* / \
|
||||||
* / \
|
* / \
|
||||||
@@ -39,10 +40,12 @@ import org.redkale.util.*;
|
|||||||
*
|
*
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
*/
|
*/
|
||||||
public abstract class WebSocketServlet extends HttpServlet {
|
public abstract class WebSocketServlet extends HttpServlet implements Resourcable {
|
||||||
|
|
||||||
|
@Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒")
|
||||||
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
|
public static final String WEBPARAM__LIVEINTERVAL = "liveinterval";
|
||||||
|
|
||||||
|
@Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒")
|
||||||
public static final int DEFAILT_LIVEINTERVAL = 60;
|
public static final int DEFAILT_LIVEINTERVAL = 60;
|
||||||
|
|
||||||
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
|
||||||
@@ -57,9 +60,12 @@ public abstract class WebSocketServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//是否用于二进制流传输
|
@Comment("是否用于二进制流传输")
|
||||||
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
|
protected final boolean wsbinary = getClass().getAnnotation(WebSocketBinary.class) != null;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
protected JsonConvert jsonConvert;
|
||||||
|
|
||||||
@Resource(name = "$")
|
@Resource(name = "$")
|
||||||
protected WebSocketNode node;
|
protected WebSocketNode node;
|
||||||
|
|
||||||
@@ -67,7 +73,7 @@ public abstract class WebSocketServlet extends HttpServlet {
|
|||||||
|
|
||||||
public final void preInit(HttpContext context, AnyValue conf) {
|
public final void preInit(HttpContext context, AnyValue conf) {
|
||||||
InetSocketAddress addr = context.getServerAddress();
|
InetSocketAddress addr = context.getServerAddress();
|
||||||
this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-[" + name() + "]", this.node, logger);
|
this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.node, logger);
|
||||||
if (this.node == null) this.node = createWebSocketNode();
|
if (this.node == null) this.node = createWebSocketNode();
|
||||||
if (this.node == null) {
|
if (this.node == null) {
|
||||||
this.node = new WebSocketNodeService();
|
this.node = new WebSocketNodeService();
|
||||||
@@ -84,7 +90,8 @@ public abstract class WebSocketServlet extends HttpServlet {
|
|||||||
engine.close();
|
engine.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String name() {
|
@Override
|
||||||
|
public String resourceName() {
|
||||||
return this.getClass().getSimpleName().replace("Servlet", "").replace("WebSocket", "").toLowerCase();
|
return this.getClass().getSimpleName().replace("Servlet", "").replace("WebSocket", "").toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +113,7 @@ public abstract class WebSocketServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
final WebSocket webSocket = this.createWebSocket();
|
final WebSocket webSocket = this.createWebSocket();
|
||||||
webSocket._engine = engine;
|
webSocket._engine = engine;
|
||||||
|
webSocket._jsonConvert = jsonConvert;
|
||||||
webSocket._remoteAddress = request.getRemoteAddress();
|
webSocket._remoteAddress = request.getRemoteAddress();
|
||||||
webSocket._remoteAddr = request.getRemoteAddr();
|
webSocket._remoteAddr = request.getRemoteAddr();
|
||||||
Serializable sessionid = webSocket.onOpen(request);
|
Serializable sessionid = webSocket.onOpen(request);
|
||||||
@@ -125,7 +133,7 @@ public abstract class WebSocketServlet extends HttpServlet {
|
|||||||
response.setHeader("Connection", "Upgrade");
|
response.setHeader("Connection", "Upgrade");
|
||||||
response.addHeader("Upgrade", "websocket");
|
response.addHeader("Upgrade", "websocket");
|
||||||
response.addHeader("Sec-WebSocket-Accept", key);
|
response.addHeader("Sec-WebSocket-Accept", key);
|
||||||
response.sendBody((ByteBuffer) null, null, new CompletionHandler<Integer, Void>() {
|
response.sendBody((ByteBuffer) null, null, new AsyncHandler<Integer, Void>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void completed(Integer result, Void attachment) {
|
public void completed(Integer result, Void attachment) {
|
||||||
@@ -138,7 +146,7 @@ public abstract class WebSocketServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
webSocket._groupid = groupid;
|
webSocket._groupid = groupid;
|
||||||
engine.add(webSocket);
|
engine.add(webSocket);
|
||||||
context.submit(new WebSocketRunner(context, webSocket, response.removeChannel(), wsbinary));
|
context.runAsync(new WebSocketRunner(context, webSocket, response.removeChannel(), wsbinary));
|
||||||
response.finish(true);
|
response.finish(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.net.sncp;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import org.redkale.service.Service;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Service对象的封装类
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* 详情见: https://redkale.org
|
|
||||||
*
|
|
||||||
* @author zhangjx
|
|
||||||
* @param <T> Service的子类
|
|
||||||
*/
|
|
||||||
public final class ServiceWrapper<T extends Service> implements Comparable<ServiceWrapper> {
|
|
||||||
|
|
||||||
private static volatile int maxClassNameLength = 0;
|
|
||||||
|
|
||||||
private static volatile int maxNameLength = 0;
|
|
||||||
|
|
||||||
private final T service;
|
|
||||||
|
|
||||||
private final AnyValue conf;
|
|
||||||
|
|
||||||
private final String sncpGroup; //自身的组节点名 可能为null
|
|
||||||
|
|
||||||
private final Set<String> groups; //所有的组节点,包含自身
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private final boolean remote;
|
|
||||||
|
|
||||||
private final Class[] types;
|
|
||||||
|
|
||||||
public ServiceWrapper(T service, String name, String sncpGroup, Set<String> groups, AnyValue conf) {
|
|
||||||
this(null, service, name, sncpGroup, groups, conf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public ServiceWrapper(Class<T> type, T service, String name, String sncpGroup, Set<String> groups, AnyValue conf) {
|
|
||||||
this.service = service;
|
|
||||||
this.conf = conf;
|
|
||||||
this.sncpGroup = sncpGroup;
|
|
||||||
this.groups = groups;
|
|
||||||
this.name = name;
|
|
||||||
this.remote = Sncp.isRemote(service);
|
|
||||||
ResourceType rty = service.getClass().getAnnotation(ResourceType.class);
|
|
||||||
this.types = rty == null ? new Class[]{type == null ? (Class<T>) service.getClass() : type} : rty.value();
|
|
||||||
|
|
||||||
maxNameLength = Math.max(maxNameLength, name.length());
|
|
||||||
StringBuilder s = new StringBuilder();
|
|
||||||
if (this.types.length == 1) {
|
|
||||||
s.append(types[0].getName());
|
|
||||||
} else {
|
|
||||||
s.append('[');
|
|
||||||
s.append(Arrays.asList(this.types).stream().map((Class t) -> t.getName()).collect(Collectors.joining(",")));
|
|
||||||
s.append(']');
|
|
||||||
}
|
|
||||||
maxClassNameLength = Math.max(maxClassNameLength, s.length() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Class[] parseTypes(final Class<? extends Service> servicetype) {
|
|
||||||
ResourceType rty = servicetype.getAnnotation(ResourceType.class);
|
|
||||||
return rty == null ? new Class[]{servicetype} : rty.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return toSimpleString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toSimpleString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(remote ? "RemoteService" : "LocalService ");
|
|
||||||
int len;
|
|
||||||
if (types.length == 1) {
|
|
||||||
sb.append("(type= ").append(types[0].getName());
|
|
||||||
len = maxClassNameLength - types[0].getName().length();
|
|
||||||
} else {
|
|
||||||
StringBuilder s = new StringBuilder();
|
|
||||||
s.append('[');
|
|
||||||
s.append(Arrays.asList(this.types).stream().map((Class t) -> t.getName()).collect(Collectors.joining(",")));
|
|
||||||
s.append(']');
|
|
||||||
sb.append("(types=").append(s);
|
|
||||||
len = maxClassNameLength - s.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
sb.append(' ');
|
|
||||||
}
|
|
||||||
sb.append(", name='").append(name).append("'");
|
|
||||||
for (int i = 0; i < maxNameLength - name.length(); i++) {
|
|
||||||
sb.append(' ');
|
|
||||||
}
|
|
||||||
sb.append(")");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj == this) return true;
|
|
||||||
if (obj == null) return false;
|
|
||||||
if (!(obj instanceof ServiceWrapper)) return false;
|
|
||||||
ServiceWrapper other = (ServiceWrapper) obj;
|
|
||||||
return (this.types[0].equals(other.types[0]) && this.remote == other.remote && this.name.equals(other.name) && Objects.equals(this.sncpGroup, other.sncpGroup));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int hash = 3;
|
|
||||||
hash = 67 * hash + Objects.hashCode(this.types[0]);
|
|
||||||
hash = 67 * hash + Objects.hashCode(this.sncpGroup);
|
|
||||||
hash = 67 * hash + Objects.hashCode(this.name);
|
|
||||||
hash = 67 * hash + (this.remote ? 1 : 0);
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(ServiceWrapper o) {
|
|
||||||
int rs = this.types[0].getName().compareTo(o.types[0].getName());
|
|
||||||
if (rs == 0) rs = this.name.compareTo(o.name);
|
|
||||||
return rs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class[] getTypes() {
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Service getService() {
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnyValue getConf() {
|
|
||||||
return conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRemote() {
|
|
||||||
return remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getGroups() {
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,7 @@ import java.net.InetSocketAddress;
|
|||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
||||||
import jdk.internal.org.objectweb.asm.*;
|
import jdk.internal.org.objectweb.asm.*;
|
||||||
@@ -55,11 +56,6 @@ public abstract class Sncp {
|
|||||||
private Sncp() {
|
private Sncp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long nodeid(InetSocketAddress ip) {
|
|
||||||
byte[] bytes = ip.getAddress().getAddress();
|
|
||||||
return ((0L + ip.getPort()) << 32) | ((0xffffffff & bytes[0]) << 24) | ((0xffffff & bytes[1]) << 16) | ((0xffff & bytes[2]) << 8) | (0xff & bytes[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DLong hash(final java.lang.reflect.Method method) {
|
public static DLong hash(final java.lang.reflect.Method method) {
|
||||||
if (method == null) return DLong.ZERO;
|
if (method == null) return DLong.ZERO;
|
||||||
StringBuilder sb = new StringBuilder(); //不能使用method.toString() 因为包含declaringClass信息导致接口与实现类的方法hash不一致
|
StringBuilder sb = new StringBuilder(); //不能使用method.toString() 因为包含declaringClass信息导致接口与实现类的方法hash不一致
|
||||||
@@ -97,6 +93,62 @@ public abstract class Sncp {
|
|||||||
return dyn != null && dyn.remote();
|
return dyn != null && dyn.remote();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getResourceName(Service service) {
|
||||||
|
if (service == null) return null;
|
||||||
|
Resource res = service.getClass().getAnnotation(Resource.class);
|
||||||
|
return res == null ? null : res.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class getServiceType(Service service) {
|
||||||
|
if (service == null) return null;
|
||||||
|
try {
|
||||||
|
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_service_type");
|
||||||
|
ts.setAccessible(true);
|
||||||
|
return (Class) ts.get(service);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_service_type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class[] getResourceTypes(Service service) {
|
||||||
|
if (service == null) return null;
|
||||||
|
ResourceType types = service.getClass().getAnnotation(ResourceType.class);
|
||||||
|
return types == null ? new Class[]{getServiceType(service)} : types.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSncpGroup(Service service) {
|
||||||
|
if (service == null) return null;
|
||||||
|
try {
|
||||||
|
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_sncpGroup");
|
||||||
|
ts.setAccessible(true);
|
||||||
|
return (String) ts.get(service);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_sncpGroup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getGroups(Service service) {
|
||||||
|
if (service == null) return null;
|
||||||
|
try {
|
||||||
|
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_groups");
|
||||||
|
ts.setAccessible(true);
|
||||||
|
return (Set) ts.get(service);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_groups");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AnyValue getConf(Service service) {
|
||||||
|
if (service == null) return null;
|
||||||
|
try {
|
||||||
|
Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_conf");
|
||||||
|
ts.setAccessible(true);
|
||||||
|
return (AnyValue) ts.get(service);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(service + " not found " + FIELDPREFIX + "_conf");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static SncpClient getSncpClient(Service service) {
|
public static SncpClient getSncpClient(Service service) {
|
||||||
if (service == null) return null;
|
if (service == null) return null;
|
||||||
try {
|
try {
|
||||||
@@ -130,6 +182,73 @@ public abstract class Sncp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void checkAsyncModifier(Class param, Method method) {
|
||||||
|
if (param == AsyncHandler.class) return;
|
||||||
|
if (Modifier.isFinal(param.getModifiers())) {
|
||||||
|
throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} cannot final modifier");
|
||||||
|
}
|
||||||
|
if (!Modifier.isPublic(param.getModifiers())) {
|
||||||
|
throw new RuntimeException("AsyncHandler Type Parameter on {" + method + "} must be public modifier");
|
||||||
|
}
|
||||||
|
if (param.isInterface()) return;
|
||||||
|
boolean constructorflag = false;
|
||||||
|
for (Constructor c : param.getDeclaredConstructors()) {
|
||||||
|
if (c.getParameterCount() == 0) {
|
||||||
|
int mod = c.getModifiers();
|
||||||
|
if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) {
|
||||||
|
constructorflag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (param.getDeclaredConstructors().length == 0) constructorflag = true;
|
||||||
|
if (!constructorflag) throw new RuntimeException(param + " must have a empty parameter Constructor");
|
||||||
|
for (Method m : param.getMethods()) {
|
||||||
|
if (m.getName().equals("completed") && Modifier.isFinal(m.getModifiers())) {
|
||||||
|
throw new RuntimeException(param + "'s completed method cannot final modifier");
|
||||||
|
} else if (m.getName().equals("failed") && Modifier.isFinal(m.getModifiers())) {
|
||||||
|
throw new RuntimeException(param + "'s failed method cannot final modifier");
|
||||||
|
} else if (m.getName().equals("sncp_getParams") && Modifier.isFinal(m.getModifiers())) {
|
||||||
|
throw new RuntimeException(param + "'s sncp_getParams method cannot final modifier");
|
||||||
|
} else if (m.getName().equals("sncp_setParams") && Modifier.isFinal(m.getModifiers())) {
|
||||||
|
throw new RuntimeException(param + "'s sncp_setParams method cannot final modifier");
|
||||||
|
} else if (m.getName().equals("sncp_setFuture") && Modifier.isFinal(m.getModifiers())) {
|
||||||
|
throw new RuntimeException(param + "'s sncp_setFuture method cannot final modifier");
|
||||||
|
} else if (m.getName().equals("sncp_getFuture") && Modifier.isFinal(m.getModifiers())) {
|
||||||
|
throw new RuntimeException(param + "'s sncp_getFuture method cannot final modifier");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toSimpleString(final Service service, int maxNameLength, int maxClassNameLength) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(isRemote(service) ? "RemoteService" : "LocalService ");
|
||||||
|
int len;
|
||||||
|
Class[] types = getResourceTypes(service);
|
||||||
|
String name = getResourceName(service);
|
||||||
|
if (types.length == 1) {
|
||||||
|
sb.append("(type= ").append(types[0].getName());
|
||||||
|
len = maxClassNameLength - types[0].getName().length();
|
||||||
|
} else {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append('[');
|
||||||
|
s.append(Arrays.asList(types).stream().map((Class t) -> t.getName()).collect(Collectors.joining(",")));
|
||||||
|
s.append(']');
|
||||||
|
sb.append("(types=").append(s);
|
||||||
|
len = maxClassNameLength - s.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
sb.append(' ');
|
||||||
|
}
|
||||||
|
sb.append(", name='").append(name).append("'");
|
||||||
|
for (int i = 0; i < maxNameLength - name.length(); i++) {
|
||||||
|
sb.append(' ');
|
||||||
|
}
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* public class TestService implements Service{
|
* public class TestService implements Service{
|
||||||
@@ -139,16 +258,16 @@ public abstract class Sncp {
|
|||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @RpcMultiRun(selfrun = false)
|
* @RpcMultiRun(selfrun = false)
|
||||||
public void createSomeThing(TestBean bean){
|
* public void createSomeThing(TestBean bean){
|
||||||
//do something
|
* //do something
|
||||||
}
|
* }
|
||||||
|
*
|
||||||
@RpcMultiRun
|
* @RpcMultiRun
|
||||||
public String updateSomeThing(String id){
|
* public String updateSomeThing(String id){
|
||||||
return "hello" + id;
|
* return "hello" + id;
|
||||||
}
|
* }
|
||||||
}
|
* }
|
||||||
</pre></blockquote>
|
* </pre></blockquote>
|
||||||
*
|
*
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* @Resource(name = "")
|
* @Resource(name = "")
|
||||||
@@ -156,12 +275,20 @@ public abstract class Sncp {
|
|||||||
* @ResourceType({TestService.class})
|
* @ResourceType({TestService.class})
|
||||||
* public final class _DynLocalTestService extends TestService{
|
* public final class _DynLocalTestService extends TestService{
|
||||||
*
|
*
|
||||||
|
* private static final Class _redkale_service_type = TestService.class;
|
||||||
|
*
|
||||||
* @Resource
|
* @Resource
|
||||||
* private BsonConvert _redkale_bsonConvert;
|
* private BsonConvert _redkale_bsonConvert;
|
||||||
*
|
*
|
||||||
* @Resource
|
* @Resource
|
||||||
* private JsonConvert _redkale_jsonConvert;
|
* private JsonConvert _redkale_jsonConvert;
|
||||||
*
|
*
|
||||||
|
* private AnyValue _redkale_conf;
|
||||||
|
*
|
||||||
|
* private String _redkale_sncpGroup; //自身的组节点名 可能为null
|
||||||
|
*
|
||||||
|
* private Set<String> groups; //所有的组节点,包含自身
|
||||||
|
*
|
||||||
* private Transport _redkale_sameGroupTransport;
|
* private Transport _redkale_sameGroupTransport;
|
||||||
*
|
*
|
||||||
* private Transport[] _redkale_diffGroupTransports;
|
* private Transport[] _redkale_diffGroupTransports;
|
||||||
@@ -184,8 +311,8 @@ public abstract class Sncp {
|
|||||||
* public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){
|
* public void _redkale_createSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, TestBean bean){
|
||||||
* if(selfrunnable) super.createSomeThing(bean);
|
* if(selfrunnable) super.createSomeThing(bean);
|
||||||
* if (_redkale_client== null) return;
|
* if (_redkale_client== null) return;
|
||||||
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_bsonConvert, _redkale_jsonConvert, _sameGroupTransport, 0, true, false, false, bean);
|
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_sameGroupTransport, 0, true, false, false, bean);
|
||||||
* if (diffrunnable) _redkale_client.remoteDiffGroup(_redkale_bsonConvert, _redkale_jsonConvert, _diffGroupTransports, 0, true, true, false, bean);
|
* if (diffrunnable) _redkale_client.remoteDiffGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_diffGroupTransports, 0, true, true, false, bean);
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* @Override
|
* @Override
|
||||||
@@ -196,9 +323,9 @@ public abstract class Sncp {
|
|||||||
* @SncpDyn(remote = false, index = 1)
|
* @SncpDyn(remote = false, index = 1)
|
||||||
* public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){
|
* public String _redkale_updateSomeThing(boolean selfrunnable, boolean samerunnable, boolean diffrunnable, String id){
|
||||||
* String rs = super.updateSomeThing(id);
|
* String rs = super.updateSomeThing(id);
|
||||||
* if (_redkale_client== null) return;
|
* if (_redkale_client== null) return rs;
|
||||||
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_bsonConvert, _redkale_jsonConvert, _sameGroupTransport, 1, true, false, false, id);
|
* if (samerunnable) _redkale_client.remoteSameGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_sameGroupTransport, 1, true, false, false, id);
|
||||||
* if (diffrunnable) _redkale_client.remoteDiffGroup(_redkale_bsonConvert, _redkale_jsonConvert, _diffGroupTransports, 1, true, true, false, id);
|
* if (diffrunnable) _redkale_client.remoteDiffGroup(_redkale_bsonConvert, _redkale_jsonConvert, _redkale_diffGroupTransports, 1, true, true, false, id);
|
||||||
* return rs;
|
* return rs;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
@@ -208,38 +335,41 @@ public abstract class Sncp {
|
|||||||
*
|
*
|
||||||
* @param <T> Service子类
|
* @param <T> Service子类
|
||||||
* @param name 资源名
|
* @param name 资源名
|
||||||
* @param serviceClass Service类
|
* @param serviceImplClass Service类
|
||||||
*
|
*
|
||||||
* @return Service实例
|
* @return Service实例
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceClass) {
|
protected static <T extends Service> Class<? extends T> createLocalServiceClass(final String name, final Class<T> serviceImplClass) {
|
||||||
if (serviceClass == null) return null;
|
if (serviceImplClass == null) return null;
|
||||||
if (!Service.class.isAssignableFrom(serviceClass)) return serviceClass;
|
if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass;
|
||||||
int mod = serviceClass.getModifiers();
|
int mod = serviceImplClass.getModifiers();
|
||||||
if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceClass;
|
if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceImplClass;
|
||||||
if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceClass;
|
if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceImplClass;
|
||||||
final List<Method> methods = SncpClient.parseMethod(serviceClass);
|
final List<Method> methods = SncpClient.parseMethod(serviceImplClass);
|
||||||
final String supDynName = serviceClass.getName().replace('.', '/');
|
final String supDynName = serviceImplClass.getName().replace('.', '/');
|
||||||
final String clientName = SncpClient.class.getName().replace('.', '/');
|
final String clientName = SncpClient.class.getName().replace('.', '/');
|
||||||
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
||||||
final String bsonConvertDesc = Type.getDescriptor(BsonConvert.class);
|
final String bsonConvertDesc = Type.getDescriptor(BsonConvert.class);
|
||||||
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
|
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
|
||||||
|
final String stringDesc = Type.getDescriptor(String.class);
|
||||||
|
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
|
||||||
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
||||||
final String transportDesc = Type.getDescriptor(Transport.class);
|
final String transportDesc = Type.getDescriptor(Transport.class);
|
||||||
final String transportsDesc = Type.getDescriptor(Transport[].class);
|
final String transportsDesc = Type.getDescriptor(Transport[].class);
|
||||||
ClassLoader loader = Sncp.class.getClassLoader();
|
ClassLoader loader = Sncp.class.getClassLoader();
|
||||||
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceClass.getSimpleName();
|
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName();
|
||||||
if (!name.isEmpty()) {
|
if (!name.isEmpty()) {
|
||||||
boolean normal = true;
|
boolean normal = true;
|
||||||
for (char ch : name.toCharArray()) {
|
for (char ch : name.toCharArray()) {
|
||||||
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) normal = false;
|
if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) normal = false;
|
||||||
}
|
}
|
||||||
|
if (!normal) throw new RuntimeException(serviceImplClass + "'s resource name is illegal, must be 0-9 _ a-z A-Z");
|
||||||
newDynName += "_" + (normal ? name : hash(name));
|
newDynName += "_" + (normal ? name : hash(name));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return (Class<T>) Class.forName(newDynName.replace('/', '.'));
|
return (Class<T>) Class.forName(newDynName.replace('/', '.'));
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||||
@@ -259,7 +389,7 @@ public abstract class Sncp {
|
|||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
}
|
}
|
||||||
{ //给新类加上 原有的Annotation
|
{ //给新类加上 原有的Annotation
|
||||||
for (Annotation ann : serviceClass.getAnnotations()) {
|
for (Annotation ann : serviceImplClass.getAnnotations()) {
|
||||||
if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue;
|
if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue;
|
||||||
visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann);
|
visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann);
|
||||||
}
|
}
|
||||||
@@ -268,9 +398,9 @@ public abstract class Sncp {
|
|||||||
av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true);
|
av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true);
|
||||||
{
|
{
|
||||||
AnnotationVisitor av1 = av0.visitArray("value");
|
AnnotationVisitor av1 = av0.visitArray("value");
|
||||||
ResourceType rty = serviceClass.getAnnotation(ResourceType.class);
|
ResourceType rty = serviceImplClass.getAnnotation(ResourceType.class);
|
||||||
if (rty == null) {
|
if (rty == null) {
|
||||||
av1.visit(null, Type.getType(Type.getDescriptor(serviceClass)));
|
av1.visit(null, Type.getType(Type.getDescriptor(serviceImplClass)));
|
||||||
} else {
|
} else {
|
||||||
for (Class cl : rty.value()) {
|
for (Class cl : rty.value()) {
|
||||||
av1.visit(null, Type.getType(Type.getDescriptor(cl)));
|
av1.visit(null, Type.getType(Type.getDescriptor(cl)));
|
||||||
@@ -280,7 +410,10 @@ public abstract class Sncp {
|
|||||||
}
|
}
|
||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, FIELDPREFIX + "_service_type", "Ljava/lang/Class;", null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null);
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null);
|
||||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||||
@@ -293,6 +426,18 @@ public abstract class Sncp {
|
|||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_sncpGroup", stringDesc, null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_groups", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_sameGroupTransport", transportDesc, null, null);
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_sameGroupTransport", transportDesc, null, null);
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
@@ -310,6 +455,14 @@ public abstract class Sncp {
|
|||||||
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_selfstring", "Ljava/lang/String;", null, null);
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_selfstring", "Ljava/lang/String;", null, null);
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
{//静态构造函数
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null));
|
||||||
|
mv.visitLdcInsn(Type.getType(Type.getDescriptor(serviceImplClass)));
|
||||||
|
mv.visitFieldInsn(PUTSTATIC, newDynName, FIELDPREFIX + "_service_type", "Ljava/lang/Class;");
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(1, 0);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
{ //构造函数
|
{ //构造函数
|
||||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
||||||
//mv.setDebug(true);
|
//mv.setDebug(true);
|
||||||
@@ -362,7 +515,13 @@ public abstract class Sncp {
|
|||||||
mv.visitInsn(mrun.samerun() ? ICONST_1 : ICONST_0);
|
mv.visitInsn(mrun.samerun() ? ICONST_1 : ICONST_0);
|
||||||
mv.visitInsn(mrun.diffrun() ? ICONST_1 : ICONST_0);
|
mv.visitInsn(mrun.diffrun() ? ICONST_1 : ICONST_0);
|
||||||
int varindex = 0;
|
int varindex = 0;
|
||||||
|
boolean handlerFuncFlag = false;
|
||||||
for (Class pt : paramtypes) {
|
for (Class pt : paramtypes) {
|
||||||
|
if (AsyncHandler.class.isAssignableFrom(pt)) {
|
||||||
|
if (handlerFuncFlag) throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
|
||||||
|
checkAsyncModifier(pt, method);
|
||||||
|
handlerFuncFlag = true;
|
||||||
|
}
|
||||||
if (pt.isPrimitive()) {
|
if (pt.isPrimitive()) {
|
||||||
if (pt == long.class) {
|
if (pt == long.class) {
|
||||||
mv.visitVarInsn(LLOAD, ++varindex);
|
mv.visitVarInsn(LLOAD, ++varindex);
|
||||||
@@ -403,8 +562,15 @@ public abstract class Sncp {
|
|||||||
//mv.setDebug(true);
|
//mv.setDebug(true);
|
||||||
{ //给参数加上 Annotation
|
{ //给参数加上 Annotation
|
||||||
final Annotation[][] anns = method.getParameterAnnotations();
|
final Annotation[][] anns = method.getParameterAnnotations();
|
||||||
|
boolean handlerAttachFlag = false;
|
||||||
for (int k = 0; k < anns.length; k++) {
|
for (int k = 0; k < anns.length; k++) {
|
||||||
for (Annotation ann : anns[k]) {
|
for (Annotation ann : anns[k]) {
|
||||||
|
if (ann.annotationType() == RpcAttachment.class) {
|
||||||
|
if (handlerAttachFlag) {
|
||||||
|
throw new RuntimeException(method + " have more than one @RpcAttachment parameter");
|
||||||
|
}
|
||||||
|
handlerAttachFlag = true;
|
||||||
|
}
|
||||||
if (ann instanceof SncpDyn || ann instanceof RpcMultiRun) continue; //必须过滤掉 RpcMultiRun、SncpDyn,否则生成远程模式Service时会出错
|
if (ann instanceof SncpDyn || ann instanceof RpcMultiRun) continue; //必须过滤掉 RpcMultiRun、SncpDyn,否则生成远程模式Service时会出错
|
||||||
visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann);
|
visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann);
|
||||||
}
|
}
|
||||||
@@ -501,7 +667,7 @@ public abstract class Sncp {
|
|||||||
mv.visitVarInsn(ALOAD, 0); //传递 _sameGroupTransport
|
mv.visitVarInsn(ALOAD, 0); //传递 _sameGroupTransport
|
||||||
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_sameGroupTransport", transportDesc);
|
mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_sameGroupTransport", transportDesc);
|
||||||
|
|
||||||
final int preparams = 4; //调用selfrunnable之前的参数个数; _client/_bsonConvert/_jsonConvert/_sameGroupTransport
|
final int preparams = 3; //调用selfrunnable之前的参数个数; _client/_bsonConvert/_jsonConvert/_sameGroupTransport
|
||||||
|
|
||||||
if (index <= 5) { //第几个 SncpAction
|
if (index <= 5) { //第几个 SncpAction
|
||||||
mv.visitInsn(ICONST_0 + index);
|
mv.visitInsn(ICONST_0 + index);
|
||||||
@@ -524,13 +690,13 @@ public abstract class Sncp {
|
|||||||
|
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(DUP);
|
||||||
mv.visitInsn(ICONST_1);
|
mv.visitInsn(ICONST_1);
|
||||||
mv.visitInsn(ICONST_0); //第一个参数 samerunnable
|
mv.visitInsn(ICONST_0); //第二个参数 samerunnable
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||||
mv.visitInsn(AASTORE);
|
mv.visitInsn(AASTORE);
|
||||||
|
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(DUP);
|
||||||
mv.visitInsn(ICONST_2);
|
mv.visitInsn(ICONST_2);
|
||||||
mv.visitInsn(ICONST_0); //第二个参数 diffrunnable
|
mv.visitInsn(ICONST_0); //第三个参数 diffrunnable
|
||||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||||
mv.visitInsn(AASTORE);
|
mv.visitInsn(AASTORE);
|
||||||
|
|
||||||
@@ -719,6 +885,10 @@ public abstract class Sncp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T extends Service> T createSimpleLocalService(final String name, final Class<T> serviceImplClass, final InetSocketAddress clientAddress, final Transport sameGroupTransport) {
|
||||||
|
return createLocalService(name, null, ResourceFactory.root(), serviceImplClass, clientAddress, null, new HashSet<>(), null, sameGroupTransport, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* 创建本地模式Service实例
|
* 创建本地模式Service实例
|
||||||
@@ -727,18 +897,30 @@ public abstract class Sncp {
|
|||||||
* @param name 资源名
|
* @param name 资源名
|
||||||
* @param executor 线程池
|
* @param executor 线程池
|
||||||
* @param resourceFactory 资源容器
|
* @param resourceFactory 资源容器
|
||||||
* @param serviceClass Service类
|
* @param serviceImplClass Service类
|
||||||
* @param clientAddress 本地IP地址
|
* @param clientAddress 本地IP地址
|
||||||
|
* @param sncpGroup 自身的组节点名 可能为null
|
||||||
|
* @param groups 所有的组节点,包含自身
|
||||||
|
* @param conf 启动配置项
|
||||||
* @param sameGroupTransport 同组的通信组件
|
* @param sameGroupTransport 同组的通信组件
|
||||||
* @param diffGroupTransports 异组的通信组件列表
|
* @param diffGroupTransports 异组的通信组件列表
|
||||||
*
|
*
|
||||||
* @return Service的本地模式实例
|
* @return Service的本地模式实例
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends Service> T createLocalService(final String name, final Consumer<Runnable> executor, final ResourceFactory resourceFactory,
|
public static <T extends Service> T createLocalService(
|
||||||
final Class<T> serviceClass, final InetSocketAddress clientAddress, final Transport sameGroupTransport, final Collection<Transport> diffGroupTransports) {
|
final String name,
|
||||||
|
final Consumer<Runnable> executor,
|
||||||
|
final ResourceFactory resourceFactory,
|
||||||
|
final Class<T> serviceImplClass,
|
||||||
|
final InetSocketAddress clientAddress,
|
||||||
|
final String sncpGroup,
|
||||||
|
final Set<String> groups,
|
||||||
|
final AnyValue conf,
|
||||||
|
final Transport sameGroupTransport,
|
||||||
|
final Collection<Transport> diffGroupTransports) {
|
||||||
try {
|
try {
|
||||||
final Class newClazz = createLocalServiceClass(name, serviceClass);
|
final Class newClazz = createLocalServiceClass(name, serviceImplClass);
|
||||||
T rs = (T) newClazz.newInstance();
|
T rs = (T) newClazz.newInstance();
|
||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
Service remoteService = null;
|
Service remoteService = null;
|
||||||
@@ -770,7 +952,7 @@ public abstract class Sncp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (remoteService == null && remoteTransport != null) {
|
if (remoteService == null && remoteTransport != null) {
|
||||||
remoteService = createRemoteService(name, executor, serviceClass, clientAddress, remoteTransport);
|
remoteService = createRemoteService(name, executor, serviceImplClass, clientAddress, sncpGroup, groups, conf, remoteTransport);
|
||||||
}
|
}
|
||||||
if (remoteService != null) field.set(rs, remoteService);
|
if (remoteService != null) field.set(rs, remoteService);
|
||||||
}
|
}
|
||||||
@@ -781,7 +963,7 @@ public abstract class Sncp {
|
|||||||
try {
|
try {
|
||||||
Field e = newClazz.getDeclaredField(FIELDPREFIX + "_client");
|
Field e = newClazz.getDeclaredField(FIELDPREFIX + "_client");
|
||||||
e.setAccessible(true);
|
e.setAccessible(true);
|
||||||
client = new SncpClient(name, serviceClass, rs, executor, false, newClazz, clientAddress);
|
client = new SncpClient(name, serviceImplClass, rs, executor, false, newClazz, clientAddress);
|
||||||
e.set(rs, client);
|
e.set(rs, client);
|
||||||
} catch (NoSuchFieldException ne) {
|
} catch (NoSuchFieldException ne) {
|
||||||
}
|
}
|
||||||
@@ -793,13 +975,13 @@ public abstract class Sncp {
|
|||||||
sb.append(", serviceid = ").append(client.getServiceid());
|
sb.append(", serviceid = ").append(client.getServiceid());
|
||||||
sb.append(", serviceversion = ").append(client.getServiceversion());
|
sb.append(", serviceversion = ").append(client.getServiceversion());
|
||||||
sb.append(", action.size = ").append(client.getActionCount());
|
sb.append(", action.size = ").append(client.getActionCount());
|
||||||
List<String> groups = new ArrayList<>();
|
// List<String> groups = new ArrayList<>();
|
||||||
if (sameGroupTransport != null) groups.add(sameGroupTransport.getName());
|
// if (sameGroupTransport != null) groups.add(sameGroupTransport.getName());
|
||||||
if (diffGroupTransports != null) {
|
// if (diffGroupTransports != null) {
|
||||||
for (Transport t : diffGroupTransports) {
|
// for (Transport t : diffGroupTransports) {
|
||||||
groups.add(t.getName());
|
// groups.add(t.getName());
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
sb.append(", address = ").append(clientAddress).append(", groups = ").append(groups);
|
sb.append(", address = ").append(clientAddress).append(", groups = ").append(groups);
|
||||||
sb.append(", sameaddrs = ").append(sameGroupTransport == null ? null : Arrays.asList(sameGroupTransport.getRemoteAddresses()));
|
sb.append(", sameaddrs = ").append(sameGroupTransport == null ? null : Arrays.asList(sameGroupTransport.getRemoteAddresses()));
|
||||||
|
|
||||||
@@ -819,6 +1001,21 @@ public abstract class Sncp {
|
|||||||
s.set(rs, sb.toString());
|
s.set(rs, sb.toString());
|
||||||
}
|
}
|
||||||
if (client == null) return rs;
|
if (client == null) return rs;
|
||||||
|
{
|
||||||
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_sncpGroup");
|
||||||
|
c.setAccessible(true);
|
||||||
|
c.set(rs, sncpGroup);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_groups");
|
||||||
|
c.setAccessible(true);
|
||||||
|
c.set(rs, groups);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf");
|
||||||
|
c.setAccessible(true);
|
||||||
|
c.set(rs, conf);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_sameGroupTransport");
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_sameGroupTransport");
|
||||||
c.setAccessible(true);
|
c.setAccessible(true);
|
||||||
@@ -838,6 +1035,10 @@ public abstract class Sncp {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T extends Service> T createSimpleRemoteService(final String name, final Class<T> serviceTypeOrImplClass, final InetSocketAddress clientAddress, final Transport transport) {
|
||||||
|
return createRemoteService(name, null, serviceTypeOrImplClass, clientAddress, (String) null, new HashSet<>(), (AnyValue) null, transport);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* @Resource(name = "")
|
* @Resource(name = "")
|
||||||
@@ -845,12 +1046,20 @@ public abstract class Sncp {
|
|||||||
* @ResourceType({TestService.class})
|
* @ResourceType({TestService.class})
|
||||||
* public final class _DynRemoteTestService extends TestService{
|
* public final class _DynRemoteTestService extends TestService{
|
||||||
*
|
*
|
||||||
|
* private static final Class _redkale_service_type = TestService.class;
|
||||||
|
*
|
||||||
* @Resource
|
* @Resource
|
||||||
* private BsonConvert _redkale_bsonConvert;
|
* private BsonConvert _redkale_bsonConvert;
|
||||||
*
|
*
|
||||||
* @Resource
|
* @Resource
|
||||||
* private JsonConvert _redkale_jsonConvert;
|
* private JsonConvert _redkale_jsonConvert;
|
||||||
*
|
*
|
||||||
|
* private String _redkale_sncpGroup; //自身的组节点名 可能为null
|
||||||
|
*
|
||||||
|
* private Set<String> groups; //所有的组节点,包含自身
|
||||||
|
*
|
||||||
|
* private AnyValue _redkale_conf;
|
||||||
|
*
|
||||||
* private Transport _redkale_transport;
|
* private Transport _redkale_transport;
|
||||||
*
|
*
|
||||||
* private SncpClient _redkale_client;
|
* private SncpClient _redkale_client;
|
||||||
@@ -894,34 +1103,45 @@ public abstract class Sncp {
|
|||||||
* @param <T> Service泛型
|
* @param <T> Service泛型
|
||||||
* @param name 资源名
|
* @param name 资源名
|
||||||
* @param executor 线程池
|
* @param executor 线程池
|
||||||
* @param serviceClass Service类
|
* @param serviceTypeOrImplClass Service类
|
||||||
* @param clientAddress 本地IP地址
|
* @param clientAddress 本地IP地址
|
||||||
|
* @param sncpGroup 自身的组节点名 可能为null
|
||||||
|
* @param groups 所有的组节点,包含自身
|
||||||
|
* @param conf 启动配置项
|
||||||
* @param transport 通信组件
|
* @param transport 通信组件
|
||||||
*
|
*
|
||||||
* @return Service的远程模式实例
|
* @return Service的远程模式实例
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends Service> T createRemoteService(final String name, final Consumer<Runnable> executor, final Class<T> serviceClass,
|
public static <T extends Service> T createRemoteService(
|
||||||
final InetSocketAddress clientAddress, final Transport transport) {
|
final String name,
|
||||||
if (serviceClass == null) return null;
|
final Consumer<Runnable> executor,
|
||||||
if (!Service.class.isAssignableFrom(serviceClass)) return null;
|
final Class<T> serviceTypeOrImplClass,
|
||||||
int mod = serviceClass.getModifiers();
|
final InetSocketAddress clientAddress,
|
||||||
boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceClass.isInterface());
|
final String sncpGroup,
|
||||||
|
final Set<String> groups,
|
||||||
|
final AnyValue conf,
|
||||||
|
final Transport transport) {
|
||||||
|
if (serviceTypeOrImplClass == null) return null;
|
||||||
|
if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null;
|
||||||
|
int mod = serviceTypeOrImplClass.getModifiers();
|
||||||
|
boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceTypeOrImplClass.isInterface());
|
||||||
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
|
if (!java.lang.reflect.Modifier.isPublic(mod)) return null;
|
||||||
final String supDynName = serviceClass.getName().replace('.', '/');
|
final String supDynName = serviceTypeOrImplClass.getName().replace('.', '/');
|
||||||
final String clientName = SncpClient.class.getName().replace('.', '/');
|
final String clientName = SncpClient.class.getName().replace('.', '/');
|
||||||
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
final String clientDesc = Type.getDescriptor(SncpClient.class);
|
||||||
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
final String sncpDynDesc = Type.getDescriptor(SncpDyn.class);
|
||||||
final String bsonConvertDesc = Type.getDescriptor(BsonConvert.class);
|
final String bsonConvertDesc = Type.getDescriptor(BsonConvert.class);
|
||||||
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
|
final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class);
|
||||||
final String transportDesc = Type.getDescriptor(Transport.class);
|
final String transportDesc = Type.getDescriptor(Transport.class);
|
||||||
|
final String stringDesc = Type.getDescriptor(String.class);
|
||||||
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
|
final String anyValueDesc = Type.getDescriptor(AnyValue.class);
|
||||||
ClassLoader loader = Sncp.class.getClassLoader();
|
ClassLoader loader = Sncp.class.getClassLoader();
|
||||||
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceClass.getSimpleName();
|
String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName();
|
||||||
try {
|
try {
|
||||||
Class newClazz = Class.forName(newDynName.replace('/', '.'));
|
Class newClazz = Class.forName(newDynName.replace('/', '.'));
|
||||||
T rs = (T) newClazz.newInstance();
|
T rs = (T) newClazz.newInstance();
|
||||||
SncpClient client = new SncpClient(name, serviceClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceClass) : serviceClass, clientAddress);
|
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
|
||||||
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
|
||||||
c.setAccessible(true);
|
c.setAccessible(true);
|
||||||
c.set(rs, client);
|
c.set(rs, client);
|
||||||
@@ -942,7 +1162,7 @@ public abstract class Sncp {
|
|||||||
s.set(rs, sb.toString());
|
s.set(rs, sb.toString());
|
||||||
}
|
}
|
||||||
return rs;
|
return rs;
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||||
@@ -950,7 +1170,7 @@ public abstract class Sncp {
|
|||||||
AsmMethodVisitor mv;
|
AsmMethodVisitor mv;
|
||||||
AnnotationVisitor av0;
|
AnnotationVisitor av0;
|
||||||
|
|
||||||
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, serviceClass.isInterface() ? "java/lang/Object" : supDynName, serviceClass.isInterface() ? new String[]{supDynName} : null);
|
cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, serviceTypeOrImplClass.isInterface() ? new String[]{supDynName} : null);
|
||||||
{
|
{
|
||||||
av0 = cw.visitAnnotation("Ljavax/annotation/Resource;", true);
|
av0 = cw.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||||
av0.visit("name", name);
|
av0.visit("name", name);
|
||||||
@@ -960,9 +1180,9 @@ public abstract class Sncp {
|
|||||||
av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true);
|
av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true);
|
||||||
{
|
{
|
||||||
AnnotationVisitor av1 = av0.visitArray("value");
|
AnnotationVisitor av1 = av0.visitArray("value");
|
||||||
ResourceType rty = serviceClass.getAnnotation(ResourceType.class);
|
ResourceType rty = serviceTypeOrImplClass.getAnnotation(ResourceType.class);
|
||||||
if (rty == null) {
|
if (rty == null) {
|
||||||
av1.visit(null, Type.getType(Type.getDescriptor(serviceClass)));
|
av1.visit(null, Type.getType(Type.getDescriptor(serviceTypeOrImplClass)));
|
||||||
} else {
|
} else {
|
||||||
for (Class cl : rty.value()) {
|
for (Class cl : rty.value()) {
|
||||||
av1.visit(null, Type.getType(Type.getDescriptor(cl)));
|
av1.visit(null, Type.getType(Type.getDescriptor(cl)));
|
||||||
@@ -978,11 +1198,15 @@ public abstract class Sncp {
|
|||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
}
|
}
|
||||||
{ //给新类加上 原有的Annotation
|
{ //给新类加上 原有的Annotation
|
||||||
for (Annotation ann : serviceClass.getAnnotations()) {
|
for (Annotation ann : serviceTypeOrImplClass.getAnnotations()) {
|
||||||
if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue;
|
if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue;
|
||||||
visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann);
|
visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, FIELDPREFIX + "_service_type", "Ljava/lang/Class;", null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null);
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_bsonConvert", bsonConvertDesc, null, null);
|
||||||
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
av0 = fv.visitAnnotation("Ljavax/annotation/Resource;", true);
|
||||||
@@ -995,6 +1219,18 @@ public abstract class Sncp {
|
|||||||
av0.visitEnd();
|
av0.visitEnd();
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_sncpGroup", stringDesc, null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_groups", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_transport", transportDesc, null, null);
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_transport", transportDesc, null, null);
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
@@ -1007,11 +1243,19 @@ public abstract class Sncp {
|
|||||||
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_selfstring", "Ljava/lang/String;", null, null);
|
fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_selfstring", "Ljava/lang/String;", null, null);
|
||||||
fv.visitEnd();
|
fv.visitEnd();
|
||||||
}
|
}
|
||||||
|
{//静态构造函数
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null));
|
||||||
|
mv.visitLdcInsn(Type.getType(Type.getDescriptor(serviceTypeOrImplClass)));
|
||||||
|
mv.visitFieldInsn(PUTSTATIC, newDynName, FIELDPREFIX + "_service_type", "Ljava/lang/Class;");
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(1, 0);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
{ //构造函数
|
{ //构造函数
|
||||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
|
||||||
//mv.setDebug(true);
|
//mv.setDebug(true);
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.visitMethodInsn(INVOKESPECIAL, serviceClass.isInterface() ? "java/lang/Object" : supDynName, "<init>", "()V", false);
|
mv.visitMethodInsn(INVOKESPECIAL, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, "<init>", "()V", false);
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
mv.visitMaxs(1, 1);
|
mv.visitMaxs(1, 1);
|
||||||
mv.visitEnd();
|
mv.visitEnd();
|
||||||
@@ -1047,7 +1291,7 @@ public abstract class Sncp {
|
|||||||
mv.visitEnd();
|
mv.visitEnd();
|
||||||
}
|
}
|
||||||
int i = -1;
|
int i = -1;
|
||||||
for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(name, serviceClass) : serviceClass)) {
|
for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) {
|
||||||
final int index = ++i;
|
final int index = ++i;
|
||||||
final java.lang.reflect.Method method = entry.method;
|
final java.lang.reflect.Method method = entry.method;
|
||||||
{
|
{
|
||||||
@@ -1156,13 +1400,32 @@ public abstract class Sncp {
|
|||||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||||
try {
|
try {
|
||||||
T rs = (T) newClazz.newInstance();
|
T rs = (T) newClazz.newInstance();
|
||||||
|
SncpClient client = new SncpClient(name, serviceTypeOrImplClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress);
|
||||||
|
{
|
||||||
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client");
|
||||||
c.setAccessible(true);
|
c.setAccessible(true);
|
||||||
SncpClient client = new SncpClient(name, serviceClass, rs, executor, true, realed ? createLocalServiceClass(name, serviceClass) : serviceClass, clientAddress);
|
|
||||||
c.set(rs, client);
|
c.set(rs, client);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf");
|
||||||
|
c.setAccessible(true);
|
||||||
|
c.set(rs, conf);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_sncpGroup");
|
||||||
|
c.setAccessible(true);
|
||||||
|
c.set(rs, sncpGroup);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Field c = newClazz.getDeclaredField(FIELDPREFIX + "_groups");
|
||||||
|
c.setAccessible(true);
|
||||||
|
c.set(rs, groups);
|
||||||
|
}
|
||||||
|
{
|
||||||
Field t = newClazz.getDeclaredField(FIELDPREFIX + "_transport");
|
Field t = newClazz.getDeclaredField(FIELDPREFIX + "_transport");
|
||||||
t.setAccessible(true);
|
t.setAccessible(true);
|
||||||
t.set(rs, transport);
|
t.set(rs, transport);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(newClazz.getName()).append("{name = '").append(name);
|
sb.append(newClazz.getName()).append("{name = '").append(name);
|
||||||
|
|||||||
307
src/org/redkale/net/sncp/SncpAsyncHandler.java
Normal file
307
src/org/redkale/net/sncp/SncpAsyncHandler.java
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.redkale.net.sncp;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import jdk.internal.org.objectweb.asm.*;
|
||||||
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
|
import org.redkale.convert.bson.*;
|
||||||
|
import org.redkale.net.sncp.SncpDynServlet.SncpServletAction;
|
||||||
|
import org.redkale.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步回调函数 <br>
|
||||||
|
*
|
||||||
|
* public class _DyncSncpAsyncHandler extends XXXAsyncHandler implements SncpAsyncHandler
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 详情见: https://redkale.org
|
||||||
|
*
|
||||||
|
* @author zhangjx
|
||||||
|
* @param <V> 结果对象的泛型
|
||||||
|
* @param <A> 附件对象的泛型
|
||||||
|
*/
|
||||||
|
public interface SncpAsyncHandler<V, A> extends AsyncHandler<V, A> {
|
||||||
|
|
||||||
|
public Object[] sncp_getParams();
|
||||||
|
|
||||||
|
public void sncp_setParams(Object... params);
|
||||||
|
|
||||||
|
public void sncp_setFuture(CompletableFuture future);
|
||||||
|
|
||||||
|
public CompletableFuture sncp_getFuture();
|
||||||
|
|
||||||
|
static class Factory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <blockquote><pre>
|
||||||
|
*
|
||||||
|
* 考虑点:
|
||||||
|
* 1、AsyncHandler子类是接口,且还有其他多个方法
|
||||||
|
* 2、AsyncHandler子类是类, 需要继承,且必须有空参数构造函数
|
||||||
|
* 3、AsyncHandler子类无论是接口还是类,都可能存在其他泛型
|
||||||
|
*
|
||||||
|
* public class XXXAsyncHandler_DyncSncpAsyncHandler_4323 extends XXXAsyncHandler implements SncpAsyncHandler {
|
||||||
|
*
|
||||||
|
* private SncpAsyncHandler sncphandler;
|
||||||
|
*
|
||||||
|
* private CompletableFuture sncpfuture;
|
||||||
|
*
|
||||||
|
* @java.beans.ConstructorProperties({"sncphandler"})
|
||||||
|
* public XXXAsyncHandler_DyncSncpAsyncHandler_4323(SncpAsyncHandler sncphandler) {
|
||||||
|
* super();
|
||||||
|
* this.sncphandler = sncphandler;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void completed(Object result, Object attachment) {
|
||||||
|
* sncphandler.completed(result, attachment);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void failed(Throwable exc, Object attachment) {
|
||||||
|
* sncphandler.failed(exc, attachment);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public Object[] sncp_getParams() {
|
||||||
|
* return sncphandler.sncp_getParams();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void sncp_setParams(Object... params) {
|
||||||
|
* sncphandler.sncp_setParams(params);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void sncp_setFuture(CompletableFuture future) {
|
||||||
|
* this.sncpfuture = future;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public CompletableFuture sncp_getFuture() {
|
||||||
|
* return this.sncpfuture;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* </pre></blockquote>
|
||||||
|
*
|
||||||
|
* @param handlerClass AsyncHandler类型或子类
|
||||||
|
*
|
||||||
|
* @return Creator
|
||||||
|
*/
|
||||||
|
public static Creator<SncpAsyncHandler> createCreator(Class<? extends AsyncHandler> handlerClass) {
|
||||||
|
//-------------------------------------------------------------
|
||||||
|
final boolean handlerinterface = handlerClass.isInterface();
|
||||||
|
final String handlerClassName = handlerClass.getName().replace('.', '/');
|
||||||
|
final String sncpHandlerName = SncpAsyncHandler.class.getName().replace('.', '/');
|
||||||
|
final String sncpHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class);
|
||||||
|
final String sncpFutureDesc = Type.getDescriptor(CompletableFuture.class);
|
||||||
|
final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + SncpAsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000);
|
||||||
|
|
||||||
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||||
|
FieldVisitor fv;
|
||||||
|
AsmMethodVisitor mv;
|
||||||
|
AnnotationVisitor av0;
|
||||||
|
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName, sncpHandlerName} : new String[]{sncpHandlerName});
|
||||||
|
|
||||||
|
{ //handler 属性
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, "sncphandler", sncpHandlerDesc, null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
{ //future 属性
|
||||||
|
fv = cw.visitField(ACC_PRIVATE, "sncpfuture", sncpFutureDesc, null, null);
|
||||||
|
fv.visitEnd();
|
||||||
|
}
|
||||||
|
{//构造方法
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "<init>", "(" + sncpHandlerDesc + ")V", null, null));
|
||||||
|
//mv.setDebug(true);
|
||||||
|
{
|
||||||
|
av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true);
|
||||||
|
{
|
||||||
|
AnnotationVisitor av1 = av0.visitArray("value");
|
||||||
|
av1.visit(null, "sncphandler");
|
||||||
|
av1.visitEnd();
|
||||||
|
}
|
||||||
|
av0.visitEnd();
|
||||||
|
}
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "<init>", "()V", false);
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitFieldInsn(PUTFIELD, newDynName, "sncphandler", sncpHandlerDesc);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(2, 2);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (java.lang.reflect.Method method : handlerClass.getMethods()) { //
|
||||||
|
if ("completed".equals(method.getName()) && method.getParameterCount() == 2) {
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(3, 3);
|
||||||
|
mv.visitEnd();
|
||||||
|
} else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) {
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(3, 3);
|
||||||
|
mv.visitEnd();
|
||||||
|
} else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) {
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null));
|
||||||
|
Class returnType = method.getReturnType();
|
||||||
|
if (returnType == void.class) {
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(0, 1);
|
||||||
|
} else if (returnType.isPrimitive()) {
|
||||||
|
mv.visitInsn(ICONST_0);
|
||||||
|
if (returnType == long.class) {
|
||||||
|
mv.visitInsn(LRETURN);
|
||||||
|
mv.visitMaxs(2, 1);
|
||||||
|
} else if (returnType == float.class) {
|
||||||
|
mv.visitInsn(FRETURN);
|
||||||
|
mv.visitMaxs(2, 1);
|
||||||
|
} else if (returnType == double.class) {
|
||||||
|
mv.visitInsn(DRETURN);
|
||||||
|
mv.visitMaxs(2, 1);
|
||||||
|
} else {
|
||||||
|
mv.visitInsn(IRETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mv.visitInsn(ACONST_NULL);
|
||||||
|
mv.visitInsn(ARETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
}
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ // sncp_getParams
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getParams", "()[Ljava/lang/Object;", null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc);
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_getParams", "()[Ljava/lang/Object;", true);
|
||||||
|
mv.visitInsn(ARETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
{ // sncp_setParams
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "sncp_setParams", "([Ljava/lang/Object;)V", null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(2, 2);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
{ // sncp_setFuture
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_setFuture", "(" + sncpFutureDesc + ")V", null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitFieldInsn(PUTFIELD, newDynName, "sncpfuture", sncpFutureDesc);
|
||||||
|
mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(2, 2);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
{ // sncp_getFuture
|
||||||
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getFuture", "()" + sncpFutureDesc, null, null));
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "sncpfuture", sncpFutureDesc);
|
||||||
|
mv.visitInsn(ARETURN);
|
||||||
|
mv.visitMaxs(1, 1);
|
||||||
|
mv.visitEnd();
|
||||||
|
}
|
||||||
|
cw.visitEnd();
|
||||||
|
byte[] bytes = cw.toByteArray();
|
||||||
|
Class<SncpAsyncHandler> newHandlerClazz = (Class<SncpAsyncHandler>) new ClassLoader(handlerClass.getClassLoader()) {
|
||||||
|
public final Class<?> loadClass(String name, byte[] b) {
|
||||||
|
return defineClass(name, b, 0, b.length);
|
||||||
|
}
|
||||||
|
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||||
|
return Creator.create(newHandlerClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultSncpAsyncHandler<V, A> implements SncpAsyncHandler<V, A> {
|
||||||
|
|
||||||
|
//为了在回调函数中调用_callParameter方法
|
||||||
|
protected Object[] params;
|
||||||
|
|
||||||
|
protected SncpServletAction action;
|
||||||
|
|
||||||
|
protected BsonReader in;
|
||||||
|
|
||||||
|
protected BsonWriter out;
|
||||||
|
|
||||||
|
protected SncpRequest request;
|
||||||
|
|
||||||
|
protected SncpResponse response;
|
||||||
|
|
||||||
|
protected CompletableFuture future;
|
||||||
|
|
||||||
|
public DefaultSncpAsyncHandler(SncpServletAction action, BsonReader in, BsonWriter out, SncpRequest request, SncpResponse response) {
|
||||||
|
this.action = action;
|
||||||
|
this.in = in;
|
||||||
|
this.out = out;
|
||||||
|
this.request = request;
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completed(Object result, Object attachment) {
|
||||||
|
try {
|
||||||
|
action._callParameter(out, sncp_getParams());
|
||||||
|
action.convert.convertTo(out, Object.class, result);
|
||||||
|
response.finish(0, out);
|
||||||
|
} catch (Exception e) {
|
||||||
|
failed(e, attachment);
|
||||||
|
} finally {
|
||||||
|
action.convert.offerBsonReader(in);
|
||||||
|
action.convert.offerBsonWriter(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed(Throwable exc, Object attachment) {
|
||||||
|
response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", exc);
|
||||||
|
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] sncp_getParams() {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sncp_setParams(Object... params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sncp_setFuture(CompletableFuture future) {
|
||||||
|
this.future = future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture sncp_getFuture() {
|
||||||
|
return this.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,35 +41,66 @@ public final class SncpClient {
|
|||||||
|
|
||||||
protected final Type[] paramTypes;
|
protected final Type[] paramTypes;
|
||||||
|
|
||||||
|
protected final Class[] paramClass;
|
||||||
|
|
||||||
protected final Attribute[] paramAttrs; // 为null表示无RpcCall处理,index=0固定为null, 其他为参数标记的RpcCall回调方法
|
protected final Attribute[] paramAttrs; // 为null表示无RpcCall处理,index=0固定为null, 其他为参数标记的RpcCall回调方法
|
||||||
|
|
||||||
|
protected final int handlerFuncParamIndex;
|
||||||
|
|
||||||
|
protected final int handlerAttachParamIndex;
|
||||||
|
|
||||||
protected final int addressTargetParamIndex;
|
protected final int addressTargetParamIndex;
|
||||||
|
|
||||||
protected final int addressSourceParamIndex;
|
protected final int addressSourceParamIndex;
|
||||||
|
|
||||||
public SncpAction(Method method, DLong actionid) {
|
protected final boolean boolReturnTypeFuture; // 返回结果类型是否为 CompletableFuture
|
||||||
this.actionid = actionid;
|
|
||||||
|
protected final Creator<? extends CompletableFuture> futureCreator;
|
||||||
|
|
||||||
|
public SncpAction(final Class clazz, Method method, DLong actionid) {
|
||||||
|
this.actionid = actionid == null ? Sncp.hash(method) : actionid;
|
||||||
Type rt = method.getGenericReturnType();
|
Type rt = method.getGenericReturnType();
|
||||||
if (rt instanceof TypeVariable) {
|
if (rt instanceof TypeVariable) {
|
||||||
TypeVariable tv = (TypeVariable) rt;
|
TypeVariable tv = (TypeVariable) rt;
|
||||||
if (tv.getBounds().length == 1) rt = tv.getBounds()[0];
|
if (tv.getBounds().length == 1) rt = tv.getBounds()[0];
|
||||||
}
|
}
|
||||||
this.resultTypes = rt == void.class ? null : rt;
|
this.resultTypes = rt == void.class ? null : rt;
|
||||||
|
this.boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType());
|
||||||
|
this.futureCreator = boolReturnTypeFuture ? Creator.create((Class<? extends CompletableFuture>) method.getReturnType()) : null;
|
||||||
this.paramTypes = method.getGenericParameterTypes();
|
this.paramTypes = method.getGenericParameterTypes();
|
||||||
|
this.paramClass = method.getParameterTypes();
|
||||||
this.method = method;
|
this.method = method;
|
||||||
Annotation[][] anns = method.getParameterAnnotations();
|
Annotation[][] anns = method.getParameterAnnotations();
|
||||||
int targetAddrIndex = -1;
|
int targetAddrIndex = -1;
|
||||||
int sourceAddrIndex = -1;
|
int sourceAddrIndex = -1;
|
||||||
|
int handlerAttachIndex = -1;
|
||||||
|
int handlerFuncIndex = -1;
|
||||||
boolean hasattr = false;
|
boolean hasattr = false;
|
||||||
Attribute[] atts = new Attribute[paramTypes.length + 1];
|
Attribute[] atts = new Attribute[paramTypes.length + 1];
|
||||||
if (anns.length > 0) {
|
if (anns.length > 0) {
|
||||||
Class<?>[] params = method.getParameterTypes();
|
Class<?>[] params = method.getParameterTypes();
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
if (AsyncHandler.class.isAssignableFrom(params[i])) {
|
||||||
|
if (boolReturnTypeFuture) {
|
||||||
|
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
|
||||||
|
}
|
||||||
|
if (handlerFuncIndex >= 0) {
|
||||||
|
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
|
||||||
|
}
|
||||||
|
Sncp.checkAsyncModifier(params[i], method);
|
||||||
|
handlerFuncIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int i = 0; i < anns.length; i++) {
|
for (int i = 0; i < anns.length; i++) {
|
||||||
if (anns[i].length > 0) {
|
if (anns[i].length > 0) {
|
||||||
for (Annotation ann : anns[i]) {
|
for (Annotation ann : anns[i]) {
|
||||||
if (ann.annotationType() == RpcTargetAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
|
if (ann.annotationType() == RpcAttachment.class) {
|
||||||
|
if (handlerAttachIndex >= 0) {
|
||||||
|
throw new RuntimeException(method + " have more than one @RpcAttachment parameter");
|
||||||
|
}
|
||||||
|
handlerAttachIndex = i;
|
||||||
|
} else if (ann.annotationType() == RpcTargetAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
|
||||||
targetAddrIndex = i;
|
targetAddrIndex = i;
|
||||||
} else if (ann.annotationType() == RpcSourceAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
|
} else if (ann.annotationType() == RpcSourceAddress.class && SocketAddress.class.isAssignableFrom(params[i])) {
|
||||||
sourceAddrIndex = i;
|
sourceAddrIndex = i;
|
||||||
@@ -91,7 +122,12 @@ public final class SncpClient {
|
|||||||
}
|
}
|
||||||
this.addressTargetParamIndex = targetAddrIndex;
|
this.addressTargetParamIndex = targetAddrIndex;
|
||||||
this.addressSourceParamIndex = sourceAddrIndex;
|
this.addressSourceParamIndex = sourceAddrIndex;
|
||||||
|
this.handlerFuncParamIndex = handlerFuncIndex;
|
||||||
|
this.handlerAttachParamIndex = handlerAttachIndex;
|
||||||
this.paramAttrs = hasattr ? atts : null;
|
this.paramAttrs = hasattr ? atts : null;
|
||||||
|
if (this.handlerFuncParamIndex >= 0 && method.getReturnType() != void.class) {
|
||||||
|
throw new RuntimeException(method + " have AsyncHandler type parameter but return type is not void");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,19 +162,22 @@ public final class SncpClient {
|
|||||||
|
|
||||||
protected final Consumer<Runnable> executor;
|
protected final Consumer<Runnable> executor;
|
||||||
|
|
||||||
public <T extends Service> SncpClient(final String serviceName, final Class<T> serviceType, final T service, final Consumer<Runnable> executor,
|
public <T extends Service> SncpClient(final String serviceName, final Class<T> serviceTypeOrImplClass, final T service, final Consumer<Runnable> executor,
|
||||||
final boolean remote, final Class serviceClass, final InetSocketAddress clientAddress) {
|
final boolean remote, final Class serviceClass, final InetSocketAddress clientAddress) {
|
||||||
this.remote = remote;
|
this.remote = remote;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.serviceClass = serviceClass;
|
this.serviceClass = serviceClass;
|
||||||
this.serviceversion = service.version();
|
this.serviceversion = 0;
|
||||||
this.clientAddress = clientAddress;
|
this.clientAddress = clientAddress;
|
||||||
this.name = serviceName;
|
this.name = serviceName;
|
||||||
this.serviceid = Sncp.hash(serviceType.getName() + ':' + serviceName);
|
Class tn = serviceTypeOrImplClass;
|
||||||
|
ResourceType rt = (ResourceType) tn.getAnnotation(ResourceType.class);
|
||||||
|
if (rt != null && rt.value().length > 0) tn = rt.value()[0];
|
||||||
|
this.serviceid = Sncp.hash(tn.getName() + ':' + serviceName);
|
||||||
final List<SncpAction> methodens = new ArrayList<>();
|
final List<SncpAction> methodens = new ArrayList<>();
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
for (java.lang.reflect.Method method : parseMethod(serviceClass)) {
|
for (java.lang.reflect.Method method : parseMethod(serviceClass)) {
|
||||||
methodens.add(new SncpAction(method, Sncp.hash(method)));
|
methodens.add(new SncpAction(serviceClass, method, Sncp.hash(method)));
|
||||||
}
|
}
|
||||||
this.actions = methodens.toArray(new SncpAction[methodens.size()]);
|
this.actions = methodens.toArray(new SncpAction[methodens.size()]);
|
||||||
this.addrBytes = clientAddress == null ? new byte[4] : clientAddress.getAddress().getAddress();
|
this.addrBytes = clientAddress == null ? new byte[4] : clientAddress.getAddress().getAddress();
|
||||||
@@ -149,7 +188,7 @@ public final class SncpClient {
|
|||||||
final List<SncpAction> actions = new ArrayList<>();
|
final List<SncpAction> actions = new ArrayList<>();
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
for (java.lang.reflect.Method method : parseMethod(serviceClass)) {
|
for (java.lang.reflect.Method method : parseMethod(serviceClass)) {
|
||||||
actions.add(new SncpAction(method, Sncp.hash(method)));
|
actions.add(new SncpAction(serviceClass, method, Sncp.hash(method)));
|
||||||
}
|
}
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
@@ -193,7 +232,7 @@ public final class SncpClient {
|
|||||||
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
|
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
|
||||||
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
|
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
|
||||||
if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
|
if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
|
||||||
if (method.getName().equals("version") || method.getName().equals("name")) continue;
|
//if (method.getName().equals("version") || method.getName().equals("name")) continue;
|
||||||
//if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue;
|
//if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue;
|
||||||
DLong actionid = Sncp.hash(method);
|
DLong actionid = Sncp.hash(method);
|
||||||
Method old = actionids.get(actionid);
|
Method old = actionids.get(actionid);
|
||||||
@@ -221,13 +260,16 @@ public final class SncpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void remoteSameGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) {
|
public void remoteSameGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) {
|
||||||
|
if (transport == null) return;
|
||||||
final SncpAction action = actions[index];
|
final SncpAction action = actions[index];
|
||||||
|
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; //不能让远程调用handler,因为之前本地方法已经调用过了
|
||||||
for (InetSocketAddress addr : transport.getRemoteAddresses()) {
|
for (InetSocketAddress addr : transport.getRemoteAddresses()) {
|
||||||
remote0(bsonConvert, jsonConvert, transport, addr, action, params);
|
remote0(null, bsonConvert, jsonConvert, transport, addr, action, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void asyncRemoteSameGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) {
|
public void asyncRemoteSameGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) {
|
||||||
|
if (transport == null) return;
|
||||||
if (executor != null) {
|
if (executor != null) {
|
||||||
executor.accept(() -> {
|
executor.accept(() -> {
|
||||||
remoteSameGroup(bsonConvert, jsonConvert, transport, index, params);
|
remoteSameGroup(bsonConvert, jsonConvert, transport, index, params);
|
||||||
@@ -240,8 +282,9 @@ public final class SncpClient {
|
|||||||
public void remoteDiffGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport[] transports, final int index, final Object... params) {
|
public void remoteDiffGroup(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport[] transports, final int index, final Object... params) {
|
||||||
if (transports == null || transports.length < 1) return;
|
if (transports == null || transports.length < 1) return;
|
||||||
final SncpAction action = actions[index];
|
final SncpAction action = actions[index];
|
||||||
|
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; //不能让远程调用handler,因为之前本地方法已经调用过了
|
||||||
for (Transport transport : transports) {
|
for (Transport transport : transports) {
|
||||||
remote0(bsonConvert, jsonConvert, transport, null, action, params);
|
remote0(null, bsonConvert, jsonConvert, transport, null, action, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,9 +302,34 @@ public final class SncpClient {
|
|||||||
//只给远程模式调用的
|
//只给远程模式调用的
|
||||||
public <T> T remote(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) {
|
public <T> T remote(final BsonConvert bsonConvert, final JsonConvert jsonConvert, Transport transport, final int index, final Object... params) {
|
||||||
final SncpAction action = actions[index];
|
final SncpAction action = actions[index];
|
||||||
SncpFuture<byte[]> future = remote0(bsonConvert, jsonConvert, transport, null, action, params);
|
final AsyncHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (AsyncHandler) params[action.handlerFuncParamIndex] : null;
|
||||||
|
if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null;
|
||||||
final BsonReader reader = bsonConvert.pollBsonReader();
|
final BsonReader reader = bsonConvert.pollBsonReader();
|
||||||
|
CompletableFuture<byte[]> future = remote0(handlerFunc, bsonConvert, jsonConvert, transport, null, action, params);
|
||||||
|
if (action.boolReturnTypeFuture) {
|
||||||
|
CompletableFuture result = action.futureCreator.create();
|
||||||
|
future.whenComplete((v, e) -> {
|
||||||
|
try {
|
||||||
|
if (e != null) {
|
||||||
|
result.completeExceptionally(e);
|
||||||
|
} else {
|
||||||
|
reader.setBytes(v);
|
||||||
|
byte i;
|
||||||
|
while ((i = reader.readByte()) != 0) {
|
||||||
|
final Attribute attr = action.paramAttrs[i];
|
||||||
|
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||||
|
}
|
||||||
|
Object rs = bsonConvert.convertFrom(Object.class, reader);
|
||||||
|
|
||||||
|
result.complete(rs);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
bsonConvert.offerBsonReader(reader);
|
||||||
|
}
|
||||||
|
}); //需要获取 Executor
|
||||||
|
return (T) result;
|
||||||
|
}
|
||||||
|
if (handlerFunc != null) return null;
|
||||||
try {
|
try {
|
||||||
reader.setBytes(future.get(5, TimeUnit.SECONDS));
|
reader.setBytes(future.get(5, TimeUnit.SECONDS));
|
||||||
byte i;
|
byte i;
|
||||||
@@ -269,7 +337,7 @@ public final class SncpClient {
|
|||||||
final Attribute attr = action.paramAttrs[i];
|
final Attribute attr = action.paramAttrs[i];
|
||||||
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||||
}
|
}
|
||||||
return bsonConvert.convertFrom(action.resultTypes, reader);
|
return bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
|
||||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
logger.log(Level.SEVERE, actions[index].method + " sncp (params: " + jsonConvert.convertTo(params) + ") remote error", e);
|
logger.log(Level.SEVERE, actions[index].method + " sncp (params: " + jsonConvert.convertTo(params) + ") remote error", e);
|
||||||
throw new RuntimeException(actions[index].method + " sncp remote error", e);
|
throw new RuntimeException(actions[index].method + " sncp remote error", e);
|
||||||
@@ -282,28 +350,30 @@ public final class SncpClient {
|
|||||||
if (transports == null || transports.length < 1) return;
|
if (transports == null || transports.length < 1) return;
|
||||||
remote(bsonConvert, jsonConvert, transports[0], index, params);
|
remote(bsonConvert, jsonConvert, transports[0], index, params);
|
||||||
for (int i = 1; i < transports.length; i++) {
|
for (int i = 1; i < transports.length; i++) {
|
||||||
remote0(bsonConvert, jsonConvert, transports[i], null, actions[index], params);
|
remote0(null, bsonConvert, jsonConvert, transports[i], null, actions[index], params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SncpFuture<byte[]> remote0(final BsonConvert bsonConvert, final JsonConvert jsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
private CompletableFuture<byte[]> remote0(final AsyncHandler handler, final BsonConvert bsonConvert, final JsonConvert jsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
||||||
if ("rest".equalsIgnoreCase(transport.getKind())) {
|
if ("rest".equalsIgnoreCase(transport.getSubprotocol())) {
|
||||||
return remoteRest0(jsonConvert, transport, addr0, action, params);
|
return remoteRest0(handler, jsonConvert, transport, addr0, action, params);
|
||||||
}
|
}
|
||||||
return remoteSncp0(bsonConvert, transport, addr0, action, params);
|
return remoteSncp0(handler, bsonConvert, transport, addr0, action, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SncpFuture<byte[]> remoteRest0(final JsonConvert jsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
//尚未实现
|
||||||
|
private CompletableFuture<byte[]> remoteRest0(final AsyncHandler handler, final JsonConvert jsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SncpFuture<byte[]> remoteSncp0(final BsonConvert bsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
private CompletableFuture<byte[]> remoteSncp0(final AsyncHandler handler, final BsonConvert bsonConvert, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) {
|
||||||
Type[] myparamtypes = action.paramTypes;
|
final Type[] myparamtypes = action.paramTypes;
|
||||||
|
final Class[] myparamclass = action.paramClass;
|
||||||
if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientAddress;
|
if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientAddress;
|
||||||
final BsonWriter writer = bsonConvert.pollBsonWriter(transport.getBufferSupplier()); // 将head写入
|
final BsonWriter writer = bsonConvert.pollBsonWriter(transport.getBufferSupplier()); // 将head写入
|
||||||
writer.writeTo(DEFAULT_HEADER);
|
writer.writeTo(DEFAULT_HEADER);
|
||||||
for (int i = 0; i < params.length; i++) {
|
for (int i = 0; i < params.length; i++) { //params 可能包含: 3 个 boolean
|
||||||
bsonConvert.convertTo(writer, myparamtypes[i], params[i]);
|
bsonConvert.convertTo(writer, AsyncHandler.class.isAssignableFrom(myparamclass[i]) ? AsyncHandler.class : myparamtypes[i], params[i]);
|
||||||
}
|
}
|
||||||
final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度
|
final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度
|
||||||
final long seqid = System.nanoTime();
|
final long seqid = System.nanoTime();
|
||||||
@@ -318,7 +388,7 @@ public final class SncpClient {
|
|||||||
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
|
fillHeader(sendBuffers[0], seqid, actionid, reqBodyLength);
|
||||||
|
|
||||||
final ByteBuffer buffer = transport.pollBuffer();
|
final ByteBuffer buffer = transport.pollBuffer();
|
||||||
final SncpFuture<byte[]> future = new SncpFuture(false);
|
final CompletableFuture<byte[]> future = new CompletableFuture();
|
||||||
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
|
conn.write(sendBuffers, sendBuffers, new CompletionHandler<Integer, ByteBuffer[]>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -352,7 +422,7 @@ public final class SncpClient {
|
|||||||
@Override
|
@Override
|
||||||
public void completed(Integer count, Void attachment2) {
|
public void completed(Integer count, Void attachment2) {
|
||||||
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
|
if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读
|
||||||
future.set(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
future.completeExceptionally(new RuntimeException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data"));
|
||||||
transport.offerBuffer(buffer);
|
transport.offerBuffer(buffer);
|
||||||
transport.offerConnection(true, conn);
|
transport.offerConnection(true, conn);
|
||||||
return;
|
return;
|
||||||
@@ -397,17 +467,39 @@ public final class SncpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void success() {
|
public void success() {
|
||||||
future.set(this.body);
|
future.complete(this.body);
|
||||||
transport.offerBuffer(buffer);
|
transport.offerBuffer(buffer);
|
||||||
transport.offerConnection(false, conn);
|
transport.offerConnection(false, conn);
|
||||||
|
if (handler != null) {
|
||||||
|
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||||
|
final BsonReader reader = bsonConvert.pollBsonReader();
|
||||||
|
try {
|
||||||
|
reader.setBytes(this.body);
|
||||||
|
int i;
|
||||||
|
while ((i = (reader.readByte() & 0xff)) != 0) {
|
||||||
|
final Attribute attr = action.paramAttrs[i];
|
||||||
|
attr.set(params[i - 1], bsonConvert.convertFrom(attr.type(), reader));
|
||||||
|
}
|
||||||
|
Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader);
|
||||||
|
handler.completed(rs, handlerAttach);
|
||||||
|
} catch (Exception e) {
|
||||||
|
handler.failed(e, handlerAttach);
|
||||||
|
} finally {
|
||||||
|
bsonConvert.offerBsonReader(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failed(Throwable exc, Void attachment2) {
|
public void failed(Throwable exc, Void attachment2) {
|
||||||
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
|
logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed", exc);
|
||||||
future.set(new RuntimeException(action.method + " sncp remote exec failed"));
|
future.completeExceptionally(new RuntimeException(action.method + " sncp remote exec failed"));
|
||||||
transport.offerBuffer(buffer);
|
transport.offerBuffer(buffer);
|
||||||
transport.offerConnection(true, conn);
|
transport.offerConnection(true, conn);
|
||||||
|
if (handler != null) {
|
||||||
|
final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null;
|
||||||
|
handler.failed(exc, handlerAttach);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -431,7 +523,8 @@ public final class SncpClient {
|
|||||||
int version = buffer.getInt();
|
int version = buffer.getInt();
|
||||||
if (version != this.serviceversion) throw new RuntimeException("sncp(" + action.method + ") response.serviceversion = " + serviceversion + ", but request.serviceversion =" + version);
|
if (version != this.serviceversion) throw new RuntimeException("sncp(" + action.method + ") response.serviceversion = " + serviceversion + ", but request.serviceversion =" + version);
|
||||||
DLong raction = DLong.read(buffer);
|
DLong raction = DLong.read(buffer);
|
||||||
if (!action.actionid.equals(raction)) throw new RuntimeException("sncp(" + action.method + ") response.actionid = " + action.actionid + ", but request.actionid =(" + raction + ")");
|
DLong actid = action.actionid;
|
||||||
|
if (!actid.equals(raction)) throw new RuntimeException("sncp(" + action.method + ") response.actionid = " + action.actionid + ", but request.actionid =(" + raction + ")");
|
||||||
buffer.getInt(); //地址
|
buffer.getInt(); //地址
|
||||||
buffer.getChar(); //端口
|
buffer.getChar(); //端口
|
||||||
}
|
}
|
||||||
@@ -452,91 +545,4 @@ public final class SncpClient {
|
|||||||
buffer.position(currentpos);
|
buffer.position(currentpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static final class SncpFuture<T> implements Future<T> {
|
|
||||||
|
|
||||||
private volatile boolean done;
|
|
||||||
|
|
||||||
private T result;
|
|
||||||
|
|
||||||
private RuntimeException ex;
|
|
||||||
|
|
||||||
private final boolean rest;
|
|
||||||
|
|
||||||
public SncpFuture(boolean rest) {
|
|
||||||
this.rest = rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SncpFuture(boolean rest, T result) {
|
|
||||||
this.rest = rest;
|
|
||||||
this.result = result;
|
|
||||||
this.done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRest() {
|
|
||||||
return this.rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(T result) {
|
|
||||||
this.result = result;
|
|
||||||
this.done = true;
|
|
||||||
synchronized (this) {
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(RuntimeException ex) {
|
|
||||||
this.ex = ex;
|
|
||||||
this.done = true;
|
|
||||||
synchronized (this) {
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDone() {
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() throws InterruptedException, ExecutionException {
|
|
||||||
if (done) {
|
|
||||||
if (ex != null) throw ex;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (!done) wait(10_000);
|
|
||||||
}
|
|
||||||
if (done) {
|
|
||||||
if (ex != null) throw ex;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
|
||||||
if (done) {
|
|
||||||
if (ex != null) throw ex;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
if (!done) wait(unit.toMillis(timeout));
|
|
||||||
}
|
|
||||||
if (done) {
|
|
||||||
if (ex != null) throw ex;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
throw new TimeoutException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.lang.annotation.*;
|
|||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.nio.*;
|
import java.nio.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
import java.util.logging.*;
|
import java.util.logging.*;
|
||||||
import javax.annotation.*;
|
import javax.annotation.*;
|
||||||
@@ -19,6 +20,7 @@ import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
|||||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
import jdk.internal.org.objectweb.asm.Type;
|
import jdk.internal.org.objectweb.asm.Type;
|
||||||
import org.redkale.convert.bson.*;
|
import org.redkale.convert.bson.*;
|
||||||
|
import org.redkale.net.sncp.SncpAsyncHandler.DefaultSncpAsyncHandler;
|
||||||
import org.redkale.service.*;
|
import org.redkale.service.*;
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
import org.redkale.service.RpcCall;
|
import org.redkale.service.RpcCall;
|
||||||
@@ -63,7 +65,7 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
|
if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue;
|
||||||
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
|
if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue;
|
||||||
if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
|
if (method.getName().equals("init") || method.getName().equals("destroy")) continue;
|
||||||
if (method.getName().equals("version") || method.getName().equals("name")) continue;
|
//if (method.getName().equals("version") || method.getName().equals("name")) continue;
|
||||||
final DLong actionid = Sncp.hash(method);
|
final DLong actionid = Sncp.hash(method);
|
||||||
SncpServletAction action = SncpServletAction.create(service, actionid, method);
|
SncpServletAction action = SncpServletAction.create(service, actionid, method);
|
||||||
action.convert = convert;
|
action.convert = convert;
|
||||||
@@ -112,7 +114,7 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
if (bufferSupplier == null) {
|
if (bufferSupplier == null) {
|
||||||
bufferSupplier = request.getContext().getBufferSupplier();
|
bufferSupplier = request.getContext().getBufferSupplier();
|
||||||
}
|
}
|
||||||
SncpServletAction action = actions.get(request.getActionid());
|
final SncpServletAction action = actions.get(request.getActionid());
|
||||||
//if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
|
//if (finest) logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method));
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid
|
response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid
|
||||||
@@ -120,16 +122,52 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
BsonWriter out = action.convert.pollBsonWriter(bufferSupplier);
|
BsonWriter out = action.convert.pollBsonWriter(bufferSupplier);
|
||||||
out.writeTo(DEFAULT_HEADER);
|
out.writeTo(DEFAULT_HEADER);
|
||||||
BsonReader in = action.convert.pollBsonReader();
|
BsonReader in = action.convert.pollBsonReader();
|
||||||
|
SncpAsyncHandler handler = null;
|
||||||
try {
|
try {
|
||||||
|
if (action.handlerFuncParamIndex >= 0) {
|
||||||
|
if (action.handlerFuncParamClass == AsyncHandler.class) {
|
||||||
|
handler = new DefaultSncpAsyncHandler(action, in, out, request, response);
|
||||||
|
} else {
|
||||||
|
Creator<SncpAsyncHandler> creator = action.handlerCreator;
|
||||||
|
if (creator == null) {
|
||||||
|
creator = SncpAsyncHandler.Factory.createCreator(action.handlerFuncParamClass);
|
||||||
|
action.handlerCreator = creator;
|
||||||
|
}
|
||||||
|
handler = creator.create(new DefaultSncpAsyncHandler(action, in, out, request, response));
|
||||||
|
}
|
||||||
|
} else if (action.boolReturnTypeFuture) {
|
||||||
|
handler = new DefaultSncpAsyncHandler(action, in, out, request, response);
|
||||||
|
}
|
||||||
in.setBytes(request.getBody());
|
in.setBytes(request.getBody());
|
||||||
action.action(in, out);
|
action.action(in, out, handler);
|
||||||
|
if (handler == null) {
|
||||||
response.finish(0, out);
|
response.finish(0, out);
|
||||||
|
action.convert.offerBsonReader(in);
|
||||||
|
action.convert.offerBsonWriter(out);
|
||||||
|
} else if (action.boolReturnTypeFuture) {
|
||||||
|
CompletableFuture future = handler.sncp_getFuture();
|
||||||
|
if (future == null) {
|
||||||
|
action._callParameter(out, handler.sncp_getParams());
|
||||||
|
action.convert.convertTo(out, Object.class, null);
|
||||||
|
} else {
|
||||||
|
Object[] sncpParams = handler.sncp_getParams();
|
||||||
|
future.whenComplete((v, e) -> {
|
||||||
|
if (e != null) {
|
||||||
|
response.getContext().getLogger().log(Level.INFO, "sncp CompleteAsync error(" + request + ")", e);
|
||||||
|
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
action._callParameter(out, sncpParams);
|
||||||
|
action.convert.convertTo(out, Object.class, v);
|
||||||
|
response.finish(0, out);
|
||||||
|
action.convert.offerBsonReader(in);
|
||||||
|
action.convert.offerBsonWriter(out);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", t);
|
response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", t);
|
||||||
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
|
response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null);
|
||||||
} finally {
|
|
||||||
action.convert.offerBsonReader(in);
|
|
||||||
action.convert.offerBsonWriter(out);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,6 +176,8 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
|
|
||||||
public Method method;
|
public Method method;
|
||||||
|
|
||||||
|
public Creator<SncpAsyncHandler> handlerCreator;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
protected BsonConvert convert;
|
protected BsonConvert convert;
|
||||||
|
|
||||||
@@ -145,8 +185,15 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
|
|
||||||
protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type, void的返回参数类型为null
|
protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type, void的返回参数类型为null
|
||||||
|
|
||||||
public abstract void action(final BsonReader in, final BsonWriter out) throws Throwable;
|
protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在AsyncHandler参数
|
||||||
|
|
||||||
|
protected boolean boolReturnTypeFuture = false; // 返回结果类型是否为 CompletableFuture
|
||||||
|
|
||||||
|
protected Class handlerFuncParamClass; //AsyncHandler参数的类型
|
||||||
|
|
||||||
|
public abstract void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable;
|
||||||
|
|
||||||
|
//只有同步方法才调用 (没有AsyncHandler、CompletableFuture)
|
||||||
public final void _callParameter(final BsonWriter out, final Object... params) {
|
public final void _callParameter(final BsonWriter out, final Object... params) {
|
||||||
if (paramAttrs != null) {
|
if (paramAttrs != null) {
|
||||||
for (int i = 1; i < paramAttrs.length; i++) {
|
for (int i = 1; i < paramAttrs.length; i++) {
|
||||||
@@ -162,17 +209,29 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
/**
|
/**
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* public class TestService implements Service {
|
* public class TestService implements Service {
|
||||||
|
*
|
||||||
* public boolean change(TestBean bean, String name, int id) {
|
* public boolean change(TestBean bean, String name, int id) {
|
||||||
|
* return false;
|
||||||
|
* }
|
||||||
*
|
*
|
||||||
|
* public void insert(AsyncHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public void update(long show, short v2, AsyncHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public CompletableFuture<String> changeName(TestBean bean, String name, int id) {
|
||||||
|
* return null;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* public class DynActionTestService_change extends SncpServletAction {
|
*
|
||||||
|
* class DynActionTestService_change extends SncpServletAction {
|
||||||
*
|
*
|
||||||
* public TestService service;
|
* public TestService service;
|
||||||
*
|
*
|
||||||
* @Override
|
* @Override
|
||||||
* public void action(final BsonReader in, final BsonWriter out) throws Throwable {
|
* public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
|
||||||
* TestBean arg1 = convert.convertFrom(paramTypes[1], in);
|
* TestBean arg1 = convert.convertFrom(paramTypes[1], in);
|
||||||
* String arg2 = convert.convertFrom(paramTypes[2], in);
|
* String arg2 = convert.convertFrom(paramTypes[2], in);
|
||||||
* int arg3 = convert.convertFrom(paramTypes[3], in);
|
* int arg3 = convert.convertFrom(paramTypes[3], in);
|
||||||
@@ -181,6 +240,57 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
* convert.convertTo(out, paramTypes[0], rs);
|
* convert.convertTo(out, paramTypes[0], rs);
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
|
*
|
||||||
|
* class DynActionTestService_insert extends SncpServletAction {
|
||||||
|
*
|
||||||
|
* public TestService service;
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
|
||||||
|
* SncpAsyncHandler arg0 = handler;
|
||||||
|
* convert.convertFrom(AsyncHandler.class, in);
|
||||||
|
* TestBean arg1 = convert.convertFrom(paramTypes[2], in);
|
||||||
|
* String arg2 = convert.convertFrom(paramTypes[3], in);
|
||||||
|
* int arg3 = convert.convertFrom(paramTypes[4], in);
|
||||||
|
* handler.sncp_setParams(arg0, arg1, arg2, arg3);
|
||||||
|
* service.insert(arg0, arg1, arg2, arg3);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class DynActionTestService_update extends SncpServletAction {
|
||||||
|
*
|
||||||
|
* public TestService service;
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
|
||||||
|
* long a1 = convert.convertFrom(paramTypes[1], in);
|
||||||
|
* short a2 = convert.convertFrom(paramTypes[2], in);
|
||||||
|
* SncpAsyncHandler a3 = handler;
|
||||||
|
* convert.convertFrom(AsyncHandler.class, in);
|
||||||
|
* TestBean arg1 = convert.convertFrom(paramTypes[4], in);
|
||||||
|
* String arg2 = convert.convertFrom(paramTypes[5], in);
|
||||||
|
* int arg3 = convert.convertFrom(paramTypes[6], in);
|
||||||
|
* handler.sncp_setParams(a1, a2, a3, arg1, arg2, arg3);
|
||||||
|
* service.update(a1, a2, a3, arg1, arg2, arg3);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* class DynActionTestService_changeName extends SncpServletAction {
|
||||||
|
*
|
||||||
|
* public TestService service;
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* public void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable {
|
||||||
|
* TestBean arg1 = convert.convertFrom(paramTypes[1], in);
|
||||||
|
* String arg2 = convert.convertFrom(paramTypes[2], in);
|
||||||
|
* int arg3 = convert.convertFrom(paramTypes[3], in);
|
||||||
|
* handler.sncp_setParams(arg1, arg2, arg3);
|
||||||
|
* CompletableFuture future = service.changeName(arg1, arg2, arg3);
|
||||||
|
* handler.sncp_setFuture(future);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
*
|
*
|
||||||
* @param service Service
|
* @param service Service
|
||||||
@@ -195,16 +305,19 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
final String supDynName = SncpServletAction.class.getName().replace('.', '/');
|
final String supDynName = SncpServletAction.class.getName().replace('.', '/');
|
||||||
final String serviceName = serviceClass.getName().replace('.', '/');
|
final String serviceName = serviceClass.getName().replace('.', '/');
|
||||||
final String convertName = BsonConvert.class.getName().replace('.', '/');
|
final String convertName = BsonConvert.class.getName().replace('.', '/');
|
||||||
|
final String handlerName = SncpAsyncHandler.class.getName().replace('.', '/');
|
||||||
|
final String asyncHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class);
|
||||||
final String convertReaderDesc = Type.getDescriptor(BsonReader.class);
|
final String convertReaderDesc = Type.getDescriptor(BsonReader.class);
|
||||||
final String convertWriterDesc = Type.getDescriptor(BsonWriter.class);
|
final String convertWriterDesc = Type.getDescriptor(BsonWriter.class);
|
||||||
final String serviceDesc = Type.getDescriptor(serviceClass);
|
final String serviceDesc = Type.getDescriptor(serviceClass);
|
||||||
|
final boolean boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType());
|
||||||
String newDynName = serviceName.substring(0, serviceName.lastIndexOf('/') + 1)
|
String newDynName = serviceName.substring(0, serviceName.lastIndexOf('/') + 1)
|
||||||
+ "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid;
|
+ "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid;
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Class.forName(newDynName.replace('/', '.'));
|
Class.forName(newDynName.replace('/', '.'));
|
||||||
newDynName += "_";
|
newDynName += "_";
|
||||||
} catch (Exception ex) {
|
} catch (Throwable ex) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,15 +349,42 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException(ex); //不可能会发生
|
throw new RuntimeException(ex); //不可能会发生
|
||||||
}
|
}
|
||||||
|
int handlerFuncIndex = -1;
|
||||||
|
Class handlerFuncClass = null;
|
||||||
{ // action方法
|
{ // action方法
|
||||||
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "action", "(" + convertReaderDesc + convertWriterDesc + ")V", null, new String[]{"java/lang/Throwable"}));
|
mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "action", "(" + convertReaderDesc + convertWriterDesc + asyncHandlerDesc + ")V", null, new String[]{"java/lang/Throwable"}));
|
||||||
//mv.setDebug(true);
|
//mv.setDebug(true);
|
||||||
int iconst = ICONST_1;
|
int iconst = ICONST_1;
|
||||||
int intconst = 1;
|
int intconst = 1;
|
||||||
int store = 3; //action的参数个数+1
|
int store = 4; //action的参数个数+1
|
||||||
final Class[] paramClasses = method.getParameterTypes();
|
final Class[] paramClasses = method.getParameterTypes();
|
||||||
int[][] codes = new int[paramClasses.length][2];
|
int[][] codes = new int[paramClasses.length][2];
|
||||||
for (int i = 0; i < paramClasses.length; i++) { //参数
|
for (int i = 0; i < paramClasses.length; i++) { //反序列化方法的每个参数
|
||||||
|
if (AsyncHandler.class.isAssignableFrom(paramClasses[i])) {
|
||||||
|
if (boolReturnTypeFuture) {
|
||||||
|
throw new RuntimeException(method + " have both AsyncHandler and CompletableFuture");
|
||||||
|
}
|
||||||
|
if (handlerFuncIndex >= 0) {
|
||||||
|
throw new RuntimeException(method + " have more than one AsyncHandler type parameter");
|
||||||
|
}
|
||||||
|
Sncp.checkAsyncModifier(paramClasses[i], method);
|
||||||
|
handlerFuncIndex = i;
|
||||||
|
handlerFuncClass = paramClasses[i];
|
||||||
|
mv.visitVarInsn(ALOAD, 3);
|
||||||
|
mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/'));
|
||||||
|
mv.visitVarInsn(ASTORE, store);
|
||||||
|
codes[i] = new int[]{ALOAD, store};
|
||||||
|
store++;
|
||||||
|
iconst++;
|
||||||
|
intconst++;
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
|
||||||
|
mv.visitLdcInsn(Type.getType(Type.getDescriptor(AsyncHandler.class)));
|
||||||
|
mv.visitVarInsn(ALOAD, 1);
|
||||||
|
mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false);
|
||||||
|
mv.visitInsn(POP);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
|
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
@@ -296,6 +436,43 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
intconst++;
|
intconst++;
|
||||||
store++;
|
store++;
|
||||||
}
|
}
|
||||||
|
if (boolReturnTypeFuture || handlerFuncIndex >= 0) { //调用SncpAsyncHandler.setParams(Object... params)
|
||||||
|
mv.visitVarInsn(ALOAD, 3);
|
||||||
|
if (paramClasses.length > 5) {
|
||||||
|
mv.visitIntInsn(BIPUSH, paramClasses.length);
|
||||||
|
} else {
|
||||||
|
mv.visitInsn(paramClasses.length + ICONST_0);
|
||||||
|
}
|
||||||
|
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||||
|
int insn = 3; //action的参数个数
|
||||||
|
for (int j = 0; j < paramClasses.length; j++) {
|
||||||
|
final Class pt = paramClasses[j];
|
||||||
|
mv.visitInsn(DUP);
|
||||||
|
insn++;
|
||||||
|
if (j <= 5) {
|
||||||
|
mv.visitInsn(ICONST_0 + j);
|
||||||
|
} else {
|
||||||
|
mv.visitIntInsn(BIPUSH, j);
|
||||||
|
}
|
||||||
|
if (pt.isPrimitive()) {
|
||||||
|
if (pt == long.class) {
|
||||||
|
mv.visitVarInsn(LLOAD, insn++);
|
||||||
|
} else if (pt == float.class) {
|
||||||
|
mv.visitVarInsn(FLOAD, insn++);
|
||||||
|
} else if (pt == double.class) {
|
||||||
|
mv.visitVarInsn(DLOAD, insn++);
|
||||||
|
} else {
|
||||||
|
mv.visitVarInsn(ILOAD, insn);
|
||||||
|
}
|
||||||
|
Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass();
|
||||||
|
mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false);
|
||||||
|
} else {
|
||||||
|
mv.visitVarInsn(ALOAD, insn);
|
||||||
|
}
|
||||||
|
mv.visitInsn(AASTORE);
|
||||||
|
}
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true);
|
||||||
|
}
|
||||||
{ //调用service
|
{ //调用service
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.visitFieldInsn(GETFIELD, newDynName, "service", serviceDesc);
|
mv.visitFieldInsn(GETFIELD, newDynName, "service", serviceDesc);
|
||||||
@@ -317,7 +494,13 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mv.visitVarInsn(ASTORE, store); //11
|
mv.visitVarInsn(ASTORE, store); //11
|
||||||
|
if (boolReturnTypeFuture) {
|
||||||
|
mv.visitVarInsn(ALOAD, 3);
|
||||||
|
mv.visitVarInsn(ALOAD, store);
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setFuture", "(Ljava/util/concurrent/CompletableFuture;)V", true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (!boolReturnTypeFuture && handlerFuncIndex < 0) { //同步方法
|
||||||
//------------------------- _callParameter 方法 --------------------------------
|
//------------------------- _callParameter 方法 --------------------------------
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
@@ -327,7 +510,7 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
mv.visitIntInsn(BIPUSH, paramClasses.length);
|
mv.visitIntInsn(BIPUSH, paramClasses.length);
|
||||||
}
|
}
|
||||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
|
||||||
int insn = 2;
|
int insn = 3;//action的参数个数
|
||||||
for (int j = 0; j < paramClasses.length; j++) {
|
for (int j = 0; j < paramClasses.length; j++) {
|
||||||
final Class pt = paramClasses[j];
|
final Class pt = paramClasses[j];
|
||||||
mv.visitInsn(DUP);
|
mv.visitInsn(DUP);
|
||||||
@@ -355,13 +538,13 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
mv.visitInsn(AASTORE);
|
mv.visitInsn(AASTORE);
|
||||||
}
|
}
|
||||||
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_callParameter", "(" + convertWriterDesc + "[Ljava/lang/Object;)V", false);
|
mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_callParameter", "(" + convertWriterDesc + "[Ljava/lang/Object;)V", false);
|
||||||
|
}
|
||||||
//-------------------------直接返回 或者 调用convertTo方法 --------------------------------
|
//-------------------------直接返回 或者 调用convertTo方法 --------------------------------
|
||||||
int maxStack = codes.length > 0 ? codes[codes.length - 1][1] : 1;
|
int maxStack = codes.length > 0 ? codes[codes.length - 1][1] : 1;
|
||||||
if (returnClass == void.class) { //返回
|
if (boolReturnTypeFuture || returnClass == void.class) { //返回
|
||||||
mv.visitInsn(RETURN);
|
mv.visitInsn(RETURN);
|
||||||
maxStack = 8;
|
maxStack = 8;
|
||||||
} else {
|
} else { //同步方法调用
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
|
mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class));
|
||||||
mv.visitVarInsn(ALOAD, 2);
|
mv.visitVarInsn(ALOAD, 2);
|
||||||
@@ -398,6 +581,9 @@ public final class SncpDynServlet extends SncpServlet {
|
|||||||
types[0] = rt;
|
types[0] = rt;
|
||||||
System.arraycopy(ptypes, 0, types, 1, ptypes.length);
|
System.arraycopy(ptypes, 0, types, 1, ptypes.length);
|
||||||
instance.paramTypes = types;
|
instance.paramTypes = types;
|
||||||
|
instance.handlerFuncParamIndex = handlerFuncIndex;
|
||||||
|
instance.handlerFuncParamClass = handlerFuncClass;
|
||||||
|
instance.boolReturnTypeFuture = boolReturnTypeFuture;
|
||||||
|
|
||||||
org.redkale.util.Attribute[] atts = new org.redkale.util.Attribute[ptypes.length + 1];
|
org.redkale.util.Attribute[] atts = new org.redkale.util.Attribute[ptypes.length + 1];
|
||||||
Annotation[][] anns = method.getParameterAnnotations();
|
Annotation[][] anns = method.getParameterAnnotations();
|
||||||
|
|||||||
@@ -30,26 +30,22 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
|
|||||||
|
|
||||||
public void addServlet(SncpServlet servlet, AnyValue conf) {
|
public void addServlet(SncpServlet servlet, AnyValue conf) {
|
||||||
setServletConf(servlet, conf);
|
setServletConf(servlet, conf);
|
||||||
synchronized (mappings) {
|
putMapping(servlet.getServiceid(), servlet);
|
||||||
mappings.put(servlet.getServiceid(), servlet);
|
putServlet(servlet);
|
||||||
servlets.add(servlet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SncpServlet> getSncpServlets() {
|
public List<SncpServlet> getSncpServlets() {
|
||||||
ArrayList<SncpServlet> list = new ArrayList<>(servlets.size());
|
return new ArrayList<>(getServlets());
|
||||||
servlets.forEach(x -> list.add((SncpServlet) x));
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(SncpContext context, AnyValue config) {
|
public void init(SncpContext context, AnyValue config) {
|
||||||
servlets.forEach(s -> s.init(context, getServletConf(s)));
|
getServlets().forEach(s -> s.init(context, getServletConf(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy(SncpContext context, AnyValue config) {
|
public void destroy(SncpContext context, AnyValue config) {
|
||||||
servlets.forEach(s -> s.destroy(context, getServletConf(s)));
|
getServlets().forEach(s -> s.destroy(context, getServletConf(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,7 +54,7 @@ public class SncpPrepareServlet extends PrepareServlet<DLong, SncpContext, SncpR
|
|||||||
response.finish(pongBuffer.duplicate());
|
response.finish(pongBuffer.duplicate());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SncpServlet servlet = (SncpServlet) mappings.get(request.getServiceid());
|
SncpServlet servlet = (SncpServlet) mappingServlet(request.getServiceid());
|
||||||
if (servlet == null) {
|
if (servlet == null) {
|
||||||
response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid
|
response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ public final class SncpServer extends Server<DLong, SncpContext, SncpRequest, Sn
|
|||||||
super.init(config);
|
super.init(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSncpServlet(ServiceWrapper entry) {
|
public void addSncpServlet(Service sncpService) {
|
||||||
for (Class type : entry.getTypes()) {
|
for (Class type : Sncp.getResourceTypes(sncpService)) {
|
||||||
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), entry.getName(), type, entry.getService());
|
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), Sncp.getResourceName(sncpService), type, sncpService);
|
||||||
this.prepare.addServlet(sds, null, entry.getConf());
|
this.prepare.addServlet(sds, null, Sncp.getConf(sncpService));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Service> void addSncpServlet(Class<T> serviceType, String name, T service, AnyValue conf) {
|
public <T extends Service> void addSncpServlet(Class<T> serviceTypeClass, String name, T service, AnyValue conf) {
|
||||||
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), name, serviceType, service);
|
SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), name, serviceTypeClass, service);
|
||||||
this.prepare.addServlet(sds, null, conf);
|
this.prepare.addServlet(sds, null, conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.redkale.net.sncp;
|
package org.redkale.net.sncp;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.*;
|
||||||
import org.redkale.net.*;
|
import org.redkale.net.*;
|
||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
@@ -20,6 +21,14 @@ public abstract class SncpServlet extends Servlet<SncpContext, SncpRequest, Sncp
|
|||||||
|
|
||||||
public abstract DLong getServiceid();
|
public abstract DLong getServiceid();
|
||||||
|
|
||||||
|
protected ExecutorService getExecutor() {
|
||||||
|
Thread thread = Thread.currentThread();
|
||||||
|
if (thread instanceof WorkThread) {
|
||||||
|
return ((WorkThread) thread).getExecutor();
|
||||||
|
}
|
||||||
|
return ForkJoinPool.commonPool();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean equals(Object obj) {
|
public final boolean equals(Object obj) {
|
||||||
if (!(obj instanceof SncpServlet)) return false;
|
if (!(obj instanceof SncpServlet)) return false;
|
||||||
|
|||||||
33
src/org/redkale/service/AbstractService.java
Normal file
33
src/org/redkale/service/AbstractService.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.redkale.service;
|
||||||
|
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import org.redkale.net.WorkThread;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author zhangjx
|
||||||
|
*/
|
||||||
|
public abstract class AbstractService implements Service {
|
||||||
|
|
||||||
|
protected void runAsync(Runnable runner) {
|
||||||
|
Thread thread = Thread.currentThread();
|
||||||
|
if (thread instanceof WorkThread) {
|
||||||
|
((WorkThread) thread).runAsync(runner);
|
||||||
|
} else {
|
||||||
|
ForkJoinPool.commonPool().execute(runner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExecutorService getExecutor() {
|
||||||
|
Thread thread = Thread.currentThread();
|
||||||
|
if (thread instanceof WorkThread) {
|
||||||
|
return ((WorkThread) thread).getExecutor();
|
||||||
|
}
|
||||||
|
return ForkJoinPool.commonPool();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import org.redkale.source.*;
|
|||||||
import org.redkale.util.*;
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 实现进程间DataSource的缓存数据同步
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
@@ -26,20 +27,23 @@ public class DataCacheListenerService implements DataCacheListener, Service {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@RpcMultiRun(selfrun = false, async = true)
|
@RpcMultiRun(selfrun = false, async = true)
|
||||||
public <T> void insertCache(Class<T> clazz, T... entitys) {
|
public <T> int insertCache(Class<T> clazz, T... entitys) {
|
||||||
((DataDefaultSource) source).insertCache(clazz, entitys);
|
if (!(source instanceof DataCacheListener)) return -2;
|
||||||
|
return ((DataCacheListener) source).insertCache(clazz, entitys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@RpcMultiRun(selfrun = false, async = true)
|
@RpcMultiRun(selfrun = false, async = true)
|
||||||
public <T> void updateCache(Class<T> clazz, T... entitys) {
|
public <T> int updateCache(Class<T> clazz, T... entitys) {
|
||||||
((DataDefaultSource) source).updateCache(clazz, entitys);
|
if (!(source instanceof DataCacheListener)) return -2;
|
||||||
|
return ((DataCacheListener) source).updateCache(clazz, entitys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@RpcMultiRun(selfrun = false, async = true)
|
@RpcMultiRun(selfrun = false, async = true)
|
||||||
public <T> void deleteCache(Class<T> clazz, Serializable... ids) {
|
public <T> int deleteCache(Class<T> clazz, Serializable... ids) {
|
||||||
((DataDefaultSource) source).deleteCache(clazz, ids);
|
if (!(source instanceof DataCacheListener)) return -2;
|
||||||
|
return ((DataCacheListener) source).deleteCache(clazz, ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,306 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.redkale.service;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.sql.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.*;
|
|
||||||
import javax.annotation.*;
|
|
||||||
import org.redkale.source.*;
|
|
||||||
import org.redkale.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DataSource对应的Service类, 该类主要特点是将所有含FilterBean参数的方法重载成FilterNode对应的方法。
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* 详情见: https://redkale.org
|
|
||||||
*
|
|
||||||
* @author zhangjx
|
|
||||||
*/
|
|
||||||
@AutoLoad(false)
|
|
||||||
@ResourceType({DataSourceService.class, DataSource.class})
|
|
||||||
public class DataSourceService implements DataSource, Service, AutoCloseable {
|
|
||||||
|
|
||||||
@Resource(name = "$")
|
|
||||||
private DataSource source;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> void insert(@RpcCall(DataCallArrayAttribute.class) T... values) {
|
|
||||||
source.insert(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int delete(T... values) {
|
|
||||||
return source.delete(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int delete(final Class<T> clazz, final Serializable... ids) {
|
|
||||||
return source.delete(clazz, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int delete(final Class<T> clazz, FilterNode node) {
|
|
||||||
return source.delete(clazz, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int update(T... values) {
|
|
||||||
return source.update(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int updateColumn(final Class<T> clazz, final Serializable id, final String column, final Serializable value) {
|
|
||||||
return source.updateColumn(clazz, id, column, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int updateColumn(final Class<T> clazz, final String column, final Serializable value, final FilterNode node) {
|
|
||||||
return source.updateColumn(clazz, column, value, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int updateColumn(final Class<T> clazz, final Serializable id, final ColumnValue... values) {
|
|
||||||
return source.updateColumn(clazz, id, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int updateColumn(final Class<T> clazz, final FilterNode node, final ColumnValue... values) {
|
|
||||||
return source.updateColumn(clazz, node, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int updateColumns(T bean, final String... columns) {
|
|
||||||
return source.updateColumns(bean, columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> int updateColumns(T bean, final FilterNode node, final String... columns) {
|
|
||||||
return source.updateColumns(bean, node, columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Number getNumberResult(final Class entityClass, FilterFunc func, final String column) {
|
|
||||||
return source.getNumberResult(entityClass, func, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final Number getNumberResult(final Class entityClass, FilterFunc func, final String column, FilterBean bean) {
|
|
||||||
return getNumberResult(entityClass, func, column, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Number getNumberResult(final Class entityClass, FilterFunc func, final String column, FilterNode node) {
|
|
||||||
return source.getNumberResult(entityClass, func, column, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Number getNumberResult(final Class entityClass, FilterFunc func, final Number defVal, final String column) {
|
|
||||||
return source.getNumberResult(entityClass, func, defVal, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final Number getNumberResult(final Class entityClass, FilterFunc func, final Number defVal, final String column, FilterBean bean) {
|
|
||||||
return getNumberResult(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Number getNumberResult(final Class entityClass, FilterFunc func, final Number defVal, final String column, FilterNode node) {
|
|
||||||
return source.getNumberResult(entityClass, func, defVal, column, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, FilterFunc func, final String funcColumn) {
|
|
||||||
return source.queryColumnMap(entityClass, keyColumn, func, funcColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, FilterFunc func, final String funcColumn, FilterBean bean) {
|
|
||||||
return queryColumnMap(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T, K extends Serializable, N extends Number> Map<K, N> queryColumnMap(final Class<T> entityClass, final String keyColumn, FilterFunc func, final String funcColumn, FilterNode node) {
|
|
||||||
return source.queryColumnMap(entityClass, keyColumn, func, funcColumn, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T find(final Class<T> clazz, final Serializable pk) {
|
|
||||||
return source.find(clazz, pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T find(final Class<T> clazz, SelectColumn selects, final Serializable pk) {
|
|
||||||
return source.find(clazz, selects, pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T find(final Class<T> clazz, final String column, final Serializable key) {
|
|
||||||
return source.find(clazz, column, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> T find(final Class<T> clazz, FilterBean bean) {
|
|
||||||
return find(clazz, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T find(final Class<T> clazz, FilterNode node) {
|
|
||||||
return source.find(clazz, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> T find(final Class<T> clazz, final SelectColumn selects, FilterBean bean) {
|
|
||||||
return find(clazz, selects, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T find(final Class<T> clazz, final SelectColumn selects, final FilterNode node) {
|
|
||||||
return source.find(clazz, selects, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> boolean exists(final Class<T> clazz, final Serializable pk) {
|
|
||||||
return source.exists(clazz, pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> boolean exists(final Class<T> clazz, FilterBean bean) {
|
|
||||||
return exists(clazz, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> boolean exists(final Class<T> clazz, FilterNode node) {
|
|
||||||
return source.exists(clazz, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(String selectedColumn, Class<T> clazz, final String column, final Serializable key) {
|
|
||||||
return source.queryColumnSet(selectedColumn, clazz, column, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T, V extends Serializable> HashSet<V> queryColumnSet(String selectedColumn, Class<T> clazz, FilterBean bean) {
|
|
||||||
return queryColumnSet(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T, V extends Serializable> HashSet<V> queryColumnSet(String selectedColumn, Class<T> clazz, FilterNode node) {
|
|
||||||
return source.queryColumnSet(selectedColumn, clazz, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, final String column, final Serializable key) {
|
|
||||||
return source.queryColumnList(selectedColumn, clazz, column, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, FilterBean bean) {
|
|
||||||
return queryColumnList(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T, V extends Serializable> List<V> queryColumnList(String selectedColumn, Class<T> clazz, FilterNode node) {
|
|
||||||
return source.queryColumnList(selectedColumn, clazz, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T, V extends Serializable> Sheet<V> queryColumnSheet(String selectedColumn, Class<T> clazz, Flipper flipper, FilterBean bean) {
|
|
||||||
return queryColumnSheet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T, V extends Serializable> Sheet<V> queryColumnSheet(String selectedColumn, Class<T> clazz, Flipper flipper, FilterNode node) {
|
|
||||||
return source.queryColumnSheet(selectedColumn, clazz, flipper, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> List<T> queryList(final Class<T> clazz, final String column, final Serializable key) {
|
|
||||||
return source.queryList(clazz, column, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> List<T> queryList(final Class<T> clazz, final FilterBean bean) {
|
|
||||||
return queryList(clazz, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> List<T> queryList(final Class<T> clazz, final FilterNode node) {
|
|
||||||
return source.queryList(clazz, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final FilterBean bean) {
|
|
||||||
return queryList(clazz, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final FilterNode node) {
|
|
||||||
return source.queryList(clazz, selects, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final String column, final Serializable key) {
|
|
||||||
return source.queryList(clazz, flipper, column, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final FilterBean bean) {
|
|
||||||
return queryList(clazz, flipper, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> List<T> queryList(final Class<T> clazz, final Flipper flipper, final FilterNode node) {
|
|
||||||
return source.queryList(clazz, flipper, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) {
|
|
||||||
return queryList(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> List<T> queryList(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) {
|
|
||||||
return source.queryList(clazz, selects, flipper, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterBean bean) {
|
|
||||||
return querySheet(clazz, flipper, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> Sheet<T> querySheet(final Class<T> clazz, final Flipper flipper, final FilterNode node) {
|
|
||||||
return source.querySheet(clazz, flipper, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) {
|
|
||||||
return querySheet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> Sheet<T> querySheet(final Class<T> clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) {
|
|
||||||
return source.querySheet(clazz, selects, flipper, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws Exception {
|
|
||||||
source.getClass().getMethod("close").invoke(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void directQuery(String sql, Consumer<ResultSet> consumer) {
|
|
||||||
source.directQuery(sql, consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final int[] directExecute(String... sqls) {
|
|
||||||
return source.directExecute(sqls);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -7,15 +7,16 @@ package org.redkale.service;
|
|||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
|
import java.text.MessageFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于定义错误码的注解
|
* 用于定义错误码的注解 <br>
|
||||||
* 结果码定义范围:
|
* 结果码定义范围: <br>
|
||||||
* // 10000001 - 19999999 预留给Redkale的核心包使用
|
* // 10000001 - 19999999 预留给Redkale的核心包使用 <br>
|
||||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用
|
* // 20000001 - 29999999 预留给Redkale的扩展包使用 <br>
|
||||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用
|
* // 30000001 - 99999999 预留给Dev开发系统自身使用 <br>
|
||||||
*
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
@@ -28,6 +29,39 @@ public @interface RetLabel {
|
|||||||
|
|
||||||
String value();
|
String value();
|
||||||
|
|
||||||
|
public static abstract class AbstractRetCode {
|
||||||
|
|
||||||
|
protected static final Map<Integer, String> rets = new HashMap();
|
||||||
|
|
||||||
|
protected static void load(Class clazz) {
|
||||||
|
rets.putAll(RetLoader.load(clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetResult retResult(int retcode) {
|
||||||
|
if (retcode == 0) return RetResult.success();
|
||||||
|
return new RetResult(retcode, retInfo(retcode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetResult retResult(int retcode, Object... args) {
|
||||||
|
if (retcode == 0) return RetResult.success();
|
||||||
|
if (args == null || args.length < 1) return new RetResult(retcode, retInfo(retcode));
|
||||||
|
String info = MessageFormat.format(retInfo(retcode), args);
|
||||||
|
return new RetResult(retcode, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RetResult set(RetResult result, int retcode, Object... args) {
|
||||||
|
if (retcode == 0) return result.retcode(0).retinfo("");
|
||||||
|
if (args == null || args.length < 1) return result.retcode(retcode).retinfo(retInfo(retcode));
|
||||||
|
String info = MessageFormat.format(retInfo(retcode), args);
|
||||||
|
return result.retcode(retcode).retinfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String retInfo(int retcode) {
|
||||||
|
if (retcode == 0) return "成功";
|
||||||
|
return rets.getOrDefault(retcode, "未知错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static abstract class RetLoader {
|
public static abstract class RetLoader {
|
||||||
|
|
||||||
public static Map<Integer, String> load(Class clazz) {
|
public static Map<Integer, String> load(Class clazz) {
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ package org.redkale.service;
|
|||||||
import org.redkale.convert.json.*;
|
import org.redkale.convert.json.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用的结果对象,在常见的HTTP+JSON接口中返回的结果需要含结果码,错误信息,和实体对象。
|
* 通用的结果对象,在常见的HTTP+JSON接口中返回的结果需要含结果码,错误信息,和实体对象。 <br>
|
||||||
* 通常前四位为模块,后四位为操作。
|
* 结果码定义通常前四位为模块,后四位为操作。<br>
|
||||||
* 结果码定义范围:
|
* 结果码定义范围: <br>
|
||||||
* // 10000001 - 19999999 预留给Redkale的核心包使用
|
* // 10000001 - 19999999 预留给Redkale的核心包使用 <br>
|
||||||
* // 20000001 - 29999999 预留给Redkale的扩展包使用
|
* // 20000001 - 29999999 预留给Redkale的扩展包使用 <br>
|
||||||
* // 30000001 - 99999999 预留给Dev开发系统自身使用
|
* // 30000001 - 99999999 预留给Dev开发系统自身使用 <br>
|
||||||
*
|
* <p>
|
||||||
* 详情见: https://redkale.org
|
* 详情见: https://redkale.org
|
||||||
*
|
*
|
||||||
* @author zhangjx
|
* @author zhangjx
|
||||||
@@ -22,13 +22,6 @@ import org.redkale.convert.json.*;
|
|||||||
*/
|
*/
|
||||||
public class RetResult<T> {
|
public class RetResult<T> {
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 RetResult.success() 方法代替
|
|
||||||
*
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
private static final RetResult SUCCESS = new RetResult();
|
|
||||||
|
|
||||||
protected int retcode;
|
protected int retcode;
|
||||||
|
|
||||||
protected String retinfo;
|
protected String retinfo;
|
||||||
@@ -119,10 +112,20 @@ public class RetResult<T> {
|
|||||||
this.retcode = retcode;
|
this.retcode = retcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结果信息,通常retcode != 0时值为错误信息
|
||||||
|
*
|
||||||
|
* @return 结果信息
|
||||||
|
*/
|
||||||
public String getRetinfo() {
|
public String getRetinfo() {
|
||||||
return retinfo;
|
return retinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置结果信息
|
||||||
|
*
|
||||||
|
* @param retinfo 结果信息
|
||||||
|
*/
|
||||||
public void setRetinfo(String retinfo) {
|
public void setRetinfo(String retinfo) {
|
||||||
this.retinfo = retinfo;
|
this.retinfo = retinfo;
|
||||||
}
|
}
|
||||||
@@ -136,6 +139,11 @@ public class RetResult<T> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置结果对象
|
||||||
|
*
|
||||||
|
* @param result T
|
||||||
|
*/
|
||||||
public void setResult(T result) {
|
public void setResult(T result) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user