mirror of
https://gitee.com/farsunset/cim.git
synced 2025-07-02 07:04:50 +08:00
Compare commits
156 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e6d50f36cb | ||
|
3634392e7f | ||
|
4a68b47fdb | ||
|
965abd1467 | ||
|
e8c87358ac | ||
|
fe788c50c0 | ||
|
b02d43af32 | ||
|
c4a90251db | ||
|
0d6ff67f08 | ||
|
626ee06c21 | ||
|
eed7ffb747 | ||
|
bdd66ce133 | ||
|
7175585d37 | ||
|
baa536f442 | ||
|
62fc15b33b | ||
|
53fd3aa005 | ||
|
b126dfc198 | ||
|
ec130c97b4 | ||
|
a4b401e16b | ||
|
5d22b80a37 | ||
|
7e7b4c03da | ||
|
4098e564f4 | ||
|
570b977910 | ||
|
66c4ed8398 | ||
|
e5ba2ca32e | ||
|
36270254b0 | ||
|
789c89185a | ||
|
d189d4cca4 | ||
|
a572ccc9a3 | ||
|
c1df188360 | ||
|
e60f64ec55 | ||
|
43308baa5f | ||
|
a9909a4039 | ||
|
ce2377c72b | ||
|
c9177612ea | ||
|
5cdb08306f | ||
|
cfac26b7ad | ||
|
6a02c58d4e | ||
|
9856788ed1 | ||
|
1c9efbbaf0 | ||
|
37bb7a06bc | ||
|
fdf435a873 | ||
|
df3385151a | ||
|
2a3d315d75 | ||
|
c8f45c9a97 | ||
|
03a309d8ee | ||
|
5252c00a8f | ||
|
d7e8649c17 | ||
|
8a494b9c02 | ||
|
2a8ec5865e | ||
|
2ba80c85cf | ||
|
457eb21769 | ||
|
f3a7ab7cdf | ||
|
c4b30acef3 | ||
|
c55c3d5012 | ||
|
721839d981 | ||
|
c5947f718f | ||
|
f798e8a6fb | ||
|
1d8bf474f9 | ||
|
68b3ebd5c4 | ||
|
046bfd8ade | ||
|
97fe0a2721 | ||
|
adf4f827e6 | ||
|
11f40a0f51 | ||
|
40a4353d21 | ||
|
e89a3918c5 | ||
|
35ab7cc72c | ||
|
2a2613b5cc | ||
|
560da9bebe | ||
|
538f80cee1 | ||
|
ea9efc8594 | ||
|
c93ac73e80 | ||
|
a0ddbe47c5 | ||
|
7a9a369960 | ||
|
69cbb31d9e | ||
|
c62d646cfc | ||
|
bb7266e170 | ||
|
40a32b8887 | ||
|
d2e9727df7 | ||
|
822bc6f104 | ||
|
e54f90cb04 | ||
|
0d45aac81b | ||
|
994b78272d | ||
|
2996271f83 | ||
|
c5bed5b689 | ||
|
8cd43fdf9c | ||
|
cba314151d | ||
|
461be0fab1 | ||
|
7579845369 | ||
|
ec94bfcdf9 | ||
|
b9afa1ca08 | ||
|
585f3c4c80 | ||
|
bffe162516 | ||
|
a339e2640c | ||
|
c9552347a4 | ||
|
ac32108750 | ||
|
4051660957 | ||
|
0e8a6aba18 | ||
|
8f9d420ba2 | ||
|
16d5738451 | ||
|
42ba6e0cd4 | ||
|
62107d5b44 | ||
|
be301d95c9 | ||
|
6c61a09682 | ||
|
ad60928b54 | ||
|
2563c3fc12 | ||
|
bf7b6e79ae | ||
|
eadac1b887 | ||
|
4cc4d1d3b4 | ||
|
0263f804bb | ||
|
1cadbd167c | ||
|
91a890396d | ||
|
fbe5e24e41 | ||
|
ec8b4a8392 | ||
|
9247b3daa5 | ||
|
5ad68b833b | ||
|
8739fb87eb | ||
|
39a4ae2485 | ||
|
4e98a6e571 | ||
|
bf134137ee | ||
|
a505d3f06c | ||
|
e19c6424f2 | ||
|
a901e2b1f6 | ||
|
35762f5ec9 | ||
|
7275b4b5ea | ||
|
bf9b2860df | ||
|
84786e6fdb | ||
|
f6699c039e | ||
|
4e0520b2b5 | ||
|
1faf67a977 | ||
|
1c1480ef2c | ||
|
77ad75cbba | ||
|
0ce42c6901 | ||
|
3d7ffd9038 | ||
|
418c26f6f9 | ||
|
562508d5a2 | ||
|
98c04c936d | ||
|
eec65bcfe1 | ||
|
5af799d808 | ||
|
abc3300f9d | ||
|
ddd7e741b8 | ||
|
bfa0b5b8ea | ||
|
bcb7a59ef4 | ||
|
919df56402 | ||
|
15885dbee5 | ||
|
673bb6bbc0 | ||
|
6baf846b4c | ||
|
c9e0d15bd1 | ||
|
c9c317d6c8 | ||
|
cada6b890e | ||
|
fd4b0eff90 | ||
|
7c46874e44 | ||
|
19f634e31e | ||
|
7bb5ae30ec | ||
|
4bf070fa0d | ||
|
756ff96b8a |
30
.gitignore
vendored
30
.gitignore
vendored
@ -1,2 +1,28 @@
|
||||
target/
|
||||
app/build/
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
/target/
|
||||
/.idea/
|
||||
.idea
|
||||
*.class
|
||||
*/local.properties
|
100
README.md
100
README.md
@ -1,97 +1,69 @@
|
||||
#### 项目介绍
|
||||
CIM项目是基于mina或者netty框架下的推送系统,我们平常使用第三方的推送SDK,如极光推送,百度推送,小米推送,以及腾讯信鸽等来支撑自己的移动端的业务,或许有一些用户自己实现即时通讯系统的需求,那么CIM为您提供了一个解决方案或者思路,目前CIM支持 websocket,android,ios,桌面应用,系统应用等多端接入支持,目前CIM服务端使用springboot搭建仅仅拥有消息推送的功能,可以理解为是一个消息队列方案,因此不包含任何业务功能。关于数据缓存与持久化都需要使用者自己开发,但是配备了比较完整的使用文档。最后希望CIM能为您带来一些价值。
|
||||
|
||||
---
|
||||
## 以下是基于CIM开发的2款产品,并不开源!!!
|
||||
## [http://farsunset.com](http://farsunset.com)
|
||||
#### 和信
|
||||
和信是基于CIM组件开发的一整套完整的产品,面向所有人开放注册的试用场景。具有丰富的功能,聊天、群组、好友列表、黑名单、公众号、朋友圈等功能。不依赖任何第三方服务,可以私有化部署。
|
||||
## 收费产品介绍
|
||||
|
||||
#### 侣信
|
||||
侣信是基于CIM组件开发的一整套完整的产品,面向中小企业和者各类团队组织内部交流使用工具。具有丰富的功能,聊天、群组、部门组织、公众号、内部朋友圈等功能。不依赖任何第三方服务,可以私有化部署。
|
||||
#### 布咕(https://farsunset.com/)
|
||||
布咕是基于CIM组件开发的一整套完整的产品,面向所有人开放注册的试用场景。具有丰富的功能,音视频会议,聊天、群组、好友,组织架构、公众号、朋友圈等功能。不依赖任何第三方云服务,可以私有化部署。
|
||||
|
||||
|
||||
<div align="center">
|
||||
<img src="http://staticres.oss-cn-hangzhou.aliyuncs.com/hoxin/call_video_incoming.jpg" width="24%" />
|
||||
<img src="http://staticres.oss-cn-hangzhou.aliyuncs.com/hoxin/group_video_calling.jpg" width="24%" />
|
||||
<img src="http://staticres.oss-cn-hangzhou.aliyuncs.com/hoxin/single_chatting_light.jpg" width="24%" />
|
||||
<img src="http://staticres.oss-cn-hangzhou.aliyuncs.com/hoxin/single_chatting_dark.jpg" width="24%" />
|
||||
<img src="http://staticres.oss-cn-hangzhou.aliyuncs.com/hoxin/moment_timeline_light.jpg" width="24%" />
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
#### 目录说明
|
||||
## 项目介绍
|
||||
|
||||
1.cim-use-examples是各个客户端使用示例
|
||||
[在线文档](https://www.yuque.com/yuanfangxiyang/ma4ytb)(https://www.yuque.com/yuanfangxiyang/ma4ytb)
|
||||
|
||||
2.cim-client-sdk 是各个客户端的SDK源码
|
||||
CIM是一套完善的消息推送框架,可应用于信令推送,即时聊天,移动设备指令推送等领域。开发者可沉浸于业务开发,不用关心消息通道长连接、消息编解码协议等繁杂处理。
|
||||
|
||||
3.cim-server-sdk 是服务端SDK源码,分为 mina和netty 两个版本,二者任选其一
|
||||
CIM采用业内主流开源技术构建,易于扩展和使用,并完美支持集群部署支持海量链接,目前支持websocket,android,ios,桌面应用,系统应用等多端接入持,可应用于移动应用,物联网,智能家居,嵌入式开发,桌面应用,WEB应用即时消服务。
|
||||
|
||||
4.cim-boot-server是springboot服务端工程源码,使用Idea工具开发
|
||||
历时10年基于CIM的项目已经运行在全国各个地方,包括上市公司,各地政务系统,警务系统等服务于上百家客户,希望CIM也能为您带来价值,如果您也希望加入项目成为贡献者,请联系我。如果觉得有用欢迎打赏。
|
||||
|
||||
其中所有的sdk均为IntelliJ IDEA工程,Maven打包成jar导出引入到对应的客户端或服务端工程
|
||||
如果对您有价值,请送一个star和Fork喔~
|
||||
|
||||
<div align="center">
|
||||
<img src="http://staticres.oss-cn-hangzhou.aliyuncs.com/qcode/ali_pay.jpg" width="30%" />
|
||||
<img src="http://staticres.oss-cn-hangzhou.aliyuncs.com/qcode/wechat_pay.jpg" width="30%" />
|
||||
</div>
|
||||
|
||||
#### 功能预览
|
||||
|
||||
|
||||
1.控制台页面[http://127.0.0.1:8080](http://127.0.0.1:8080)
|
||||
## 功能预览
|
||||
|
||||
### 控制台页面[http://127.0.0.1:8080](http://127.0.0.1:8080)
|
||||

|
||||
2.Android客户端
|
||||
|
||||
### Android客户端
|
||||

|
||||
3.Web客户端
|
||||
|
||||
### Web客户端
|
||||

|
||||
|
||||
|
||||
#### 更新日志
|
||||
-------------------------------------------------------------------------------------------
|
||||
版本:3.5.0/时间:2018-08-22
|
||||
|
||||
1.服务端由原来的 spring+struts2修改为springboot工程
|
||||
|
||||
2.全面重写websocket的实现,全面拥抱protobuf,替换json序列化方式,更加高效
|
||||
|
||||
-------------------------------------------------------------------------------------------
|
||||
版本:3.6.0/时间:2019-04-17
|
||||
|
||||
1.服务端springboot升级2.1.4,protobuf升级3.7.0
|
||||
|
||||
2.android sdk升级,适配android8.0+,修复一些之前的兼容性问题
|
||||
|
||||
3.消息的id字段名由mid修改为id,类型由String修改为long;
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------
|
||||
版本:3.7.0/时间:2019-05-13
|
||||
|
||||
1.服务端cim-boot-server修改为idea maven工程
|
||||
|
||||
2.android sdk优化升级,去除mina或netty相关包的依赖
|
||||
|
||||
3.java sdk优化升级,去除mina或netty相关包的依赖
|
||||
|
||||
4.新增web sdk,可以由index.html快速启动demo
|
||||
|
||||
5.修正文档中一些疏漏
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------
|
||||
版本:3.7.5/时间:2019-11-13
|
||||
|
||||
1.android sdk 优化,使用protobuf-lite版本替代较为臃肿的protobut-java版本
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------
|
||||
版本:3.8.0/时间:2020-01-17
|
||||
## Maven Gradle
|
||||
|
||||
1.服务端sdk将websocket的服务端口和原生socket的端口分离,可以禁用其中一个或者同时启用
|
||||
服务端sdk引用
|
||||
|
||||
2.web端的sdk简化流程不再需要心跳响应,修改了连接成功回调方法名称和创建连接方法名
|
||||
```
|
||||
|
||||
3.andoid sdk修改几个广播action的名称以及回调方法名称,详见cim-client-android工程
|
||||
<dependency>
|
||||
<groupId>com.farsunset</groupId>
|
||||
<artifactId>cim-server-sdk-netty</artifactId>
|
||||
<version>4.2.10</version>
|
||||
</dependency>
|
||||
|
||||
4.所有sdk均使用maven构建,idea工具开发,发现多处代码单词拼写错误,使用阿里语法检测组件优化了部分代码
|
||||
```
|
||||
|
||||
5.同步修改了文档
|
||||
|
||||
android端sdk引用
|
||||
|
||||
```
|
||||
implementation "com.farsunset:cim-android-sdk:4.2.15"
|
||||
```
|
2
cim-boot-server/README.txt
Normal file
2
cim-boot-server/README.txt
Normal file
@ -0,0 +1,2 @@
|
||||
如果你不想搭建服务端也可以使用,公共的服务器做测试喔
|
||||
具体信息参见:https://www.yuque.com/yuanfangxiyang/ma4ytb/vvy3iz#yC5Vq
|
@ -1,90 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="Spring" name="Spring">
|
||||
<configuration />
|
||||
</facet>
|
||||
<facet type="web" name="Web">
|
||||
<configuration>
|
||||
<webroots />
|
||||
<sourceRoots>
|
||||
<root url="file://$MODULE_DIR$/src/main/java" />
|
||||
<root url="file://$MODULE_DIR$/src/main/resources" />
|
||||
</sourceRoots>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.12.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.12.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
|
||||
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" scope="RUNTIME" name="Maven: org.yaml:snakeyaml:1.25" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.31" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.31" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.31" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.0.18.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.5.RELEASE" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.freemarker:freemarker:2.3.29" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.springframework:spring-context-support:5.2.4.RELEASE" level="project" />
|
||||
<orderEntry type="module-library">
|
||||
<library name="Maven: com.farsunset:cim-server-sdk:3.8.0">
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/libs/cim-server-sdk-netty-3.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="library" name="Maven: org.apache.mina:mina-core:2.1.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.35.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.35.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.35.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.35.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.35.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.35.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.35.Final" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.protobuf:protobuf-java:3.11.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: cn.teaey.apns4j:apns4j:1.1.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.8.1" level="project" />
|
||||
</component>
|
||||
</module>
|
@ -1,671 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="cim-boot-server" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
<bytecodeTargetLevel>
|
||||
<module name="cim-boot-server" target="1.8" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ContextJavaBeanUnresolvedMethodsInspection" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="TOP_LEVEL_CLASS_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="INNER_CLASS_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="METHOD_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="FIELD_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="IGNORE_DEPRECATED" value="false" />
|
||||
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
|
||||
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
|
||||
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
|
||||
<option name="myAdditionalJavadocTags" value="date" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SpringJavaInjectionPointsAutowiringInspection" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
</profile>
|
||||
<version value="1.0" />
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="cim-boot-server" options="-parameters" />
|
||||
</option>
|
||||
</component>
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/cim-boot-server.iml" filepath="$PROJECT_DIR$/cim-boot-server.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
<component name="libraryTable">
|
||||
<library name="Maven: ch.qos.logback:logback-classic:1.2.3">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: ch.qos.logback:logback-core:1.2.3">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: cn.teaey.apns4j:apns4j:1.1.4">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/cn/teaey/apns4j/apns4j/1.1.4/apns4j-1.1.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/cn/teaey/apns4j/apns4j/1.1.4/apns4j-1.1.4-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/cn/teaey/apns4j/apns4j/1.1.4/apns4j-1.1.4-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.fasterxml:classmate:1.5.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/classmate/1.5.1/classmate-1.5.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/fasterxml/classmate/1.5.1/classmate-1.5.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: com.google.protobuf:protobuf-java:3.11.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/protobuf/protobuf-java/3.11.1/protobuf-java-3.11.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/protobuf/protobuf-java/3.11.1/protobuf-java-3.11.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/protobuf/protobuf-java/3.11.1/protobuf-java-3.11.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: commons-io:commons-io:2.6">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.6/commons-io-2.6-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: io.netty:netty-buffer:4.1.35.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-buffer/4.1.35.Final/netty-buffer-4.1.35.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-buffer/4.1.35.Final/netty-buffer-4.1.35.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-buffer/4.1.35.Final/netty-buffer-4.1.35.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: io.netty:netty-codec-http:4.1.35.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-codec-http/4.1.35.Final/netty-codec-http-4.1.35.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-codec-http/4.1.35.Final/netty-codec-http-4.1.35.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-codec-http/4.1.35.Final/netty-codec-http-4.1.35.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: io.netty:netty-codec:4.1.35.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-codec/4.1.35.Final/netty-codec-4.1.35.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-codec/4.1.35.Final/netty-codec-4.1.35.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-codec/4.1.35.Final/netty-codec-4.1.35.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: io.netty:netty-common:4.1.35.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-common/4.1.35.Final/netty-common-4.1.35.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-common/4.1.35.Final/netty-common-4.1.35.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-common/4.1.35.Final/netty-common-4.1.35.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: io.netty:netty-handler:4.1.35.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-handler/4.1.35.Final/netty-handler-4.1.35.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-handler/4.1.35.Final/netty-handler-4.1.35.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-handler/4.1.35.Final/netty-handler-4.1.35.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: io.netty:netty-resolver:4.1.35.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-resolver/4.1.35.Final/netty-resolver-4.1.35.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-resolver/4.1.35.Final/netty-resolver-4.1.35.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-resolver/4.1.35.Final/netty-resolver-4.1.35.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: io.netty:netty-transport:4.1.35.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-transport/4.1.35.Final/netty-transport-4.1.35.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-transport/4.1.35.Final/netty-transport-4.1.35.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/netty/netty-transport/4.1.35.Final/netty-transport-4.1.35.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: jakarta.validation:jakarta.validation-api:2.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.apache.commons:commons-lang3:3.8.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.apache.logging.log4j:log4j-api:2.12.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.12.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.apache.mina:mina-core:2.1.3">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/mina/mina-core/2.1.3/mina-core-2.1.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/mina/mina-core/2.1.3/mina-core-2.1.3-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/mina/mina-core/2.1.3/mina-core-2.1.3-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.31">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.31/tomcat-embed-core-9.0.31.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.31/tomcat-embed-core-9.0.31-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-core/9.0.31/tomcat-embed-core-9.0.31-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.31">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.31/tomcat-embed-el-9.0.31.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.31/tomcat-embed-el-9.0.31-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-el/9.0.31/tomcat-embed-el-9.0.31-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.31">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.31/tomcat-embed-websocket-9.0.31.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.31/tomcat-embed-websocket-9.0.31-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.31/tomcat-embed-websocket-9.0.31-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.freemarker:freemarker:2.3.29">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/freemarker/freemarker/2.3.29/freemarker-2.3.29.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/freemarker/freemarker/2.3.29/freemarker-2.3.29-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/freemarker/freemarker/2.3.29/freemarker-2.3.29-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.hibernate.validator:hibernate-validator:6.0.18.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.slf4j:jul-to-slf4j:1.7.30">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.slf4j:slf4j-api:1.7.30">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.5.RELEASE/spring-boot-starter-freemarker-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.5.RELEASE/spring-boot-starter-freemarker-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-freemarker/2.2.5.RELEASE/spring-boot-starter-freemarker-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.5.RELEASE/spring-boot-starter-json-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.5.RELEASE/spring-boot-starter-json-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-json/2.2.5.RELEASE/spring-boot-starter-json-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-starter-logging:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.5.RELEASE/spring-boot-starter-logging-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.5.RELEASE/spring-boot-starter-logging-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-logging/2.2.5.RELEASE/spring-boot-starter-logging-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.5.RELEASE/spring-boot-starter-tomcat-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.5.RELEASE/spring-boot-starter-tomcat-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-tomcat/2.2.5.RELEASE/spring-boot-starter-tomcat-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.5.RELEASE/spring-boot-starter-validation-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.5.RELEASE/spring-boot-starter-validation-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-validation/2.2.5.RELEASE/spring-boot-starter-validation-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-starter-web:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.5.RELEASE/spring-boot-starter-web-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.5.RELEASE/spring-boot-starter-web-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter-web/2.2.5.RELEASE/spring-boot-starter-web-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot-starter:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.5.RELEASE/spring-boot-starter-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.5.RELEASE/spring-boot-starter-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot-starter/2.2.5.RELEASE/spring-boot-starter-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework.boot:spring-boot:2.2.5.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-aop:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.4.RELEASE/spring-aop-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.4.RELEASE/spring-aop-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.4.RELEASE/spring-aop-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-beans:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-context-support:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.4.RELEASE/spring-context-support-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.4.RELEASE/spring-context-support-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context-support/5.2.4.RELEASE/spring-context-support-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-context:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.4.RELEASE/spring-context-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.4.RELEASE/spring-context-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.4.RELEASE/spring-context-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-core:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.4.RELEASE/spring-core-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.4.RELEASE/spring-core-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.4.RELEASE/spring-core-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-expression:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.4.RELEASE/spring-expression-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.4.RELEASE/spring-expression-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.4.RELEASE/spring-expression-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-jcl:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.4.RELEASE/spring-jcl-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.4.RELEASE/spring-jcl-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.4.RELEASE/spring-jcl-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-web:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.4.RELEASE/spring-web-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.4.RELEASE/spring-web-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.4.RELEASE/spring-web-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.springframework:spring-webmvc:5.2.4.RELEASE">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.4.RELEASE/spring-webmvc-5.2.4.RELEASE.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.4.RELEASE/spring-webmvc-5.2.4.RELEASE-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.4.RELEASE/spring-webmvc-5.2.4.RELEASE-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Maven: org.yaml:snakeyaml:1.25">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.25/snakeyaml-1.25-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.25/snakeyaml-1.25-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
</project>
|
@ -1,238 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AnalysisUIOptions">
|
||||
<option name="GROUP_BY_SEVERITY" value="true" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="bed47126-03b3-4370-a6c1-08503492974f" name="Default Changelist" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="DefaultGradleProjectSettings">
|
||||
<option name="isMigrated" value="true" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Class" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectId" id="1PxWQToGq56jcmMz176UjXWLSaU" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="RequestMappingsPanelOrder0" value="0" />
|
||||
<property name="RequestMappingsPanelOrder1" value="1" />
|
||||
<property name="RequestMappingsPanelWidth0" value="75" />
|
||||
<property name="RequestMappingsPanelWidth1" value="75" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="aspect.path.notification.shown" value="true" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
<property name="node.js.detected.package.eslint" value="true" />
|
||||
<property name="node.js.detected.package.tslint" value="true" />
|
||||
<property name="node.js.path.for.package.eslint" value="project" />
|
||||
<property name="node.js.path.for.package.tslint" value="project" />
|
||||
<property name="node.js.selected.package.eslint" value="(autodetect)" />
|
||||
<property name="node.js.selected.package.tslint" value="(autodetect)" />
|
||||
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
|
||||
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
|
||||
<property name="settings.editor.selected.configurable" value="configurable.group.appearance" />
|
||||
<property name="show.migrate.to.gradle.popup" value="false" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="C:\Users\Administrator\Documents\gitee-cim\cim-boot-server\lib" />
|
||||
<recent name="$PROJECT_DIR$/lib" />
|
||||
<recent name="C:\Users\Administrator\Documents\cim\cim-boot-server\cim-boot-server\cim-boot-server" />
|
||||
<recent name="C:\Users\Administrator\Documents\cim\cim-boot-server\cim-boot-server\cim-boot-server\src" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="ServerLauncher" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" temporary="true" nameIsGenerated="true">
|
||||
<module name="cim-boot-server" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="com.farsunset.cim.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="SPRING_BOOT_MAIN_CLASS" value="com.farsunset.cim.ServerLauncher" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Spring Boot.ServerLauncher" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="bed47126-03b3-4370-a6c1-08503492974f" name="Default Changelist" comment="" />
|
||||
<created>1557734989980</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1557734989980</updated>
|
||||
<workItem from="1557734991214" duration="96000" />
|
||||
<workItem from="1557735150926" duration="502000" />
|
||||
<workItem from="1557738240090" duration="289000" />
|
||||
<workItem from="1557740865016" duration="686000" />
|
||||
<workItem from="1558432010952" duration="352000" />
|
||||
<workItem from="1558596242648" duration="1398000" />
|
||||
<workItem from="1559009816995" duration="1293000" />
|
||||
<workItem from="1559110709907" duration="1801000" />
|
||||
<workItem from="1559198165156" duration="1213000" />
|
||||
<workItem from="1559269742769" duration="598000" />
|
||||
<workItem from="1562899329294" duration="2642000" />
|
||||
<workItem from="1563179336728" duration="715000" />
|
||||
<workItem from="1563191907066" duration="17000" />
|
||||
<workItem from="1563245165817" duration="382000" />
|
||||
<workItem from="1566816214034" duration="698000" />
|
||||
<workItem from="1567410257659" duration="115000" />
|
||||
<workItem from="1568597528002" duration="523000" />
|
||||
<workItem from="1579076046499" duration="14355000" />
|
||||
<workItem from="1579226829168" duration="8852000" />
|
||||
<workItem from="1583472574255" duration="526000" />
|
||||
<workItem from="1583473153031" duration="15009000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="2" />
|
||||
</component>
|
||||
<component name="WindowStateProjectService">
|
||||
<state x="609" y="345" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1583567210548">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state x="609" y="345" key="#com.intellij.fileTypes.FileTypeChooser/0.23.1920.1057@0.23.1920.1057" timestamp="1583567210548" />
|
||||
<state x="645" y="287" key="#com.intellij.openapi.updateSettings.impl.PluginUpdateInfoDialog" timestamp="1583472646530">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state x="645" y="287" key="#com.intellij.openapi.updateSettings.impl.PluginUpdateInfoDialog/0.23.1920.1057@0.23.1920.1057" timestamp="1583472646530" />
|
||||
<state x="627" y="209" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog" timestamp="1579149988674">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="627" y="209" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog/0.0.1920.1040@0.0.1920.1040" timestamp="1579149988674" />
|
||||
<state x="690" y="218" key="FileChooserDialogImpl" timestamp="1579230010253">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="690" y="218" key="FileChooserDialogImpl/0.0.1920.1040@0.0.1920.1040" timestamp="1579230010253" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.bottom" timestamp="1583567568644">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.0.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.bottom/0.23.1920.1057@0.23.1920.1057" timestamp="1583567568644" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.center" timestamp="1583567568644">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.0.center/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.center/0.23.1920.1057@0.23.1920.1057" timestamp="1583567568644" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.left" timestamp="1583567568643">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.0.left/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.left/0.23.1920.1057@0.23.1920.1057" timestamp="1583567568643" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.right" timestamp="1583567568644">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.0.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
|
||||
<state width="1870" height="403" key="GridCell.Tab.0.right/0.23.1920.1057@0.23.1920.1057" timestamp="1583567568644" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.bottom" timestamp="1583567566487">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.1.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696751" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.bottom/0.23.1920.1057@0.23.1920.1057" timestamp="1583567566487" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.center" timestamp="1583567566486">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.1.center/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696750" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.center/0.23.1920.1057@0.23.1920.1057" timestamp="1583567566486" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.left" timestamp="1583567566486">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.1.left/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696749" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.left/0.23.1920.1057@0.23.1920.1057" timestamp="1583567566486" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.right" timestamp="1583567566486">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state width="1867" height="615" key="GridCell.Tab.1.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579243696751" />
|
||||
<state width="1870" height="565" key="GridCell.Tab.1.right/0.23.1920.1057@0.23.1920.1057" timestamp="1583567566486" />
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.bottom" timestamp="1579156159339">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1579156159339" />
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.center" timestamp="1579156159339">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.center/0.0.1920.1040@0.0.1920.1040" timestamp="1579156159339" />
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.left" timestamp="1579156159339">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.left/0.0.1920.1040@0.0.1920.1040" timestamp="1579156159339" />
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.right" timestamp="1579156159339">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1867" height="386" key="GridCell.Tab.2.right/0.0.1920.1040@0.0.1920.1040" timestamp="1579156159339" />
|
||||
<state x="574" y="219" width="771" height="670" key="find.popup" timestamp="1583473246286">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state x="574" y="193" width="771" height="654" key="find.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1579242964438" />
|
||||
<state x="574" y="219" width="771" height="670" key="find.popup/0.23.1920.1057@0.23.1920.1057" timestamp="1583473246286" />
|
||||
<state x="161" y="163" key="new project wizard" timestamp="1579242569030">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="161" y="163" key="new project wizard/0.0.1920.1040@0.0.1920.1040" timestamp="1579242569030" />
|
||||
<state x="539" y="28" width="840" height="1051" key="search.everywhere.popup" timestamp="1583567214095">
|
||||
<screen x="0" y="23" width="1920" height="1057" />
|
||||
</state>
|
||||
<state x="539" y="5" width="840" height="1034" key="search.everywhere.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1579235575382" />
|
||||
<state x="539" y="28" width="840" height="1051" key="search.everywhere.popup/0.23.1920.1057@0.23.1920.1057" timestamp="1583567214095" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
<breakpoints>
|
||||
<line-breakpoint enabled="true" type="java-line">
|
||||
<url>jar://$PROJECT_DIR$/lib/cim-server-sdk-mina-3.7.0.jar!/com/farsunset/cim/sdk/server/handler/CIMNioSocketAcceptor.class</url>
|
||||
<line>72</line>
|
||||
<option name="timeStamp" value="5" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="java-line">
|
||||
<url>jar://$PROJECT_DIR$/lib/cim-server-sdk-netty-3.7.0.jar!/com/farsunset/cim/sdk/server/coder/WebMessageEncoder.class</url>
|
||||
<line>22</line>
|
||||
<option name="timeStamp" value="10" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" type="java-line">
|
||||
<url>file://$PROJECT_DIR$/src/main/java/com/farsunset/cim/handler/SessionClosedHandler.java</url>
|
||||
<line>48</line>
|
||||
<option name="timeStamp" value="12" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
</component>
|
||||
<component name="masterDetails">
|
||||
<states>
|
||||
<state key="ProjectJDKs.UI">
|
||||
<settings>
|
||||
<last-edited>1.8</last-edited>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
</states>
|
||||
</component>
|
||||
</project>
|
4
cim-boot-server/init.sql
Normal file
4
cim-boot-server/init.sql
Normal file
@ -0,0 +1,4 @@
|
||||
create database cim;
|
||||
|
||||
#只需要创建库即可,服务器启动会自动创建表,基于application.properties中
|
||||
#spring.jpa.hibernate.ddl-auto = update
|
0
cim-client-sdk/cim-java-sdk/jar.bat → cim-boot-server/jar.bat
Executable file → Normal file
0
cim-client-sdk/cim-java-sdk/jar.bat → cim-boot-server/jar.bat
Executable file → Normal file
Binary file not shown.
Binary file not shown.
@ -6,20 +6,22 @@
|
||||
|
||||
<groupId>com.farsunset</groupId>
|
||||
<artifactId>cim-boot-server</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>4.2.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.5.RELEASE</version>
|
||||
<version>2.5.14</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<netty.version>4.1.35.Final</netty.version>
|
||||
<mina.version>2.1.3</mina.version>
|
||||
<protobuf.version>3.11.1</protobuf.version>
|
||||
<cim.server.sdk.version>4.2.10</cim.server.sdk.version>
|
||||
<protobuf.version>3.25.0</protobuf.version>
|
||||
<mysql.jdbc.version>9.2.0</mysql.jdbc.version>
|
||||
<common.pool.version>2.12.1</common.pool.version>
|
||||
<swagger.version>3.0.0</swagger.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -36,59 +38,49 @@
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-boot-starter</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.farsunset</groupId>
|
||||
<artifactId>cim-server-sdk</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<scope>system</scope>
|
||||
<!-- mina 、netty版本 sdk任选其一 -->
|
||||
<systemPath>${project.basedir}/libs/cim-server-sdk-netty-3.8.0.jar</systemPath>
|
||||
<artifactId>cim-server-sdk-netty</artifactId>
|
||||
<version>${cim.server.sdk.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--- ##################使用mina版本SDK时的配置 start ##################-->
|
||||
<dependency>
|
||||
<groupId>org.apache.mina</groupId>
|
||||
<artifactId>mina-core</artifactId>
|
||||
<version>${mina.version}</version>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
<version>${common.pool.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--- ##################使用mina版本SDK时的配置 end ##################-->
|
||||
|
||||
<!--- ##################使用netty本SDK时的配置 start ##################-->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql.jdbc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-common</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
</dependency>
|
||||
<!--- ##################使用netty本SDK时的配置 end ##################-->
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
@ -97,23 +89,21 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.teaey.apns4j</groupId>
|
||||
<artifactId>apns4j</artifactId>
|
||||
<version>1.1.4</version>
|
||||
<groupId>com.eatthepath</groupId>
|
||||
<artifactId>pushy</artifactId>
|
||||
<version>0.15.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>2.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@ -128,4 +118,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
1
cim-boot-server/run.bat
Normal file
1
cim-boot-server/run.bat
Normal file
@ -0,0 +1 @@
|
||||
java -Dcom.sun.akuma.Daemon=daemonized -Dspring.profiles.active=pro -jar ./cim-boot-server-4.2.0.jar
|
3
cim-boot-server/run.sh
Executable file
3
cim-boot-server/run.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#! /bin/bash
|
||||
|
||||
java -Dcom.sun.akuma.Daemon=daemonized -Dspring.profiles.active=dev -Dserver.port=9090 -jar ./cim-boot-server-4.2.0.jar &
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class BootApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BootApplication.class, args);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ServerLauncher {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServerLauncher.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 用户标注通过 token查到的用户账号,注入到Controller参数里面
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AccessToken {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.farsunset.cim.annotation;
|
||||
|
||||
public @interface CreateAction {
|
||||
}
|
@ -19,16 +19,17 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.server.handler;
|
||||
package com.farsunset.cim.annotation;
|
||||
|
||||
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.sdk.server.model.SentBody;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 心跳handler,主要是让netty重置cheannel的空闲时间
|
||||
* 用户标注通过 token查到的用户账号,注入到Controller参数里面
|
||||
*/
|
||||
public class HeartbeatHandler implements CIMRequestHandler {
|
||||
@Override
|
||||
public void process(CIMSession session, SentBody body) {}
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface UID {
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,18 +19,19 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.server.handler;
|
||||
package com.farsunset.cim.component.event;
|
||||
|
||||
import com.farsunset.cim.model.Message;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.sdk.server.model.SentBody;
|
||||
public class MessageEvent extends ApplicationEvent {
|
||||
public MessageEvent(Message message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public interface CIMRequestHandler {
|
||||
@Override
|
||||
public Message getSource() {
|
||||
return (Message) source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理长连接发送的请求
|
||||
* @param session
|
||||
* @param message
|
||||
*/
|
||||
void process(CIMSession session, SentBody message);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.event;
|
||||
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
public class SessionEvent extends ApplicationEvent {
|
||||
public SessionEvent(Session session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session getSource() {
|
||||
return (Session) source;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.handler;
|
||||
|
||||
import com.farsunset.cim.component.handler.annotation.CIMHandler;
|
||||
import com.farsunset.cim.component.redis.SignalRedisTemplate;
|
||||
import com.farsunset.cim.constant.ChannelAttr;
|
||||
import com.farsunset.cim.constants.Constants;
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import com.farsunset.cim.group.SessionGroup;
|
||||
import com.farsunset.cim.handler.CIMRequestHandler;
|
||||
import com.farsunset.cim.model.ReplyBody;
|
||||
import com.farsunset.cim.model.SentBody;
|
||||
import com.farsunset.cim.service.SessionService;
|
||||
import io.netty.channel.Channel;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 客户长连接 账户绑定实现
|
||||
*/
|
||||
@CIMHandler(key = "client_bind")
|
||||
public class BindHandler implements CIMRequestHandler {
|
||||
|
||||
@Resource
|
||||
private SessionService sessionService;
|
||||
|
||||
@Resource
|
||||
private SessionGroup sessionGroup;
|
||||
|
||||
@Resource
|
||||
private SignalRedisTemplate signalRedisTemplate;
|
||||
|
||||
@Override
|
||||
public void process(Channel channel, SentBody body) {
|
||||
|
||||
if (sessionGroup.isManaged(channel)){
|
||||
return;
|
||||
}
|
||||
|
||||
ReplyBody reply = new ReplyBody();
|
||||
reply.setKey(body.getKey());
|
||||
reply.setCode(HttpStatus.OK.value());
|
||||
reply.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
String uid = body.get("uid");
|
||||
Session session = new Session();
|
||||
session.setUid(uid);
|
||||
session.setNid(channel.attr(ChannelAttr.ID).get());
|
||||
session.setDeviceId(body.get("deviceId"));
|
||||
session.setChannel(body.get("channel"));
|
||||
session.setDeviceName(body.get("deviceName"));
|
||||
session.setAppVersion(body.get("appVersion"));
|
||||
session.setOsVersion(body.get("osVersion"));
|
||||
session.setLanguage(body.get("language"));
|
||||
|
||||
channel.attr(ChannelAttr.UID).set(uid);
|
||||
channel.attr(ChannelAttr.CHANNEL).set(session.getChannel());
|
||||
channel.attr(ChannelAttr.DEVICE_ID).set(session.getDeviceId());
|
||||
channel.attr(ChannelAttr.LANGUAGE).set(session.getLanguage());
|
||||
|
||||
/*
|
||||
*存储到数据库
|
||||
*/
|
||||
sessionService.add(session);
|
||||
|
||||
channel.attr(Constants.SESSION_ID).set(session.getId());
|
||||
|
||||
/*
|
||||
* 添加到内存管理
|
||||
*/
|
||||
sessionGroup.add(channel);
|
||||
|
||||
/*
|
||||
*向客户端发送bind响应
|
||||
*/
|
||||
channel.writeAndFlush(reply);
|
||||
|
||||
/*
|
||||
* 发送上线事件到集群中的其他实例,控制其他设备下线
|
||||
*/
|
||||
signalRedisTemplate.bind(session);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.handler;
|
||||
|
||||
import com.farsunset.cim.component.handler.annotation.CIMHandler;
|
||||
import com.farsunset.cim.constant.ChannelAttr;
|
||||
import com.farsunset.cim.constants.Constants;
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import com.farsunset.cim.group.SessionGroup;
|
||||
import com.farsunset.cim.handler.CIMRequestHandler;
|
||||
import com.farsunset.cim.model.SentBody;
|
||||
import com.farsunset.cim.service.SessionService;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 连接断开时,更新用户相关状态
|
||||
*/
|
||||
@CIMHandler(key = "client_closed")
|
||||
public class ClosedHandler implements CIMRequestHandler {
|
||||
|
||||
@Resource
|
||||
private SessionService sessionService;
|
||||
|
||||
@Override
|
||||
public void process(Channel channel, SentBody message) {
|
||||
|
||||
Long sessionId = channel.attr(Constants.SESSION_ID).get();
|
||||
|
||||
if (sessionId == null){
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ios开启了apns也需要显示在线,因此不删记录
|
||||
*/
|
||||
if (Objects.equals(channel.attr(ChannelAttr.CHANNEL).get(), Session.CHANNEL_IOS)){
|
||||
sessionService.updateState(sessionId, Session.STATE_INACTIVE);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionService.delete(sessionId);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.handler;
|
||||
|
||||
import com.farsunset.cim.component.handler.annotation.CIMHandler;
|
||||
import com.farsunset.cim.component.push.DefaultMessagePusher;
|
||||
import com.farsunset.cim.component.redis.SignalRedisTemplate;
|
||||
import com.farsunset.cim.constant.ChannelAttr;
|
||||
import com.farsunset.cim.constants.Constants;
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import com.farsunset.cim.group.SessionGroup;
|
||||
import com.farsunset.cim.handler.CIMRequestHandler;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import com.farsunset.cim.model.ReplyBody;
|
||||
import com.farsunset.cim.model.SentBody;
|
||||
import com.farsunset.cim.service.SessionService;
|
||||
import io.netty.channel.Channel;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 客户端长连接通道发消息
|
||||
*/
|
||||
@CIMHandler(key = "client_message")
|
||||
public class MessageHandler implements CIMRequestHandler {
|
||||
|
||||
@Resource
|
||||
private DefaultMessagePusher defaultMessagePusher;
|
||||
|
||||
|
||||
@Override
|
||||
public void process(Channel channel, SentBody body) {
|
||||
|
||||
Message message = new Message();
|
||||
/*
|
||||
获取到当前链接的UID
|
||||
*/
|
||||
message.setSender(channel.attr(ChannelAttr.UID).get());
|
||||
message.setReceiver(body.get("receiver"));
|
||||
message.setAction(body.get("action"));
|
||||
message.setContent(body.get("content"));
|
||||
message.setFormat(body.get("format"));
|
||||
message.setTitle(body.get("title"));
|
||||
message.setExtra(body.get("extra"));
|
||||
|
||||
message.setId(System.currentTimeMillis());
|
||||
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
|
||||
/*
|
||||
* 将发送的消息ID 通过Replay异步发送给客户端,
|
||||
* 可通过reqId来对应上多个消息的各自ID
|
||||
*/
|
||||
ReplyBody reply = new ReplyBody();
|
||||
reply.setKey(body.getKey());
|
||||
reply.setCode(HttpStatus.OK.value());
|
||||
reply.put("messageId",String.valueOf(message.getId()));
|
||||
reply.put("requestId",body.get("requestId"));
|
||||
reply.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.handler.annotation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
|
||||
public @interface CIMHandler {
|
||||
String key();
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.farsunset.cim.component.logger;
|
||||
|
||||
import com.farsunset.cim.handler.LoggingHandler;
|
||||
import com.farsunset.cim.model.Ping;
|
||||
import com.farsunset.cim.model.Pong;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 自定义 CIM事件日志打印,重写方法时注意需要有以下2种之一!!!
|
||||
* 1、调用对应的ctx.fireXX(msg)方法;
|
||||
* 2、调用supper
|
||||
*/
|
||||
@ChannelHandler.Sharable
|
||||
@Component
|
||||
public class CIMEventLogger extends LoggingHandler {
|
||||
|
||||
/**
|
||||
* 不打印客户端发送的Pong日志
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
|
||||
if (msg instanceof Pong){
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
super.channelRead(ctx,msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 不打印服务端发送的Ping日志
|
||||
* @param ctx
|
||||
* @param msg
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
|
||||
if (msg instanceof Ping){
|
||||
ctx.write(msg, promise);
|
||||
return;
|
||||
}
|
||||
|
||||
super.write(ctx,msg,promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* 不打长连接空闲事件日志
|
||||
* @param ctx
|
||||
* @param evt
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
ctx.fireUserEventTriggered(evt);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.message;
|
||||
|
||||
import com.farsunset.cim.component.event.SessionEvent;
|
||||
import com.farsunset.cim.constant.ChannelAttr;
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import com.farsunset.cim.group.SessionGroup;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import com.farsunset.cim.util.JSONUtils;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelOutboundInvoker;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* 集群环境下,监控多设备登录情况,控制是否其余终端下线的逻辑
|
||||
*/
|
||||
@Component
|
||||
public class BindMessageListener implements MessageListener {
|
||||
|
||||
private static final String FORCE_OFFLINE_ACTION = "999";
|
||||
|
||||
private static final String SYSTEM_ID = "0";
|
||||
|
||||
/*
|
||||
一个账号只能在同一个类型的终端登录
|
||||
如: 多个android或ios不能同时在线
|
||||
一个android或ios可以和web,桌面同时在线
|
||||
*/
|
||||
private final Map<String,String[]> conflictMap = new HashMap<>();
|
||||
|
||||
/*
|
||||
* web可能同一个终端 打开多 tab页面,可以同时保持连接
|
||||
*/
|
||||
private final Set<String> keepLiveChannels = new HashSet<>();
|
||||
|
||||
@Resource
|
||||
private SessionGroup sessionGroup;
|
||||
|
||||
public BindMessageListener(){
|
||||
conflictMap.put(Session.CHANNEL_ANDROID,new String[]{Session.CHANNEL_ANDROID,Session.CHANNEL_IOS});
|
||||
conflictMap.put(Session.CHANNEL_IOS,new String[]{Session.CHANNEL_ANDROID,Session.CHANNEL_IOS});
|
||||
conflictMap.put(Session.CHANNEL_WINDOWS,new String[]{Session.CHANNEL_WINDOWS,Session.CHANNEL_WEB,Session.CHANNEL_MAC});
|
||||
conflictMap.put(Session.CHANNEL_WEB,new String[]{Session.CHANNEL_WINDOWS,Session.CHANNEL_WEB,Session.CHANNEL_MAC});
|
||||
conflictMap.put(Session.CHANNEL_MAC,new String[]{Session.CHANNEL_WINDOWS,Session.CHANNEL_WEB,Session.CHANNEL_MAC});
|
||||
|
||||
keepLiveChannels.add(Session.CHANNEL_WEB);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onMessage(SessionEvent event) {
|
||||
this.handle(event.getSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(org.springframework.data.redis.connection.Message redisMessage, byte[] bytes) {
|
||||
|
||||
Session session = JSONUtils.fromJson(redisMessage.getBody(), Session.class);
|
||||
|
||||
this.handle(session);
|
||||
}
|
||||
|
||||
private void handle(Session session){
|
||||
|
||||
String uid = session.getUid();
|
||||
|
||||
String[] conflictChannels = conflictMap.get(session.getChannel());
|
||||
|
||||
if (ArrayUtils.isEmpty(conflictChannels)){
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<Channel> channelList = sessionGroup.find(uid,conflictChannels);
|
||||
|
||||
channelList.removeIf(new KeepLivePredicate(session));
|
||||
|
||||
/*
|
||||
* 同设备仅关闭连接,无需通知客户端
|
||||
*/
|
||||
channelList.stream().filter(new SameDevicePredicate(session)).forEach(ChannelOutboundInvoker::close);
|
||||
|
||||
/*
|
||||
* 不同设备关闭连接, 通知客户端账号在其他设备登录
|
||||
*/
|
||||
channelList.stream().filter(new DifferentDevicePredicate(session)).forEach(new BreakOffMessageConsumer(uid,session.getDeviceName()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class BreakOffMessageConsumer implements Consumer<Channel> {
|
||||
|
||||
private final Message message;
|
||||
|
||||
private BreakOffMessageConsumer(String uid,String deviceName) {
|
||||
message = new Message();
|
||||
message.setAction(FORCE_OFFLINE_ACTION);
|
||||
message.setReceiver(uid);
|
||||
message.setSender(SYSTEM_ID);
|
||||
message.setContent(deviceName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Channel channel) {
|
||||
channel.writeAndFlush(message).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
private static class SameDevicePredicate implements Predicate<Channel> {
|
||||
|
||||
private final String deviceId;
|
||||
|
||||
private SameDevicePredicate(Session session) {
|
||||
this.deviceId = session.getDeviceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Channel channel) {
|
||||
return Objects.equals(this.deviceId,channel.attr(ChannelAttr.DEVICE_ID).get());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DifferentDevicePredicate implements Predicate<Channel>{
|
||||
|
||||
private final SameDevicePredicate predicate;
|
||||
|
||||
private DifferentDevicePredicate(Session session) {
|
||||
this.predicate = new SameDevicePredicate(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Channel channel) {
|
||||
return !predicate.test(channel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class KeepLivePredicate implements Predicate<Channel>{
|
||||
private final Session session;
|
||||
|
||||
private KeepLivePredicate(Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Channel ioChannel) {
|
||||
|
||||
if (Objects.equals(session.getNid(),ioChannel.attr(ChannelAttr.ID).get())){
|
||||
return true;
|
||||
}
|
||||
|
||||
String deviceId = ioChannel.attr(ChannelAttr.DEVICE_ID).toString();
|
||||
|
||||
String channel = ioChannel.attr(ChannelAttr.CHANNEL).toString();
|
||||
|
||||
return keepLiveChannels.contains(channel) && Objects.equals(session.getDeviceId(),deviceId);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.message;
|
||||
|
||||
import com.farsunset.cim.component.event.MessageEvent;
|
||||
import com.farsunset.cim.group.SessionGroup;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import com.farsunset.cim.util.JSONUtils;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
/**
|
||||
* 集群环境下,监听redis队列,广播消息到每个实例进行推送
|
||||
* 如果使用MQ的情况也,最好替换为MQ消息队列
|
||||
*/
|
||||
@Component
|
||||
public class PushMessageListener implements MessageListener {
|
||||
|
||||
@Resource
|
||||
private SessionGroup sessionGroup;
|
||||
|
||||
@Override
|
||||
public void onMessage(org.springframework.data.redis.connection.Message redisMessage, byte[] bytes) {
|
||||
|
||||
Message message = JSONUtils.fromJson(redisMessage.getBody(), Message.class);
|
||||
|
||||
this.onMessage(message);
|
||||
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onMessage(MessageEvent event) {
|
||||
this.onMessage(event.getSource());
|
||||
}
|
||||
|
||||
public void onMessage(Message message) {
|
||||
|
||||
String uid = message.getReceiver();
|
||||
|
||||
if (uid == null){
|
||||
return;
|
||||
}
|
||||
|
||||
sessionGroup.write(uid,message);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.farsunset.cim.component.predicate;
|
||||
|
||||
|
||||
import com.farsunset.cim.auth.AuthPredicateInfo;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* WS 鉴权验证
|
||||
*/
|
||||
@Component
|
||||
public class AuthPredicate implements Predicate<AuthPredicateInfo> {
|
||||
|
||||
@Override
|
||||
public boolean test(AuthPredicateInfo auth) {
|
||||
/*
|
||||
可通过header或者uri传递参数
|
||||
String token = auth.getHeader("token");
|
||||
String token = auth.getParameter("token");
|
||||
User user = doAuth(token);
|
||||
auth.getCtx().attr(AttributeKey.valueOf("user_id")).set(user.getId());
|
||||
*/
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.farsunset.cim.component.predicate;
|
||||
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Component
|
||||
public class BlacklistPredicate implements Predicate<String> {
|
||||
|
||||
/**
|
||||
* IP黑名单拦截处理
|
||||
* @param remoteAddress IP地址
|
||||
* @return true:不拦截 false:拦截
|
||||
*/
|
||||
@Override
|
||||
public boolean test(String remoteAddress) {
|
||||
|
||||
/*
|
||||
* 自行根据IP判断是否需要拦截
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,9 +19,10 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.push;
|
||||
package com.farsunset.cim.component.push;
|
||||
|
||||
import com.farsunset.cim.sdk.server.model.Message;
|
||||
|
||||
import com.farsunset.cim.model.Message;
|
||||
|
||||
/*
|
||||
* 消息发送实接口
|
||||
@ -34,6 +35,6 @@ public interface CIMMessagePusher {
|
||||
*
|
||||
* @param msg
|
||||
*/
|
||||
public void push(Message msg);
|
||||
void push(Message msg);
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.push;
|
||||
|
||||
import com.farsunset.cim.component.redis.KeyValueRedisTemplate;
|
||||
import com.farsunset.cim.component.redis.SignalRedisTemplate;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import com.farsunset.cim.service.APNsService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/*
|
||||
* 消息发送实现类
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class DefaultMessagePusher implements CIMMessagePusher {
|
||||
|
||||
@Resource
|
||||
private APNsService apnsService;
|
||||
|
||||
@Resource
|
||||
private SignalRedisTemplate signalRedisTemplate;
|
||||
|
||||
@Resource
|
||||
private KeyValueRedisTemplate keyValueRedisTemplate;
|
||||
|
||||
/**
|
||||
* 向用户发送消息
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public final void push(Message message) {
|
||||
|
||||
String uid = message.getReceiver();
|
||||
|
||||
/*
|
||||
* 说明iOS客户端开启了apns
|
||||
*/
|
||||
String deviceToken = keyValueRedisTemplate.getDeviceToken(uid);
|
||||
if(deviceToken != null) {
|
||||
apnsService.push(message,deviceToken);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 通过发送redis广播,到集群中的每台实例,获得当前UID绑定了连接并推送
|
||||
* @see com.farsunset.cim.component.message.PushMessageListener
|
||||
*/
|
||||
signalRedisTemplate.push(message);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,44 +19,40 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.handler;
|
||||
package com.farsunset.cim.component.redis;
|
||||
|
||||
|
||||
import com.farsunset.cim.sdk.server.constant.CIMConstant;
|
||||
import com.farsunset.cim.sdk.server.handler.CIMRequestHandler;
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.sdk.server.model.SentBody;
|
||||
import com.farsunset.cim.service.CIMSessionService;
|
||||
import com.farsunset.cim.constants.Constants;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/*
|
||||
* 断开连接,清除session
|
||||
*
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class SessionClosedHandler implements CIMRequestHandler {
|
||||
public class KeyValueRedisTemplate extends StringRedisTemplate {
|
||||
|
||||
@Resource
|
||||
private CIMSessionService cimSessionService;
|
||||
|
||||
@Override
|
||||
public void process(CIMSession ios, SentBody message) {
|
||||
Object quietly = ios.getAttribute(CIMConstant.KEY_QUIETLY_CLOSE);
|
||||
if (Objects.equals(quietly, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object account = ios.getAttribute(CIMConstant.KEY_ACCOUNT);
|
||||
if (account == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
cimSessionService.remove(account.toString());
|
||||
public KeyValueRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
super(connectionFactory);
|
||||
}
|
||||
|
||||
public void set(String key ,String value) {
|
||||
super.boundValueOps(key).set(value);
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
return super.boundValueOps(key).get();
|
||||
}
|
||||
|
||||
public String getDeviceToken(String uid){
|
||||
return super.boundValueOps(String.format(Constants.APNS_DEVICE_TOKEN,uid)).get();
|
||||
}
|
||||
|
||||
public void openApns(String uid,String deviceToken){
|
||||
super.boundValueOps(String.format(Constants.APNS_DEVICE_TOKEN,uid)).set(deviceToken);
|
||||
}
|
||||
|
||||
public void closeApns(String uid){
|
||||
super.delete(String.format(Constants.APNS_DEVICE_TOKEN,uid));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.component.redis;
|
||||
|
||||
import com.farsunset.cim.component.event.MessageEvent;
|
||||
import com.farsunset.cim.component.event.SessionEvent;
|
||||
import com.farsunset.cim.constants.Constants;
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import com.farsunset.cim.util.JSONUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
public class SignalRedisTemplate extends StringRedisTemplate {
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String env;
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
public SignalRedisTemplate(LettuceConnectionFactory connectionFactory) {
|
||||
super(connectionFactory);
|
||||
connectionFactory.setValidateConnection(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息发送到 集群中的每个实例,获取对应长连接进行消息写入
|
||||
* @param message
|
||||
*/
|
||||
public void push(Message message) {
|
||||
if (isDev()){
|
||||
applicationContext.publishEvent(new MessageEvent(message));
|
||||
return;
|
||||
}
|
||||
super.convertAndSend(Constants.PUSH_MESSAGE_INNER_QUEUE, JSONUtils.toJSONString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息发送到 集群中的每个实例,解决多终端在线冲突问题
|
||||
* @param session
|
||||
*/
|
||||
public void bind(Session session) {
|
||||
if (isDev()){
|
||||
applicationContext.publishEvent(new SessionEvent(session));
|
||||
return;
|
||||
}
|
||||
super.convertAndSend(Constants.BIND_MESSAGE_INNER_QUEUE, JSONUtils.toJSONString(session));
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地调试环境下不走redis,避免lettuce 经常command timeout。
|
||||
* @return
|
||||
*/
|
||||
private boolean isDev(){
|
||||
return Objects.equals(env,"dev");
|
||||
}
|
||||
|
||||
}
|
@ -19,52 +19,42 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.client.model;
|
||||
package com.farsunset.cim.component.redis;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* java |android 客户端请求结构
|
||||
*
|
||||
*/
|
||||
public class Intent implements Serializable {
|
||||
@Component
|
||||
public class TokenRedisTemplate extends StringRedisTemplate {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TOKEN_CACHE_PREFIX = "TOKEN_%s";
|
||||
|
||||
private String action;
|
||||
|
||||
private final HashMap<String, Object> data = new HashMap<String, Object>();
|
||||
|
||||
public Intent() {
|
||||
public TokenRedisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
super(connectionFactory);
|
||||
}
|
||||
|
||||
public Intent(String action) {
|
||||
this.action = action;
|
||||
public void save(String token, String uid) {
|
||||
|
||||
String key = String.format(TOKEN_CACHE_PREFIX,token);
|
||||
|
||||
super.boundValueOps(key).set(uid);
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
public String get(String token) {
|
||||
|
||||
String key = String.format(TOKEN_CACHE_PREFIX,token);
|
||||
|
||||
return super.boundValueOps(key).get();
|
||||
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
public void remove(String token) {
|
||||
|
||||
String key = String.format(TOKEN_CACHE_PREFIX,token);
|
||||
|
||||
super.delete(key);
|
||||
}
|
||||
|
||||
public void putExtra(String key, Object value) {
|
||||
data.put(key, value);
|
||||
}
|
||||
|
||||
public Object getExtra(String key) {
|
||||
return data.get(key);
|
||||
}
|
||||
|
||||
public long getLongExtra(String key, long defValue) {
|
||||
Object v = getExtra(key);
|
||||
try {
|
||||
return Long.parseLong(v.toString());
|
||||
} catch (Exception e) {
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,46 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.config;
|
||||
|
||||
import com.farsunset.cim.handler.BindHandler;
|
||||
import com.farsunset.cim.handler.SessionClosedHandler;
|
||||
import com.farsunset.cim.sdk.server.handler.CIMNioSocketAcceptor;
|
||||
import com.farsunset.cim.sdk.server.handler.CIMRequestHandler;
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.sdk.server.model.SentBody;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import com.farsunset.cim.acceptor.AppSocketAcceptor;
|
||||
import com.farsunset.cim.acceptor.WebsocketAcceptor;
|
||||
import com.farsunset.cim.acceptor.config.SocketConfig;
|
||||
import com.farsunset.cim.acceptor.config.WebsocketConfig;
|
||||
import com.farsunset.cim.component.handler.annotation.CIMHandler;
|
||||
import com.farsunset.cim.component.logger.CIMEventLogger;
|
||||
import com.farsunset.cim.component.predicate.AuthPredicate;
|
||||
import com.farsunset.cim.component.predicate.BlacklistPredicate;
|
||||
import com.farsunset.cim.config.properties.APNsProperties;
|
||||
import com.farsunset.cim.config.properties.CIMAppSocketProperties;
|
||||
import com.farsunset.cim.config.properties.CIMWebsocketProperties;
|
||||
import com.farsunset.cim.group.SessionGroup;
|
||||
import com.farsunset.cim.group.TagSessionGroup;
|
||||
import com.farsunset.cim.handler.CIMRequestHandler;
|
||||
import com.farsunset.cim.model.SentBody;
|
||||
import com.farsunset.cim.service.SessionService;
|
||||
import io.netty.channel.Channel;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -15,36 +48,79 @@ import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@EnableConfigurationProperties({
|
||||
APNsProperties.class,
|
||||
CIMWebsocketProperties.class,
|
||||
CIMAppSocketProperties.class})
|
||||
@Configuration
|
||||
public class CIMConfig implements CIMRequestHandler, ApplicationListener<ApplicationStartedEvent> {
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private final HashMap<String,CIMRequestHandler> appHandlerMap = new HashMap<>();
|
||||
@Resource
|
||||
private SessionService sessionService;
|
||||
|
||||
private final HashMap<String,CIMRequestHandler> handlerMap = new HashMap<>();
|
||||
|
||||
@Bean
|
||||
public SessionGroup sessionGroup() {
|
||||
return new SessionGroup();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TagSessionGroup tagSessionGroup() {
|
||||
return new TagSessionGroup();
|
||||
}
|
||||
|
||||
|
||||
@Bean(destroyMethod = "destroy")
|
||||
public CIMNioSocketAcceptor getNioSocketAcceptor(@Value("${cim.app.port}") int port,
|
||||
@Value("${cim.websocket.port}") int websocketPort) {
|
||||
@Bean(destroyMethod = "destroy",initMethod = "bind")
|
||||
@ConditionalOnProperty(name = {"cim.websocket.enable"},matchIfMissing = true)
|
||||
public WebsocketAcceptor websocketAcceptor(CIMWebsocketProperties properties, AuthPredicate authPredicate, BlacklistPredicate blacklistPredicate, CIMEventLogger cimEventLogger) {
|
||||
WebsocketConfig config = new WebsocketConfig();
|
||||
config.setBlacklistPredicate(blacklistPredicate);
|
||||
config.setAuthPredicate(authPredicate);
|
||||
config.setPath(properties.getPath());
|
||||
config.setPort(properties.getPort());
|
||||
config.setProtocol(properties.getProtocol());
|
||||
config.setOuterRequestHandler(this);
|
||||
config.setEnable(properties.isEnable());
|
||||
|
||||
return new CIMNioSocketAcceptor.Builder()
|
||||
.setAppPort(port)
|
||||
.setWebsocketPort(websocketPort)
|
||||
.setOuterRequestHandler(this)
|
||||
.build();
|
||||
config.setWriteIdle(properties.getWriteIdle());
|
||||
config.setReadIdle(properties.getReadIdle());
|
||||
config.setMaxPongTimeout(properties.getMaxPongTimeout());
|
||||
config.setLoggingHandler(cimEventLogger);
|
||||
return new WebsocketAcceptor(config);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "destroy",initMethod = "bind")
|
||||
@ConditionalOnProperty(name = {"cim.app.enable"},matchIfMissing = true)
|
||||
public AppSocketAcceptor appSocketAcceptor(CIMAppSocketProperties properties,BlacklistPredicate blacklistPredicate, CIMEventLogger cimEventLogger) {
|
||||
|
||||
SocketConfig config = new SocketConfig();
|
||||
config.setPort(properties.getPort());
|
||||
config.setOuterRequestHandler(this);
|
||||
config.setEnable(properties.isEnable());
|
||||
|
||||
config.setWriteIdle(properties.getWriteIdle());
|
||||
config.setReadIdle(properties.getReadIdle());
|
||||
config.setMaxPongTimeout(properties.getMaxPongTimeout());
|
||||
config.setLoggingHandler(cimEventLogger);
|
||||
config.setBlacklistPredicate(blacklistPredicate);
|
||||
|
||||
return new AppSocketAcceptor(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(CIMSession session, SentBody body) {
|
||||
public void process(Channel channel, SentBody body) {
|
||||
|
||||
CIMRequestHandler handler = appHandlerMap.get(body.getKey());
|
||||
CIMRequestHandler handler = handlerMap.get(body.getKey());
|
||||
|
||||
if(handler == null) {return ;}
|
||||
|
||||
handler.process(session, body);
|
||||
handler.process(channel, body);
|
||||
|
||||
}
|
||||
/*
|
||||
@ -53,9 +129,19 @@ public class CIMConfig implements CIMRequestHandler, ApplicationListener<Applica
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
|
||||
|
||||
appHandlerMap.put("client_bind",applicationContext.getBean(BindHandler.class));
|
||||
appHandlerMap.put("client_closed",applicationContext.getBean(SessionClosedHandler.class));
|
||||
Map<String, CIMRequestHandler> beans = applicationContext.getBeansOfType(CIMRequestHandler.class);
|
||||
|
||||
applicationContext.getBean(CIMNioSocketAcceptor.class).bind();
|
||||
for (Map.Entry<String, CIMRequestHandler> entry : beans.entrySet()) {
|
||||
|
||||
CIMRequestHandler handler = entry.getValue();
|
||||
|
||||
CIMHandler annotation = handler.getClass().getAnnotation(CIMHandler.class);
|
||||
|
||||
if (annotation != null){
|
||||
handlerMap.put(annotation.key(),handler);
|
||||
}
|
||||
}
|
||||
|
||||
sessionService.deleteLocalhost();
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.config;
|
||||
|
||||
import com.farsunset.cim.mvc.resolver.TokenArgumentResolver;
|
||||
import com.farsunset.cim.mvc.resolver.UidArgumentResolver;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class MvcConfig implements WebMvcConfigurer{
|
||||
|
||||
@Resource
|
||||
private HandlerInterceptor tokenInterceptor;
|
||||
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
|
||||
registry.addInterceptor(tokenInterceptor)
|
||||
.addPathPatterns("/webrtc/**");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
argumentResolvers.add(new UidArgumentResolver());
|
||||
argumentResolvers.add(new TokenArgumentResolver());
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.addAllowedOriginPattern("*");
|
||||
configuration.addAllowedHeader("*");
|
||||
configuration.addAllowedMethod("*");
|
||||
configuration.setAllowCredentials(true);
|
||||
source.registerCorsConfiguration("/**", configuration);
|
||||
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.config;
|
||||
|
||||
import com.farsunset.cim.constants.Constants;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.listener.ChannelTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
@Configuration
|
||||
@AutoConfigureAfter(RedisAutoConfiguration.class)
|
||||
public class RedisConfig {
|
||||
|
||||
@Autowired
|
||||
public RedisConfig(LettuceConnectionFactory connectionFactory, @Value("${spring.profiles.active}") String profile){
|
||||
if (Objects.equals("dev",profile)){
|
||||
connectionFactory.setValidateConnection(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
|
||||
MessageListener pushMessageListener,
|
||||
MessageListener bindMessageListener){
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
container.addMessageListener(pushMessageListener,new ChannelTopic(Constants.PUSH_MESSAGE_INNER_QUEUE));
|
||||
container.addMessageListener(bindMessageListener,new ChannelTopic(Constants.BIND_MESSAGE_INNER_QUEUE));
|
||||
return container;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.oas.annotations.EnableOpenApi;
|
||||
import springfox.documentation.service.*;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@EnableOpenApi
|
||||
@Configuration
|
||||
public class SwaggerConfig {
|
||||
|
||||
@Bean
|
||||
public Docket messageApiDocket() {
|
||||
return new Docket(DocumentationType.OAS_30)
|
||||
.apiInfo(new ApiInfoBuilder()
|
||||
.title("CIM Push Service APIs.")
|
||||
.description("消息发送相关接口")
|
||||
.version("3.0")
|
||||
.build())
|
||||
.groupName("1、消息相关接口")
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("com.farsunset.cim.mvc.controller.message"))
|
||||
.build()
|
||||
.securitySchemes(securitySchemes())
|
||||
.securityContexts(securityContexts());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Docket webrtcApiDocket() {
|
||||
return new Docket(DocumentationType.OAS_30)
|
||||
.apiInfo(new ApiInfoBuilder()
|
||||
.title("CIM Push Service APIs.")
|
||||
.description("可用于webrtc通话信令接口")
|
||||
.version("3.0")
|
||||
.build())
|
||||
.groupName("2、Webrtc相关接口")
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage("com.farsunset.cim.mvc.controller.webrtc"))
|
||||
.build()
|
||||
.securitySchemes(securitySchemes())
|
||||
.securityContexts(securityContexts());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<SecurityScheme> securitySchemes() {
|
||||
List<SecurityScheme> schemeList = new ArrayList<>();
|
||||
schemeList.add(new ApiKey("access-token", "access-token", "header"));
|
||||
return schemeList;
|
||||
}
|
||||
|
||||
private List<SecurityContext> securityContexts() {
|
||||
List<SecurityContext> securityContextList = new ArrayList<>();
|
||||
List<SecurityReference> securityReferenceList = new ArrayList<>();
|
||||
securityReferenceList.add(new SecurityReference("access-token", new AuthorizationScope[]{new AuthorizationScope("global", "accessAnything")}));
|
||||
securityContextList.add(SecurityContext
|
||||
.builder()
|
||||
.securityReferences(securityReferenceList)
|
||||
.operationSelector(operationContext -> true)
|
||||
.build()
|
||||
);
|
||||
return securityContextList;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.config.properties;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "cim.apns")
|
||||
public class APNsProperties {
|
||||
|
||||
private boolean debug;
|
||||
|
||||
private String appId;
|
||||
|
||||
private final P12 p12 = new P12();
|
||||
|
||||
public static class P12 {
|
||||
private String file;
|
||||
private String password;
|
||||
|
||||
public void setFile(String file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
|
||||
public P12 getP12() {
|
||||
return p12;
|
||||
}
|
||||
|
||||
public boolean isDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
public String getP12File() {
|
||||
return p12.file;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public String getP12Password() {
|
||||
return p12.password;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.config.properties;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
|
||||
@ConfigurationProperties(prefix = "cim.app")
|
||||
public class CIMAppSocketProperties {
|
||||
|
||||
private boolean enable;
|
||||
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
长链接写空闲时间触发时间(s)
|
||||
心跳发送定时,每当x秒无数据下发写入,触发 服务端-->客户端 心跳事件
|
||||
*/
|
||||
private Duration writeIdle = Duration.ofSeconds(45);
|
||||
|
||||
/**
|
||||
长链接读空闲时间触发时间(s)
|
||||
心跳响应定时,每当readIdle - writeIdle秒无数据接收,触发心跳超时计数
|
||||
*/
|
||||
private Duration readIdle = Duration.ofSeconds(60);
|
||||
|
||||
|
||||
/**
|
||||
长链接最大允许心跳响应超时次数
|
||||
达到该次数则 服务端断开链接
|
||||
*/
|
||||
private int maxPongTimeout = 1;
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public Duration getWriteIdle() {
|
||||
return writeIdle;
|
||||
}
|
||||
|
||||
public void setWriteIdle(Duration writeIdle) {
|
||||
if (writeIdle == null || writeIdle.getSeconds() <= 0){
|
||||
return;
|
||||
}
|
||||
this.writeIdle = writeIdle;
|
||||
}
|
||||
|
||||
public Duration getReadIdle() {
|
||||
return readIdle;
|
||||
}
|
||||
|
||||
public void setReadIdle(Duration readIdle) {
|
||||
if (readIdle == null || readIdle.getSeconds() <= 0){
|
||||
return;
|
||||
}
|
||||
this.readIdle = readIdle;
|
||||
}
|
||||
|
||||
public int getMaxPongTimeout() {
|
||||
return maxPongTimeout;
|
||||
}
|
||||
|
||||
public void setMaxPongTimeout(int maxPongTimeout) {
|
||||
if (maxPongTimeout <= 0){
|
||||
return;
|
||||
}
|
||||
this.maxPongTimeout = maxPongTimeout;
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.config.properties;
|
||||
|
||||
import com.farsunset.cim.constant.WebsocketProtocol;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
|
||||
@ConfigurationProperties(prefix = "cim.websocket")
|
||||
public class CIMWebsocketProperties {
|
||||
|
||||
private boolean enable;
|
||||
|
||||
private Integer port;
|
||||
private String path;
|
||||
private WebsocketProtocol protocol;
|
||||
|
||||
/**
|
||||
长链接写空闲时间触发时间(s)
|
||||
心跳发送定时,每当x秒无数据下发写入,触发 服务端-->客户端 心跳事件
|
||||
*/
|
||||
private Duration writeIdle = Duration.ofSeconds(45);
|
||||
|
||||
/**
|
||||
长链接读空闲时间触发时间(s)
|
||||
心跳响应定时,每当readIdle - writeIdle秒无数据接收,触发心跳超时计数
|
||||
*/
|
||||
private Duration readIdle = Duration.ofSeconds(60);
|
||||
|
||||
|
||||
/**
|
||||
长链接最大允许心跳响应超时次数
|
||||
达到该次数则 服务端断开链接
|
||||
*/
|
||||
private int maxPongTimeout = 1;
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(Integer port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public WebsocketProtocol getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(WebsocketProtocol protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public Duration getWriteIdle() {
|
||||
return writeIdle;
|
||||
}
|
||||
|
||||
public void setWriteIdle(Duration writeIdle) {
|
||||
if (writeIdle == null || writeIdle.getSeconds() <= 0){
|
||||
return;
|
||||
}
|
||||
this.writeIdle = writeIdle;
|
||||
}
|
||||
|
||||
public Duration getReadIdle() {
|
||||
return readIdle;
|
||||
}
|
||||
|
||||
public void setReadIdle(Duration readIdle) {
|
||||
if (readIdle == null || readIdle.getSeconds() <= 0){
|
||||
return;
|
||||
}
|
||||
this.readIdle = readIdle;
|
||||
}
|
||||
|
||||
public int getMaxPongTimeout() {
|
||||
return maxPongTimeout;
|
||||
}
|
||||
|
||||
public void setMaxPongTimeout(int maxPongTimeout) {
|
||||
if (maxPongTimeout <= 0){
|
||||
return;
|
||||
}
|
||||
this.maxPongTimeout = maxPongTimeout;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.constants;
|
||||
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
public interface Constants {
|
||||
|
||||
String PUSH_MESSAGE_INNER_QUEUE = "signal/channel/PUSH_MESSAGE_INNER_QUEUE";
|
||||
|
||||
String BIND_MESSAGE_INNER_QUEUE = "signal/channel/BIND_MESSAGE_INNER_QUEUE";
|
||||
|
||||
String APNS_DEVICE_TOKEN = "APNS_OPEN_%s";
|
||||
|
||||
AttributeKey<Long> SESSION_ID = AttributeKey.valueOf("session_id");
|
||||
|
||||
}
|
@ -19,44 +19,58 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.client.model;
|
||||
package com.farsunset.cim.constants;
|
||||
|
||||
import java.io.Serializable;
|
||||
public interface MessageAction {
|
||||
|
||||
import com.farsunset.cim.sdk.client.constant.CIMConstant;
|
||||
/*
|
||||
* 系统定制消息---语音通话请求
|
||||
*/
|
||||
String ACTION_900 = "900";
|
||||
|
||||
/**
|
||||
* 客户端心跳响应
|
||||
*/
|
||||
public class HeartbeatResponse implements Serializable, Protobufable {
|
||||
/*
|
||||
* 系统定制消息---视频通话请求
|
||||
*/
|
||||
String ACTION_901 = "901";
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE";
|
||||
private static final String CMD_HEARTBEAT_RESPONSE = "CR";
|
||||
/*
|
||||
* 系统定制消息---通话接受
|
||||
*/
|
||||
String ACTION_902 = "902";
|
||||
|
||||
private static final HeartbeatResponse object = new HeartbeatResponse();
|
||||
/*
|
||||
* 系统定制消息---通话拒绝
|
||||
*/
|
||||
String ACTION_903 = "903";
|
||||
|
||||
private HeartbeatResponse() {
|
||||
/*
|
||||
* 系统定制消息---对方正忙
|
||||
*/
|
||||
String ACTION_904 = "904";
|
||||
|
||||
}
|
||||
/*
|
||||
* 系统定制消息---对方挂断
|
||||
*/
|
||||
String ACTION_905 = "905";
|
||||
|
||||
public static HeartbeatResponse getInstance() {
|
||||
return object;
|
||||
}
|
||||
/*
|
||||
* 系统定制消息---取消呼叫
|
||||
*/
|
||||
String ACTION_906 = "906";
|
||||
|
||||
@Override
|
||||
public byte[] getByteArray() {
|
||||
return CMD_HEARTBEAT_RESPONSE.getBytes();
|
||||
}
|
||||
/*
|
||||
* 系统定制消息---同步ICE SPD
|
||||
*/
|
||||
String ACTION_907 = "907";
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TAG;
|
||||
}
|
||||
/*
|
||||
* 系统定制消息---同步Offer
|
||||
*/
|
||||
String ACTION_908 = "908";
|
||||
|
||||
@Override
|
||||
public byte getType() {
|
||||
return CIMConstant.ProtobufType.C_H_RS;
|
||||
}
|
||||
/*
|
||||
* 系统定制消息---同步Answer
|
||||
*/
|
||||
String ACTION_909 = "909";
|
||||
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.entity;
|
||||
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "t_cim_session")
|
||||
public class Session{
|
||||
|
||||
public static final int STATE_ACTIVE = 0;
|
||||
public static final int STATE_APNS = 1;
|
||||
public static final int STATE_INACTIVE = 2;
|
||||
|
||||
public static final String CHANNEL_IOS = "ios";
|
||||
public static final String CHANNEL_ANDROID = "android";
|
||||
public static final String CHANNEL_WINDOWS = "windows";
|
||||
public static final String CHANNEL_MAC = "mac";
|
||||
public static final String CHANNEL_WEB = "web";
|
||||
|
||||
/**
|
||||
* 数据库主键ID
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* session绑定的用户账号
|
||||
*/
|
||||
@Column(name = "uid")
|
||||
private String uid;
|
||||
|
||||
/**
|
||||
* session在本台服务器上的ID
|
||||
*/
|
||||
@Column(name = "nid",length = 32,nullable = false)
|
||||
private String nid;
|
||||
|
||||
/**
|
||||
* 客户端ID (设备号码+应用包名),ios为deviceToken
|
||||
*/
|
||||
|
||||
@Column(name = "device_id",length = 64,nullable = false)
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 终端设备型号
|
||||
*/
|
||||
@Column(name = "device_name")
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* session绑定的服务器IP
|
||||
*/
|
||||
@Column(name = "host",length = 15,nullable = false)
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* 终端设备类型
|
||||
*/
|
||||
@Column(name = "channel",length = 16,nullable = false)
|
||||
private String channel;
|
||||
|
||||
/**
|
||||
* 终端应用版本
|
||||
*/
|
||||
@Column(name = "app_version")
|
||||
private String appVersion;
|
||||
|
||||
/**
|
||||
* 终端系统版本
|
||||
*/
|
||||
@Column(name = "os_version")
|
||||
private String osVersion;
|
||||
|
||||
/**
|
||||
* 终端语言
|
||||
*/
|
||||
@Column(name = "language")
|
||||
private String language;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
@Column(name = "bind_time")
|
||||
private Long bindTime;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private int state;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(String uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public String getNid() {
|
||||
return nid;
|
||||
}
|
||||
|
||||
public void setNid(String nid) {
|
||||
this.nid = nid;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public String getAppVersion() {
|
||||
return appVersion;
|
||||
}
|
||||
|
||||
public void setAppVersion(String appVersion) {
|
||||
this.appVersion = appVersion;
|
||||
}
|
||||
|
||||
public String getOsVersion() {
|
||||
return osVersion;
|
||||
}
|
||||
|
||||
public void setOsVersion(String osVersion) {
|
||||
this.osVersion = osVersion;
|
||||
}
|
||||
|
||||
public Long getBindTime() {
|
||||
return bindTime;
|
||||
}
|
||||
|
||||
public void setBindTime(Long bindTime) {
|
||||
this.bindTime = bindTime;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.handler;
|
||||
|
||||
import com.farsunset.cim.push.CIMMessagePusher;
|
||||
import com.farsunset.cim.sdk.server.constant.CIMConstant;
|
||||
import com.farsunset.cim.sdk.server.handler.CIMRequestHandler;
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.sdk.server.model.Message;
|
||||
import com.farsunset.cim.sdk.server.model.ReplyBody;
|
||||
import com.farsunset.cim.sdk.server.model.SentBody;
|
||||
import com.farsunset.cim.service.CIMSessionService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
/*
|
||||
* 账号绑定实现
|
||||
*
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class BindHandler implements CIMRequestHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BindHandler.class);
|
||||
|
||||
@Resource
|
||||
private CIMSessionService cimSessionService;
|
||||
|
||||
@Value("${server.host}")
|
||||
private String host;
|
||||
|
||||
@Resource
|
||||
private CIMMessagePusher defaultMessagePusher;
|
||||
|
||||
@Override
|
||||
public void process(CIMSession newSession, SentBody body) {
|
||||
|
||||
ReplyBody reply = new ReplyBody();
|
||||
reply.setKey(body.getKey());
|
||||
reply.setCode(HttpStatus.OK.value());
|
||||
reply.setTimestamp(System.currentTimeMillis());
|
||||
|
||||
try {
|
||||
|
||||
String account = body.get("account");
|
||||
newSession.setAccount(account);
|
||||
newSession.setDeviceId(body.get("deviceId"));
|
||||
newSession.setHost(host);
|
||||
newSession.setChannel(body.get("channel"));
|
||||
newSession.setDeviceModel(body.get("device"));
|
||||
newSession.setClientVersion(body.get("appVersion"));
|
||||
newSession.setSystemVersion(body.get("osVersion"));
|
||||
newSession.setBindTime(System.currentTimeMillis());
|
||||
/*
|
||||
* 由于客户端断线服务端可能会无法获知的情况,客户端重连时,需要关闭旧的连接
|
||||
*/
|
||||
CIMSession oldSession = cimSessionService.get(account);
|
||||
|
||||
/*
|
||||
* 如果是账号已经在另一台终端登录。则让另一个终端下线
|
||||
*/
|
||||
|
||||
if (oldSession != null && fromOtherDevice(newSession,oldSession) && oldSession.isConnected()) {
|
||||
sendForceOfflineMessage(oldSession, account, newSession.getDeviceModel());
|
||||
}
|
||||
|
||||
/*
|
||||
* 有可能是同一个设备重复连接,则关闭旧的链接,这种情况一般是客户端断网,联网又重新链接上来,之前的旧链接没有来得及通过心跳机制关闭,在这里手动关闭
|
||||
* 条件1,连接来自是同一个设备
|
||||
* 条件2.2个连接都是同一台服务器
|
||||
*/
|
||||
|
||||
if (oldSession != null && !fromOtherDevice(newSession,oldSession) && Objects.equals(oldSession.getHost(),host)) {
|
||||
closeQuietly(oldSession);
|
||||
}
|
||||
|
||||
cimSessionService.save(newSession);
|
||||
|
||||
|
||||
} catch (Exception exception) {
|
||||
reply.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
logger.error("Bind has error",exception);
|
||||
}
|
||||
|
||||
newSession.write(reply);
|
||||
}
|
||||
|
||||
private boolean fromOtherDevice(CIMSession oldSession ,CIMSession newSession) {
|
||||
|
||||
return !Objects.equals(oldSession.getDeviceId(), newSession.getDeviceId());
|
||||
}
|
||||
|
||||
private void sendForceOfflineMessage(CIMSession oldSession, String account, String deviceModel) {
|
||||
|
||||
Message msg = new Message();
|
||||
msg.setAction(CIMConstant.MessageAction.ACTION_OFFLINE);
|
||||
msg.setReceiver(account);
|
||||
msg.setSender("system");
|
||||
msg.setContent(deviceModel);
|
||||
msg.setId(System.currentTimeMillis());
|
||||
|
||||
defaultMessagePusher.push(msg);
|
||||
|
||||
closeQuietly(oldSession);
|
||||
|
||||
}
|
||||
|
||||
private void closeQuietly(CIMSession oldSession) {
|
||||
if (oldSession.isConnected() && Objects.equals(host, oldSession.getHost())) {
|
||||
oldSession.setAttribute(CIMConstant.KEY_QUIETLY_CLOSE,true);
|
||||
oldSession.closeOnFlush();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,15 +19,15 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.admin.controller;
|
||||
package com.farsunset.cim.mvc.controller.admin;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
@Controller
|
||||
@Api(produces = "application/json", tags = "页面导航",hidden = true)
|
||||
public class NavigationController {
|
||||
|
||||
@GetMapping(value = "/")
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,9 +19,10 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.admin.controller;
|
||||
package com.farsunset.cim.mvc.controller.admin;
|
||||
|
||||
import com.farsunset.cim.service.CIMSessionService;
|
||||
import com.farsunset.cim.service.SessionService;
|
||||
import io.swagger.annotations.Api;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -30,15 +31,18 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Controller
|
||||
@Api(produces = "application/json", tags = "在线用户页面",hidden = true)
|
||||
@RequestMapping("/console/session")
|
||||
public class SessionController {
|
||||
|
||||
@Resource
|
||||
private CIMSessionService cimSessionService;
|
||||
|
||||
private SessionService sessionService;
|
||||
|
||||
@GetMapping(value = "/list")
|
||||
public String list(Model model) {
|
||||
model.addAttribute("sessionList", cimSessionService.list());
|
||||
model.addAttribute("sessionList", sessionService.findAll());
|
||||
return "console/session/manage";
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.mvc.controller.message;
|
||||
|
||||
import com.farsunset.cim.service.SessionService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/apns")
|
||||
@Api(produces = "application/json", tags = "APNs推送相关")
|
||||
public class APNsController {
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "开启apns")
|
||||
|
||||
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "deviceToken", value = "APNs的deviceToken", paramType = "query", dataTypeClass = String.class, required = true, example = ""),
|
||||
@ApiImplicitParam(name = "uid", value = "用户ID", paramType = "query", dataTypeClass = String.class,example = "0")
|
||||
})
|
||||
@PostMapping(value = "/open")
|
||||
public ResponseEntity<Void> open(@RequestParam String uid , @RequestParam String deviceToken) {
|
||||
|
||||
sessionService.openApns(uid,deviceToken);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@Resource
|
||||
private SessionService sessionService;
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "关闭apns")
|
||||
@ApiImplicitParam(name = "uid", value = "用户ID", paramType = "query", dataTypeClass = String.class,example = "0")
|
||||
@PostMapping(value = "/close")
|
||||
public ResponseEntity<Void> close(@RequestParam String uid) {
|
||||
|
||||
sessionService.closeApns(uid);
|
||||
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.mvc.controller.message;
|
||||
|
||||
import com.farsunset.cim.component.push.DefaultMessagePusher;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/message")
|
||||
@Api(produces = "application/json", tags = "消息相关接口" )
|
||||
public class MessageController {
|
||||
|
||||
@Resource
|
||||
private DefaultMessagePusher defaultMessagePusher;
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "发送消息")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "sender", value = "发送者UID", paramType = "query", dataTypeClass = String.class, required = true, example = ""),
|
||||
@ApiImplicitParam(name = "receiver", value = "接收者UID", paramType = "query", dataTypeClass = String.class, required = true, example = ""),
|
||||
@ApiImplicitParam(name = "action", value = "消息动作", paramType = "query", dataTypeClass = String.class, required = true, example = ""),
|
||||
@ApiImplicitParam(name = "title", value = "消息标题", paramType = "query", dataTypeClass = String.class, example = ""),
|
||||
@ApiImplicitParam(name = "content", value = "消息内容", paramType = "query", dataTypeClass = String.class, example = ""),
|
||||
@ApiImplicitParam(name = "format", value = "消息格式", paramType = "query", dataTypeClass = String.class, example = ""),
|
||||
@ApiImplicitParam(name = "extra", value = "扩展字段", paramType = "query", dataTypeClass = String.class, example = ""),
|
||||
})
|
||||
@PostMapping(value = "/send")
|
||||
public ResponseEntity<Long> send(@RequestParam String sender ,
|
||||
@RequestParam String receiver ,
|
||||
@RequestParam String action ,
|
||||
@RequestParam(required = false) String title ,
|
||||
@RequestParam(required = false) String content ,
|
||||
@RequestParam(required = false) String format ,
|
||||
@RequestParam(required = false) String extra) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setSender(sender);
|
||||
message.setReceiver(receiver);
|
||||
message.setAction(action);
|
||||
message.setContent(content);
|
||||
message.setFormat(format);
|
||||
message.setTitle(title);
|
||||
message.setExtra(extra);
|
||||
|
||||
message.setId(System.currentTimeMillis());
|
||||
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.ok(message.getId());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.mvc.controller.webrtc;
|
||||
|
||||
|
||||
import com.farsunset.cim.annotation.AccessToken;
|
||||
import com.farsunset.cim.mvc.response.ResponseEntity;
|
||||
import com.farsunset.cim.service.AccessTokenService;
|
||||
import io.swagger.annotations.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
@Api(produces = "application/json", tags = "用户登录接口" )
|
||||
@Validated
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private AccessTokenService accessTokenService;
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "模拟登录")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "telephone", value = "手机号码", paramType = "query", dataTypeClass = String.class),
|
||||
@ApiImplicitParam(name = "password", value = "密码", paramType = "query", dataTypeClass = String.class),
|
||||
})
|
||||
@PostMapping(value = "/login")
|
||||
public ResponseEntity<?> login(@RequestParam String telephone) {
|
||||
|
||||
|
||||
Map<String,Object> body = new HashMap<>();
|
||||
body.put("id",Long.parseLong(telephone));
|
||||
body.put("name","测试用户");
|
||||
body.put("telephone","telephone");
|
||||
|
||||
ResponseEntity<Map<String,Object>> result = new ResponseEntity<>();
|
||||
|
||||
result.setData(body);
|
||||
|
||||
result.setToken(accessTokenService.generate(telephone));
|
||||
result.setTimestamp(System.currentTimeMillis());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation(httpMethod = "GET", value = "退出登录")
|
||||
@GetMapping(value = "/logout")
|
||||
public ResponseEntity<Void> logout(@ApiParam(hidden = true) @AccessToken String token) {
|
||||
accessTokenService.delete(token);
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.mvc.controller.webrtc;
|
||||
|
||||
import com.farsunset.cim.annotation.UID;
|
||||
import com.farsunset.cim.component.push.DefaultMessagePusher;
|
||||
import com.farsunset.cim.constants.MessageAction;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import com.farsunset.cim.mvc.request.WebrtcRequest;
|
||||
import com.farsunset.cim.mvc.response.ResponseEntity;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/webrtc")
|
||||
@Api(produces = "application/json", tags = "单人通话信令推送接口" )
|
||||
public class WebrtcController {
|
||||
|
||||
@Resource
|
||||
private DefaultMessagePusher defaultMessagePusher;
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "发起单人语音通话")
|
||||
@ApiImplicitParam(name = "targetId", value = "对方用户ID", paramType = "query", dataTypeClass = Long.class)
|
||||
@PostMapping(value = {"/voice"})
|
||||
public ResponseEntity<Void> voice(@ApiParam(hidden = true) @UID String uid,@RequestParam String targetId) {
|
||||
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_900);
|
||||
message.setSender(uid);
|
||||
message.setReceiver(targetId);
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "发起单人视频通话")
|
||||
@ApiImplicitParam(name = "targetId", value = "对方用户ID", paramType = "query", dataTypeClass = Long.class)
|
||||
@PostMapping(value = {"/video"})
|
||||
public ResponseEntity<Void> video(@ApiParam(hidden = true) @UID String uid,@RequestParam String targetId) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_901);
|
||||
message.setSender(uid);
|
||||
message.setReceiver(targetId);
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "接受通话")
|
||||
@ApiImplicitParam(name = "targetId", value = "对方用户ID", paramType = "query", dataTypeClass = Long.class)
|
||||
@PostMapping(value = {"/accept"})
|
||||
public ResponseEntity<Void> accept(@ApiParam(hidden = true) @UID String uid,@RequestParam String targetId) {
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_902);
|
||||
message.setSender(uid);
|
||||
message.setReceiver(targetId);
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "拒绝通话")
|
||||
@ApiImplicitParam(name = "targetId", value = "对方用户ID", paramType = "query", dataTypeClass = Long.class)
|
||||
@PostMapping(value = {"/reject"})
|
||||
public ResponseEntity<Void> reject(@ApiParam(hidden = true) @UID String uid,@RequestParam String targetId) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_903);
|
||||
message.setSender(uid);
|
||||
message.setReceiver(targetId);
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "反馈正忙")
|
||||
@ApiImplicitParam(name = "targetId", value = "对方用户ID", paramType = "query", dataTypeClass = Long.class)
|
||||
@PostMapping(value = {"/busy"})
|
||||
public ResponseEntity<Void> busy(@ApiParam(hidden = true) @UID String uid, @RequestParam String targetId) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_904);
|
||||
message.setSender(uid);
|
||||
message.setReceiver(targetId);
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "挂断通话")
|
||||
@ApiImplicitParam(name = "targetId", value = "对方用户ID", paramType = "query", dataTypeClass = Long.class)
|
||||
@PostMapping(value = {"/hangup"})
|
||||
public ResponseEntity<Void> hangup(@ApiParam(hidden = true) @UID String uid,@RequestParam String targetId) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_905);
|
||||
message.setSender(uid);
|
||||
message.setReceiver(targetId);
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "取消呼叫")
|
||||
@ApiImplicitParam(name = "targetId", value = "对方用户ID", paramType = "query", dataTypeClass = Long.class)
|
||||
@PostMapping(value = {"/cancel"})
|
||||
public ResponseEntity<Void> cancel(@ApiParam(hidden = true) @UID String uid, @RequestParam String targetId) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_906);
|
||||
message.setSender(uid);
|
||||
message.setReceiver(targetId);
|
||||
defaultMessagePusher.push(message);
|
||||
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "同步IceCandidate")
|
||||
@PostMapping(value = {"/transmit/ice"})
|
||||
public ResponseEntity<Void> ice(@ApiParam(hidden = true) @UID String uid,
|
||||
@RequestBody WebrtcRequest request
|
||||
) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_907);
|
||||
message.setSender(uid);
|
||||
message.setContent(request.getContent());
|
||||
message.setReceiver(request.getUid());
|
||||
defaultMessagePusher.push(message);
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "同步offer")
|
||||
@PostMapping(value = {"/transmit/offer"})
|
||||
public ResponseEntity<Void> offer(@ApiParam(hidden = true) @UID String uid,
|
||||
@RequestBody WebrtcRequest request
|
||||
) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_908);
|
||||
message.setSender(uid);
|
||||
message.setContent(request.getContent());
|
||||
message.setReceiver(request.getUid());
|
||||
defaultMessagePusher.push(message);
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
|
||||
@ApiOperation(httpMethod = "POST", value = "同步answer")
|
||||
@PostMapping(value = {"/transmit/answer"})
|
||||
public ResponseEntity<Void> answer(@ApiParam(hidden = true) @UID String uid,
|
||||
@RequestBody WebrtcRequest request
|
||||
) {
|
||||
|
||||
Message message = new Message();
|
||||
message.setAction(MessageAction.ACTION_909);
|
||||
message.setSender(uid);
|
||||
message.setContent(request.getContent());
|
||||
message.setReceiver(request.getUid());
|
||||
defaultMessagePusher.push(message);
|
||||
return ResponseEntity.make();
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.mvc.interceptor;
|
||||
|
||||
import com.farsunset.cim.annotation.AccessToken;
|
||||
import com.farsunset.cim.annotation.UID;
|
||||
import com.farsunset.cim.service.AccessTokenService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 在此鉴权获得UID
|
||||
*/
|
||||
@Component
|
||||
public class TokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final String HEADER_TOKEN = "access-token";
|
||||
|
||||
@Resource
|
||||
private AccessTokenService accessTokenService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
|
||||
String token = request.getHeader(HEADER_TOKEN);
|
||||
|
||||
String uid = accessTokenService.getUid(token);
|
||||
|
||||
/*
|
||||
* 直接拒绝无token的接口调用请求或者token没有查询到对应的登录用户
|
||||
*/
|
||||
if (uid == null) {
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
return false;
|
||||
}
|
||||
|
||||
request.setAttribute(UID.class.getName(), uid);
|
||||
request.setAttribute(AccessToken.class.getName(), token);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
@ -19,45 +19,40 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.client.model;
|
||||
package com.farsunset.cim.mvc.request;
|
||||
|
||||
import com.farsunset.cim.annotation.CreateAction;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.farsunset.cim.sdk.client.constant.CIMConstant;
|
||||
@ApiModel("单人通话ice、offer、answer同步请求体")
|
||||
public class WebrtcRequest implements Serializable {
|
||||
|
||||
/**
|
||||
* 服务端心跳请求
|
||||
*
|
||||
*/
|
||||
public class HeartbeatRequest implements Serializable, Protobufable {
|
||||
@NotNull(message = "UID不能为空",groups = CreateAction.class)
|
||||
@ApiModelProperty("对方UID")
|
||||
private String uid;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
|
||||
private static final String CMD_HEARTBEAT_RESPONSE = "SR";
|
||||
|
||||
private static final HeartbeatRequest object = new HeartbeatRequest();
|
||||
|
||||
private HeartbeatRequest() {
|
||||
@NotEmpty(message = "content不能超过2000个字符",groups = CreateAction.class)
|
||||
@ApiModelProperty("ice信息json、offer或者answer的sdp")
|
||||
private String content;
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public static HeartbeatRequest getInstance() {
|
||||
return object;
|
||||
public void setUid(String uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getByteArray() {
|
||||
return CMD_HEARTBEAT_RESPONSE.getBytes();
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TAG;
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getType() {
|
||||
return CIMConstant.ProtobufType.S_H_RQ;
|
||||
}
|
||||
|
||||
}
|
@ -19,43 +19,28 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.server.model;
|
||||
package com.farsunset.cim.mvc.resolver;
|
||||
|
||||
import com.farsunset.cim.sdk.server.constant.CIMConstant;
|
||||
import com.farsunset.cim.annotation.AccessToken;
|
||||
import com.farsunset.cim.annotation.UID;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import java.io.Serializable;
|
||||
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* 服务端心跳请求
|
||||
*/
|
||||
public class HeartbeatRequest implements Serializable, Transportable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
|
||||
private static final String CMD_HEARTBEAT_RESPONSE = "SR";
|
||||
private static HeartbeatRequest object = new HeartbeatRequest();
|
||||
|
||||
private HeartbeatRequest() {
|
||||
|
||||
}
|
||||
|
||||
public static HeartbeatRequest getInstance() {
|
||||
return object;
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(AccessToken.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBody() {
|
||||
return CMD_HEARTBEAT_RESPONSE.getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getType() {
|
||||
return CIMConstant.ProtobufType.S_H_RQ;
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
|
||||
return webRequest.getAttribute(AccessToken.class.getName(),RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
|
||||
}
|
@ -19,45 +19,27 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.server.model;
|
||||
package com.farsunset.cim.mvc.resolver;
|
||||
|
||||
import com.farsunset.cim.sdk.server.constant.CIMConstant;
|
||||
import com.farsunset.cim.annotation.UID;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import java.io.Serializable;
|
||||
public class UidArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* 服务端心跳请求
|
||||
*
|
||||
*/
|
||||
public class HeartbeatRequest implements Serializable, Transportable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
|
||||
private static final String CMD_HEARTBEAT_RESPONSE = "SR";
|
||||
|
||||
private static HeartbeatRequest object = new HeartbeatRequest();
|
||||
|
||||
private HeartbeatRequest() {
|
||||
|
||||
}
|
||||
|
||||
public static HeartbeatRequest getInstance() {
|
||||
return object;
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return parameter.hasParameterAnnotation(UID.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBody() {
|
||||
return CMD_HEARTBEAT_RESPONSE.getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getType() {
|
||||
return CIMConstant.ProtobufType.S_H_RQ;
|
||||
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
|
||||
return webRequest.getAttribute(UID.class.getName(),RequestAttributes.SCOPE_REQUEST);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.mvc.response;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class ResponseEntity<T> {
|
||||
private int code = HttpStatus.OK.value();
|
||||
private String message;
|
||||
private T data;
|
||||
private String token;
|
||||
private Long timestamp;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(Long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public static ResponseEntity<Void> make(){
|
||||
return new ResponseEntity<>();
|
||||
}
|
||||
|
||||
public static ResponseEntity<Void> make(int code){
|
||||
return make(code,null);
|
||||
}
|
||||
|
||||
public static <T> ResponseEntity<T> make(int code,String message){
|
||||
ResponseEntity<T> result = new ResponseEntity<>();
|
||||
result.setCode(code);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ResponseEntity<Void> make(HttpStatus status){
|
||||
ResponseEntity<Void> result = new ResponseEntity<>();
|
||||
result.setCode(status.value());
|
||||
result.setMessage(status.getReasonPhrase());
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <Q> ResponseEntity<Q> make(HttpStatus status,String message){
|
||||
ResponseEntity<Q> result = new ResponseEntity<>();
|
||||
result.setCode(status.value());
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static <Q> ResponseEntity<Q> ok(Q data){
|
||||
ResponseEntity<Q> result = new ResponseEntity<>();
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.push;
|
||||
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.sdk.server.model.Message;
|
||||
import com.farsunset.cim.service.ApnsService;
|
||||
import com.farsunset.cim.service.CIMSessionService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
|
||||
/*
|
||||
* 消息发送实现类
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class DefaultMessagePusher implements CIMMessagePusher {
|
||||
|
||||
@Value("${server.host}")
|
||||
private String host;
|
||||
|
||||
@Resource
|
||||
private CIMSessionService cimSessionService;
|
||||
|
||||
|
||||
|
||||
@Resource
|
||||
private ApnsService apnsService;
|
||||
|
||||
|
||||
/*
|
||||
* 向用户发送消息
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
@Override
|
||||
public void push(Message message) {
|
||||
CIMSession session = cimSessionService.get(message.getReceiver());
|
||||
|
||||
if(session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* IOS设备,如果开启了apns,则使用apns推送
|
||||
*/
|
||||
if (session.isIOSChannel() && session.isApnsEnable()) {
|
||||
apnsService.push(message, session.getDeviceId());
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 服务器集群时,判断当前session是否连接于本台服务器
|
||||
* 如果连接到了其他服务器则转发请求到目标服务器
|
||||
*/
|
||||
if (session.isConnected() && !Objects.equals(host, session.getHost())) {
|
||||
/*
|
||||
* @TODO
|
||||
* 在此调用目标服务器接口来发送,如session.host = 123.123.123.123
|
||||
* 调用目标服务器的消息发送接口http://123.123.123.123:8080/message/send
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 如果是Android,浏览器或者windows客户端则直接发送
|
||||
*/
|
||||
if (session.isConnected() && Objects.equals(host, session.getHost())) {
|
||||
session.write(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.repository;
|
||||
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/*
|
||||
* 正式场景下,使用redis或者数据库来存储session信息
|
||||
*/
|
||||
@Repository
|
||||
public class SessionRepository {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public interface SessionRepository extends JpaRepository<Session, Long> {
|
||||
|
||||
private ConcurrentHashMap<String, CIMSession> map = new ConcurrentHashMap<>();
|
||||
@Modifying
|
||||
@Query("delete from Session where host = ?1 ")
|
||||
void deleteAll(String host);
|
||||
|
||||
@Modifying
|
||||
@Query("update Session set state = :state where id = :id")
|
||||
void updateState(long id,int state);
|
||||
|
||||
public void save(CIMSession session){
|
||||
map.put(session.getAccount(),session);
|
||||
}
|
||||
@Modifying
|
||||
@Query("update Session set state = " + Session.STATE_APNS + " where uid = ?1 and channel = ?2")
|
||||
void openApns(String uid,String channel);
|
||||
|
||||
public CIMSession get(String account){
|
||||
return map.get(account);
|
||||
}
|
||||
|
||||
public void remove(String account){
|
||||
map.remove(account);
|
||||
}
|
||||
|
||||
public List<CIMSession> findAll(){
|
||||
return new LinkedList<>(map.values());
|
||||
}
|
||||
@Modifying
|
||||
@Query("update Session set state = " + Session.STATE_ACTIVE + " where uid = ?1 and channel = ?2")
|
||||
void closeApns(String uid,String channel);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,9 +21,10 @@
|
||||
*/
|
||||
package com.farsunset.cim.service;
|
||||
|
||||
import com.farsunset.cim.sdk.server.model.Message;
|
||||
|
||||
public interface ApnsService {
|
||||
import com.farsunset.cim.model.Message;
|
||||
|
||||
public interface APNsService {
|
||||
|
||||
void push(Message message, String deviceToken);
|
||||
}
|
@ -19,14 +19,14 @@
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.sdk.client.model;
|
||||
package com.farsunset.cim.service;
|
||||
|
||||
/**
|
||||
* 需要向另一端发送的结构体
|
||||
*/
|
||||
public interface Protobufable {
|
||||
|
||||
byte[] getByteArray();
|
||||
public interface AccessTokenService {
|
||||
|
||||
byte getType();
|
||||
String generate(String uid);
|
||||
|
||||
String getUid(String token);
|
||||
|
||||
void delete(String value);
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.service;
|
||||
|
||||
|
||||
import com.farsunset.cim.entity.Session;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 存储连接信息,便于查看用户的链接信息
|
||||
*/
|
||||
public interface SessionService {
|
||||
|
||||
void add(Session session);
|
||||
|
||||
void delete(long id);
|
||||
|
||||
/**
|
||||
* 删除本机的连接记录
|
||||
*/
|
||||
void deleteLocalhost();
|
||||
|
||||
void updateState(long id,int state);
|
||||
|
||||
void openApns(String uid,String deviceToken);
|
||||
|
||||
void closeApns(String uid);
|
||||
|
||||
List<Session> findAll();
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.service.impl;
|
||||
|
||||
import com.eatthepath.pushy.apns.ApnsClient;
|
||||
import com.eatthepath.pushy.apns.ApnsClientBuilder;
|
||||
import com.eatthepath.pushy.apns.ApnsPushNotification;
|
||||
import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder;
|
||||
import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder;
|
||||
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
|
||||
import com.eatthepath.pushy.apns.util.TokenUtil;
|
||||
import com.farsunset.cim.config.properties.APNsProperties;
|
||||
import com.farsunset.cim.model.Message;
|
||||
import com.farsunset.cim.service.APNsService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Service
|
||||
public class APNsServiceImpl implements APNsService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(APNsServiceImpl.class);
|
||||
|
||||
private final ApnsClient apnsClient;
|
||||
|
||||
private final APNsProperties properties;
|
||||
|
||||
@Autowired
|
||||
public APNsServiceImpl(APNsProperties properties) throws IOException {
|
||||
this.properties = properties;
|
||||
|
||||
InputStream stream = getClass().getResourceAsStream(properties.getP12File());
|
||||
|
||||
apnsClient = new ApnsClientBuilder()
|
||||
.setApnsServer(properties.isDebug() ? ApnsClientBuilder.DEVELOPMENT_APNS_HOST : ApnsClientBuilder.PRODUCTION_APNS_HOST)
|
||||
.setClientCredentials(stream, properties.getP12Password())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void push(Message message, String deviceToken) {
|
||||
|
||||
if(StringUtils.isBlank(deviceToken)) {
|
||||
return ;
|
||||
}
|
||||
|
||||
ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder();
|
||||
|
||||
payloadBuilder.setAlertTitle("您有一条新的消息");
|
||||
|
||||
payloadBuilder.setSound("default");
|
||||
payloadBuilder.setBadgeNumber(1);
|
||||
payloadBuilder.addCustomProperty("id",message.getId());
|
||||
payloadBuilder.addCustomProperty("action",message.getAction());
|
||||
payloadBuilder.addCustomProperty("content",message.getContent());
|
||||
payloadBuilder.addCustomProperty("sender",message.getSender());
|
||||
payloadBuilder.addCustomProperty("receiver",message.getReceiver());
|
||||
payloadBuilder.addCustomProperty("format",message.getFormat());
|
||||
payloadBuilder.addCustomProperty("extra",message.getExtra());
|
||||
payloadBuilder.addCustomProperty("timestamp",message.getTimestamp());
|
||||
|
||||
String token = TokenUtil.sanitizeTokenString(deviceToken);
|
||||
|
||||
String payload = payloadBuilder.build();
|
||||
|
||||
ApnsPushNotification notification = new SimpleApnsPushNotification(token, properties.getAppId(), payload);
|
||||
|
||||
apnsClient.sendNotification(notification).whenComplete((response, cause) -> {
|
||||
if (response != null) {
|
||||
LOGGER.info("APNs push done.\ndeviceToken : {} \napnsPayload : {}",deviceToken,payload);
|
||||
} else {
|
||||
LOGGER.error("APNs push failed",cause);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -21,56 +21,42 @@
|
||||
*/
|
||||
package com.farsunset.cim.service.impl;
|
||||
|
||||
|
||||
import com.farsunset.cim.repository.SessionRepository;
|
||||
import com.farsunset.cim.sdk.server.handler.CIMNioSocketAcceptor;
|
||||
import com.farsunset.cim.sdk.server.model.CIMSession;
|
||||
import com.farsunset.cim.service.CIMSessionService;
|
||||
import com.farsunset.cim.component.redis.TokenRedisTemplate;
|
||||
import com.farsunset.cim.service.AccessTokenService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class CIMSessionServiceImpl implements CIMSessionService {
|
||||
|
||||
@Resource
|
||||
private CIMNioSocketAcceptor nioSocketAcceptor;
|
||||
|
||||
@Resource
|
||||
private SessionRepository sessionRepository;
|
||||
public class AccessTokenServiceImpl implements AccessTokenService {
|
||||
@Resource
|
||||
private TokenRedisTemplate tokenRedisTemplate;
|
||||
|
||||
@Override
|
||||
public void save(CIMSession session) {
|
||||
sessionRepository.save(session);
|
||||
public String getUid(String token) {
|
||||
if (StringUtils.isBlank(token)){
|
||||
return null;
|
||||
}
|
||||
return tokenRedisTemplate.get(token);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* @param account 用户id
|
||||
@Override
|
||||
public void delete(String token) {
|
||||
tokenRedisTemplate.delete(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 方便调试,这里生成token为永不过期
|
||||
* @param uid
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public CIMSession get(String account) {
|
||||
|
||||
CIMSession session = sessionRepository.get(account);
|
||||
|
||||
if (session != null){
|
||||
session.setSession(nioSocketAcceptor.getManagedSession(session.getNid()));
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String account) {
|
||||
sessionRepository.remove(account);
|
||||
public String generate(String uid) {
|
||||
String newToken = UUID.randomUUID().toString().replace("-","");
|
||||
tokenRedisTemplate.save(newToken, uid);
|
||||
return newToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CIMSession> list() {
|
||||
return sessionRepository.findAll();
|
||||
}
|
||||
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.service.impl;
|
||||
|
||||
import cn.teaey.apns4j.Apns4j;
|
||||
import cn.teaey.apns4j.network.ApnsChannel;
|
||||
import cn.teaey.apns4j.network.ApnsChannelFactory;
|
||||
import cn.teaey.apns4j.network.ApnsGateway;
|
||||
import com.farsunset.cim.sdk.server.model.Message;
|
||||
import com.farsunset.cim.service.ApnsService;
|
||||
import com.farsunset.cim.util.ApnsPayloadCompat;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@Service
|
||||
public class JavaApnsServiceImpl implements ApnsService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JavaApnsServiceImpl.class);
|
||||
|
||||
@Value("${apple.apns.p12.password}")
|
||||
private String password;
|
||||
@Value("${apple.apns.p12.file}")
|
||||
private String p12Path;
|
||||
@Value("${apple.apns.debug}")
|
||||
private boolean isDebug;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void push(Message message, String deviceToken) {
|
||||
|
||||
if(StringUtils.isBlank(deviceToken)) {
|
||||
return ;
|
||||
}
|
||||
|
||||
InputStream stream = getClass().getResourceAsStream(p12Path);
|
||||
ApnsChannelFactory apnsChannelFactory = Apns4j.newChannelFactoryBuilder()
|
||||
.keyStoreMeta(stream)
|
||||
.keyStorePwd(password)
|
||||
.apnsGateway(isDebug ? ApnsGateway.DEVELOPMENT : ApnsGateway.PRODUCTION)
|
||||
.build();
|
||||
|
||||
ApnsChannel apnsChannel = apnsChannelFactory.newChannel();
|
||||
|
||||
try {
|
||||
ApnsPayloadCompat apnsPayload = new ApnsPayloadCompat();
|
||||
apnsPayload.setAction(message.getAction());
|
||||
apnsPayload.setContent(message.getContent());
|
||||
apnsPayload.setSender(message.getSender());
|
||||
apnsPayload.setFormat(message.getFormat());
|
||||
apnsPayload.setReceiver(message.getReceiver());
|
||||
apnsChannel.send(deviceToken, apnsPayload);
|
||||
|
||||
LOGGER.info(deviceToken +"\r\ndata: {}",apnsPayload.toJsonString());
|
||||
}catch(Exception exception) {
|
||||
LOGGER.error("Apns has error",exception);
|
||||
}finally {
|
||||
apnsChannel.close();
|
||||
IOUtils.closeQuietly(stream);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.service.impl;
|
||||
|
||||
import com.farsunset.cim.component.redis.KeyValueRedisTemplate;
|
||||
import com.farsunset.cim.entity.Session;
|
||||
import com.farsunset.cim.repository.SessionRepository;
|
||||
import com.farsunset.cim.service.SessionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class SessionServiceImpl implements SessionService {
|
||||
|
||||
@Resource
|
||||
private SessionRepository sessionRepository;
|
||||
|
||||
@Resource
|
||||
private KeyValueRedisTemplate keyValueRedisTemplate;
|
||||
|
||||
|
||||
private final String host;
|
||||
|
||||
public SessionServiceImpl() throws UnknownHostException {
|
||||
host = InetAddress.getLocalHost().getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Session session) {
|
||||
session.setBindTime(System.currentTimeMillis());
|
||||
session.setHost(host);
|
||||
sessionRepository.save(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(long id) {
|
||||
sessionRepository.deleteById(id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void deleteLocalhost() {
|
||||
sessionRepository.deleteAll(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(long id, int state) {
|
||||
sessionRepository.updateState(id,state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openApns(String uid,String deviceToken) {
|
||||
keyValueRedisTemplate.openApns(uid,deviceToken);
|
||||
sessionRepository.openApns(uid,Session.CHANNEL_IOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeApns(String uid) {
|
||||
keyValueRedisTemplate.closeApns(uid);
|
||||
sessionRepository.closeApns(uid,Session.CHANNEL_IOS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Session> findAll() {
|
||||
return sessionRepository.findAll()
|
||||
.stream()
|
||||
.filter(session -> session.getState() == Session.STATE_ACTIVE || session.getState() == Session.STATE_APNS)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.util;
|
||||
|
||||
import cn.teaey.apns4j.protocol.ApnsPayload;
|
||||
|
||||
public class ApnsPayloadCompat extends ApnsPayload {
|
||||
|
||||
private static final String DATA_FORMAT = "{\"aps\": {\"message\": {\"action\":\"%s\",\"content\":\"%s\",\"sender\":\"%s\",\"receiver\":\"%s\",\"format\":\"%s\"},\"content-available\": 1}}";
|
||||
|
||||
private String action;
|
||||
private String content;
|
||||
private String sender;
|
||||
private String format;
|
||||
private String receiver;
|
||||
|
||||
|
||||
public String getReceiver() {
|
||||
return receiver;
|
||||
}
|
||||
|
||||
public void setReceiver(String receiver) {
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public void setSender(String sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJsonString() {
|
||||
return String.format(DATA_FORMAT, action, content, sender,receiver,format);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Xia Jun(3979434@qq.com).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
***************************************************************************************
|
||||
* *
|
||||
* Website : http://www.farsunset.com *
|
||||
* *
|
||||
***************************************************************************************
|
||||
*/
|
||||
package com.farsunset.cim.util;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public final class JSONUtils {
|
||||
private static final JsonMapper OBJECT_MAPPER = JsonMapper.builder()
|
||||
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
|
||||
.enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||
.serializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
.build();
|
||||
|
||||
|
||||
public static String toJSONString(Object data) {
|
||||
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsString(data);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static <T> T fromJson(String str, Class<T> clazz) {
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(str, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T fromJson(byte[] data, Class<T> clazz) {
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(data, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static <T> List<T> parseList(String str, Class<T> clazz) {
|
||||
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructCollectionLikeType(List.class,clazz);
|
||||
try {
|
||||
return OBJECT_MAPPER.readValue(str, javaType);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
BIN
cim-boot-server/src/main/resources/apns/app.p12
Normal file
BIN
cim-boot-server/src/main/resources/apns/app.p12
Normal file
Binary file not shown.
@ -1,6 +1,56 @@
|
||||
server.port=8080
|
||||
server.host=127.0.0.1
|
||||
# 服务端文档地址
|
||||
# https://www.yuque.com/yuanfangxiyang/ma4ytb/vvy3iz#pW3DQ
|
||||
|
||||
server.port=8080
|
||||
|
||||
spring.jackson.default-property-inclusion=non_empty
|
||||
|
||||
#单台服务器可设置为dev,广播消息走本地消息事件(参见SignalRedisTemplate.java)
|
||||
#多台服务器集群环境设置为prd,广播消息走三方消息队列
|
||||
spring.profiles.active=dev
|
||||
|
||||
|
||||
##################################################################
|
||||
# JDBC Config #
|
||||
##################################################################
|
||||
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/cim?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
|
||||
spring.datasource.username = cim
|
||||
spring.datasource.password = f8HYPmssXL6XmZeK
|
||||
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
|
||||
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
|
||||
|
||||
spring.datasource.hikari.minimum-idle=5
|
||||
spring.datasource.hikari.maximum-pool-size=10
|
||||
spring.datasource.hikari.auto-commit=true
|
||||
spring.datasource.hikari.idle-timeout=30000
|
||||
spring.datasource.hikari.pool-name=MASTER_HIKARI_POOL
|
||||
spring.datasource.hikari.max-lifetime=120000
|
||||
spring.datasource.hikari.connection-timeout=30000
|
||||
spring.datasource.hikari.connection-test-query=SELECT 1
|
||||
spring.datasource.hikari.validation-timeout=600000
|
||||
|
||||
|
||||
##################################################################
|
||||
# JPA Config #
|
||||
##################################################################
|
||||
spring.jpa.database = MYSQL
|
||||
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
|
||||
spring.jpa.hibernate.ddl-auto = update
|
||||
spring.jpa.open-in-view = false
|
||||
spring.jpa.hibernate.naming.implicit-strategy= org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
|
||||
spring.jpa.hibernate.naming.physical-strategy= org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
|
||||
|
||||
##################################################################
|
||||
# Redis Config #
|
||||
##################################################################
|
||||
spring.redis.host=127.0.0.1
|
||||
spring.redis.port=6379
|
||||
spring.redis.database=12
|
||||
spring.redis.lettuce.pool.max-active=10
|
||||
spring.redis.lettuce.pool.max-wait= 10s
|
||||
spring.redis.lettuce.pool.max-idle=5
|
||||
spring.redis.lettuce.pool.min-idle=1
|
||||
spring.redis.timeout=10s
|
||||
|
||||
##################################################################
|
||||
# Freemarker Config #
|
||||
@ -18,9 +68,26 @@ spring.messages.basename=i18n/messages
|
||||
##################################################################
|
||||
# CIM Config #
|
||||
##################################################################
|
||||
cim.app.port=23456
|
||||
cim.websocket.port=34567
|
||||
|
||||
apple.apns.debug=false
|
||||
apple.apns.p12.password= your p12 password
|
||||
apple.apns.p12.file= /apns/lvxin.p12
|
||||
#commented to disable this port.
|
||||
cim.app.port=23456
|
||||
cim.app.enable=true
|
||||
cim.app.write-idle=45s
|
||||
cim.app.read-idle=60s
|
||||
cim.app.max-pong-timeout=3
|
||||
|
||||
cim.websocket.enable=true
|
||||
cim.websocket.port=34567
|
||||
cim.websocket.path=/
|
||||
## json or protobuf
|
||||
cim.websocket.protocol=protobuf
|
||||
|
||||
cim.websocket.write-idle=45s
|
||||
cim.websocket.read-idle=60s
|
||||
cim.websocket.max-pong-timeout=3
|
||||
|
||||
#please setting your p12 info and appId.
|
||||
cim.apns.p12.file=/apns/app.p12
|
||||
cim.apns.p12.password=123
|
||||
cim.apns.debug=false
|
||||
cim.apns.app-id=com.xxx.xxx.ios
|
||||
|
@ -32,6 +32,7 @@ module.common.headlogo = 头像
|
||||
module.common.homepage = 主页
|
||||
module.common.website = 网址
|
||||
module.common.text = 文字
|
||||
module.common.language = 语言
|
||||
|
||||
module.global.error.500.hint = 服务程序发生内部错误
|
||||
module.global.error.400.hint = 请求参数类型不正确
|
||||
@ -57,7 +58,7 @@ module.console.cimsession.channel = 终端
|
||||
module.console.cimsession.app.version =应用版本
|
||||
module.console.cimsession.os.version =系统版本
|
||||
module.console.cimsession.deviceid =设备编号
|
||||
module.console.cimsession.device.model =终端型号
|
||||
module.console.cimsession.device.name =终端型号
|
||||
module.console.cimsession.online.time =在线时长(秒)
|
||||
module.console.cimsession.time.format ={0}秒
|
||||
|
||||
|
12
cim-boot-server/src/main/resources/logback-spring.xml
Normal file
12
cim-boot-server/src/main/resources/logback-spring.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<withJansi>true</withJansi>
|
||||
<encoder>
|
||||
<pattern>[%thread] %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %cyan(%logger{32}) - %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="info">
|
||||
<appender-ref ref="console"/>
|
||||
</root>
|
||||
</configuration>
|
@ -3,9 +3,14 @@
|
||||
<ul class="ui-nav-inner">
|
||||
<li style="height: 50px;text-align: center;margin-top: 10px;">
|
||||
<a type="button" target="_blank" href="/webclient" class="btn btn-danger" >
|
||||
<span class="glyphicon glyphicon-globe"></span> WEB版本
|
||||
<span class="glyphicon glyphicon-globe"></span> 网页终端
|
||||
</a>
|
||||
</li>
|
||||
<li style="height: 50px;text-align: center;margin-top: 10px;">
|
||||
<a type="button" target="_blank" href="/swagger-ui/index.html" class="btn btn-success" >
|
||||
<span class="glyphicon glyphicon-leaf"></span> 接口文档
|
||||
</a>
|
||||
</li>
|
||||
<li style="border-bottom: 1px solid #D1D6DA;"></li>
|
||||
<li class="ui-item" id="sessionMenu">
|
||||
<a href="/console/session/list">
|
||||
|
@ -20,8 +20,7 @@ function showMessageDialog(account){
|
||||
function doSendMessage(){
|
||||
var message = $('#message').val();
|
||||
var account = $('#Saccount').val();
|
||||
if($.trim(message)=='')
|
||||
{
|
||||
if($.trim(message)===''){
|
||||
return;
|
||||
}
|
||||
showProcess("<@spring.message 'module.console.cimsession.sending'/>");
|
||||
@ -50,34 +49,34 @@ function doSendMessage(){
|
||||
<thead>
|
||||
<tr class="tableHeader">
|
||||
<th width="8%"><@spring.message 'module.console.cimsession.account'/></th>
|
||||
<th width="6%"> <@spring.message 'module.console.cimsession.nid'/></th>
|
||||
<th width="6%"><@spring.message 'module.console.cimsession.channel'/></th>
|
||||
<th width="14%"><@spring.message 'module.console.cimsession.deviceid'/></th>
|
||||
<th width="10%"><@spring.message 'module.console.cimsession.device.model'/></th>
|
||||
<th width="6%"><@spring.message 'module.console.cimsession.app.version'/></th>
|
||||
<th width="6%"><@spring.message 'module.console.cimsession.os.version'/></th>
|
||||
<th width="6%"><@spring.message 'module.console.cimsession.online.time'/></th>
|
||||
<th width="25%"><@spring.message 'module.common.location'/></th>
|
||||
<th width="8%"><@spring.message "module.common.operation"/></th>
|
||||
<th width="8%"><@spring.message 'module.console.cimsession.nid'/></th>
|
||||
<th width="8%"><@spring.message 'module.console.cimsession.channel'/></th>
|
||||
<th width="15%"><@spring.message 'module.console.cimsession.deviceid'/></th>
|
||||
<th width="15%"><@spring.message 'module.console.cimsession.device.name'/></th>
|
||||
<th width="8%"><@spring.message 'module.console.cimsession.app.version'/></th>
|
||||
<th width="8%"><@spring.message 'module.console.cimsession.os.version'/></th>
|
||||
<th width="10%"><@spring.message 'module.common.language'/></th>
|
||||
<th width="10%"><@spring.message 'module.console.cimsession.online.time'/></th>
|
||||
<th width="10%"><@spring.message "module.common.operation"/></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<#list sessionList as cimsession>
|
||||
<tr style="height: 50px;">
|
||||
<td>${cimsession.account! }</td>
|
||||
<td>${cimsession.uid! }</td>
|
||||
<td><#if cimsession.nid??>${cimsession.nid}</#if></td>
|
||||
<td>${cimsession.channel! }</td>
|
||||
<td>${cimsession.deviceId! }</td>
|
||||
<td>${cimsession.deviceModel! }</td>
|
||||
<td>${cimsession.clientVersion! }</td>
|
||||
<td>${cimsession.systemVersion! }</td>
|
||||
<td>${cimsession.deviceName! }</td>
|
||||
<td>${cimsession.appVersion! }</td>
|
||||
<td>${cimsession.osVersion! }</td>
|
||||
<td>${cimsession.language! }</td>
|
||||
<td>
|
||||
<@spring.messageArgs "module.console.cimsession.time.format",[((.now?long - cimsession.bindTime)/1000)?round?c] />
|
||||
</td>
|
||||
<td>${cimsession.location!}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs">
|
||||
<button type="button" class="btn btn-primary" style="padding: 5px;" onclick="showMessageDialog('${cimsession.account!}')">
|
||||
<button type="button" class="btn btn-primary" style="padding: 5px;" onclick="showMessageDialog('${cimsession.uid!}')">
|
||||
<span class="glyphicon glyphicon-send" style="top:2px;"></span>
|
||||
<@spring.message 'module.console.cimsession.send.message'/>
|
||||
</button>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>CIM Webclient </title>
|
||||
<title>CIM Webclient for protobuf</title>
|
||||
<link rel="shortcut icon" href="/image/favicon.ico" type="image/x-icon">
|
||||
<link charset="utf-8" rel="stylesheet" href="/bootstrap-3.3.7-dist/css/bootstrap.min.css" />
|
||||
<link charset="utf-8" rel="stylesheet" href="/css/common.css" />
|
||||
@ -22,25 +22,32 @@
|
||||
|
||||
/** 当socket连接成功回调 **/
|
||||
function onConnectFinished(){
|
||||
CIMPushManager.bindAccount($('#account').val());
|
||||
CIMPushManager.bind($('#account').val());
|
||||
}
|
||||
|
||||
/** 当收到请求回复时候回调 **/
|
||||
function onReplyReceived(reply)
|
||||
{
|
||||
console.log(reply);
|
||||
if(reply.key=='client_bind' && reply.code==200)
|
||||
{
|
||||
hideProcess();
|
||||
|
||||
$('#LoginDialog').fadeOut();
|
||||
if (reply.key === KEY_CLIENT_BIND && reply.code === CODE_OK) {
|
||||
hideProcess();
|
||||
|
||||
$('#LoginDialog').fadeOut();
|
||||
|
||||
$('#MessageDialog').fadeIn();
|
||||
$('#MessageDialog').addClass("in");
|
||||
$("#current_account").text($('#account').val());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 链接鉴权失败
|
||||
*/
|
||||
if(reply.key === KEY_HANDSHAKE && reply.code === CODE_UNAUTHORIZED){
|
||||
hideProcess();
|
||||
showETip("鉴权失败");
|
||||
}
|
||||
|
||||
|
||||
$('#MessageDialog').fadeIn();
|
||||
$('#MessageDialog').addClass("in");
|
||||
$("#current_account").text($('#account').val());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** 当收到消息时候回调 **/
|
||||
@ -48,7 +55,10 @@
|
||||
function onMessageReceived(message)
|
||||
{
|
||||
console.log(message);
|
||||
if(message.action == ACTION_999){
|
||||
/*
|
||||
账户在其他地方登录了
|
||||
*/
|
||||
if(message.action === ACTION_999){
|
||||
$('#MessageDialog').fadeOut();
|
||||
$('#LoginDialog').fadeIn();
|
||||
$('#LoginDialog').addClass("in");
|
||||
@ -56,9 +66,8 @@
|
||||
}
|
||||
|
||||
showNotification(message.content);
|
||||
|
||||
$("#messageList").append("<h6 style='text-align: center;'>时间:"+new Date(message.timestamp).toLocaleString()+"</h6>");
|
||||
$("#messageList").append("<div class='alert alert-info' >"+message.content+"</div>");
|
||||
var time = new Date(message.timestamp).toLocaleString();
|
||||
$("#messageList").prepend("<div class='alert alert-info' >"+time+"</p></p>"+message.content+"</div>");
|
||||
}
|
||||
|
||||
|
||||
@ -83,8 +92,9 @@
|
||||
$(document).ready(function(){
|
||||
$('#LoginDialog').fadeIn();
|
||||
$('#LoginDialog').addClass("in");
|
||||
|
||||
initNotification();
|
||||
$('#host').val(CIM_HOST);
|
||||
$('#port').val(CIM_PORT);
|
||||
initNotification();
|
||||
});
|
||||
|
||||
|
||||
@ -130,12 +140,28 @@
|
||||
</div>
|
||||
|
||||
<div class="input-group" style="margin-top: 30px;margin-left:10px;margin-right:10px;margin-bottom:30px;">
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></span>
|
||||
<span class="input-group-addon">账号</span>
|
||||
<input type="text" class="form-control" id="account" maxlength="32" placeholder="帐号(数字或者英文字母)"
|
||||
style="display: inline; width: 100%; height: 50px;" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="alert alert-success" role="alert" style="margin: 0 10px;">
|
||||
cim.web.sdk.js中设置cim服务的IP(域名)和端口
|
||||
</div>
|
||||
|
||||
<div class="input-group" style="margin-top: 30px;margin-left:10px;margin-right:10px;margin-bottom:30px;">
|
||||
<span class="input-group-addon">host</span>
|
||||
<input type="text" class="form-control" id="host" maxlength="32" readonly = "readonly"
|
||||
style="display: inline; width: 100%; height: 50px;" />
|
||||
</div>
|
||||
|
||||
<div class="input-group" style="margin-top: 30px;margin-left:10px;margin-right:10px;margin-bottom:30px;">
|
||||
<span class="input-group-addon">port</span>
|
||||
<input type="text" class="form-control" id="port" maxlength="32" readonly = "readonly"
|
||||
style="display: inline; width: 100%; height: 50px;" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" style="text-align: center;">
|
||||
<a type="button" class="btn btn-success btn-lg" onclick="doLogin()"
|
||||
style="width: 300px;">登录</a>
|
||||
@ -146,10 +172,13 @@
|
||||
|
||||
<!-- 消息提示页面 -->
|
||||
<div class="modal fade" data-backdrop="static" id="MessageDialog" tabindex="-1" role="dialog" >
|
||||
<div class="modal-dialog" style="width: 600px;margin: 30px auto;">
|
||||
<div class="alert alert-success" role="alert">
|
||||
通过<a href="/console/session/list" class="alert-link" target="_blank" >控制台</a>或者<a href="/swagger-ui/index.html" target="_blank" class="alert-link">调用接口</a>发送消息
|
||||
</div>
|
||||
<div class="modal-dialog" style="width: 720px;margin: 30px auto;">
|
||||
<div class="modal-content" >
|
||||
<div class="modal-header" style="text-align: center;">
|
||||
<span style="float: left;">请在管理页面推送一条消息</span>
|
||||
<span style="float: left;">消息显示面板</span>
|
||||
<span style="float: right;color: #4caf50;">当前帐号:<span id="current_account"></span></span>
|
||||
</div>
|
||||
<div class="modal-body" id="messageList" style="min-height: 600px;" >
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
/*CIM服务器IP*/
|
||||
const CIM_HOST = "127.0.0.1";
|
||||
const CIM_HOST = window.location.hostname;
|
||||
/*
|
||||
*服务端 websocket端口
|
||||
*/
|
||||
@ -7,8 +7,7 @@ const CIM_PORT = 34567;
|
||||
const CIM_URI = "ws://" + CIM_HOST + ":" + CIM_PORT;
|
||||
|
||||
const APP_VERSION = "1.0.0";
|
||||
const APP_CHANNEL = "browser";
|
||||
const APP_PACKAGE = "com.farsunset.cim";
|
||||
const APP_CHANNEL = "web";
|
||||
|
||||
/*
|
||||
*特殊的消息类型,代表被服务端强制下线
|
||||
@ -18,6 +17,25 @@ const DATA_HEADER_LENGTH = 1;
|
||||
|
||||
const MESSAGE = 2;
|
||||
const REPLY_BODY = 4;
|
||||
const SENT_BODY = 3;
|
||||
const PING = 1;
|
||||
const PONG = 0;
|
||||
|
||||
/*
|
||||
* 握手鉴权常量
|
||||
*/
|
||||
const KEY_HANDSHAKE = "client_handshake";
|
||||
const CODE_UNAUTHORIZED = "401";
|
||||
|
||||
const CODE_OK = "200";
|
||||
const KEY_CLIENT_BIND = "client_bind";
|
||||
|
||||
/**
|
||||
* PONG字符串转换后
|
||||
* @type {Uint8Array}
|
||||
*/
|
||||
const PONG_BODY = new Uint8Array([80,79,78,71]);
|
||||
|
||||
|
||||
let socket;
|
||||
let manualStop = false;
|
||||
@ -33,27 +51,27 @@ CIMPushManager.connect = function () {
|
||||
socket.onclose = CIMPushManager.innerOnConnectionClosed;
|
||||
};
|
||||
|
||||
CIMPushManager.bindAccount = function (account) {
|
||||
CIMPushManager.bind = function (account) {
|
||||
|
||||
window.localStorage.account = account;
|
||||
|
||||
let deviceId = window.localStorage.deviceIddeviceId;
|
||||
if (deviceId == '' || deviceId == undefined) {
|
||||
let deviceId = window.localStorage.deviceId;
|
||||
if (deviceId === '' || deviceId === undefined) {
|
||||
deviceId = generateUUID();
|
||||
window.localStorage.deviceId = deviceId;
|
||||
}
|
||||
|
||||
let browser = getBrowser();
|
||||
let body = new proto.com.farsunset.cim.sdk.web.model.SentBody();
|
||||
body.setKey("client_bind");
|
||||
body.setKey(KEY_CLIENT_BIND);
|
||||
body.setTimestamp(new Date().getTime());
|
||||
body.getDataMap().set("account", account);
|
||||
body.getDataMap().set("uid", account);
|
||||
body.getDataMap().set("channel", APP_CHANNEL);
|
||||
body.getDataMap().set("appVersion", APP_VERSION);
|
||||
body.getDataMap().set("osVersion", browser.version);
|
||||
body.getDataMap().set("packageName", APP_PACKAGE);
|
||||
body.getDataMap().set("deviceId", deviceId);
|
||||
body.getDataMap().set("device", browser.name);
|
||||
body.getDataMap().set("deviceName", browser.name);
|
||||
body.getDataMap().set("language", navigator.language);
|
||||
CIMPushManager.sendRequest(body);
|
||||
};
|
||||
|
||||
@ -83,15 +101,21 @@ CIMPushManager.innerOnMessageReceived = function (e) {
|
||||
let type = data[0];
|
||||
let body = data.subarray(DATA_HEADER_LENGTH, data.length);
|
||||
|
||||
if (type == MESSAGE) {
|
||||
if (type === PING) {
|
||||
CIMPushManager.pong();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === MESSAGE) {
|
||||
let message = proto.com.farsunset.cim.sdk.web.model.Message.deserializeBinary(body);
|
||||
onInterceptMessageReceived(message.toObject(false));
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == REPLY_BODY) {
|
||||
if (type === REPLY_BODY) {
|
||||
let message = proto.com.farsunset.cim.sdk.web.model.ReplyBody.deserializeBinary(body);
|
||||
/**
|
||||
|
||||
/*
|
||||
* 将proto对象转换成json对象,去除无用信息
|
||||
*/
|
||||
let reply = {};
|
||||
@ -101,13 +125,21 @@ CIMPushManager.innerOnMessageReceived = function (e) {
|
||||
reply.timestamp = message.getTimestamp();
|
||||
reply.data = {};
|
||||
|
||||
/**
|
||||
/*
|
||||
* 注意,遍历map这里的参数 value在前key在后
|
||||
*/
|
||||
message.getDataMap().forEach(function (v, k) {
|
||||
reply.data[k] = v;
|
||||
});
|
||||
|
||||
/*
|
||||
* 判断是否是握手鉴权失败
|
||||
* 终止后续自动重连
|
||||
*/
|
||||
if(reply.key === KEY_HANDSHAKE && reply.code === CODE_UNAUTHORIZED){
|
||||
manualStop = true;
|
||||
}
|
||||
|
||||
onReplyReceived(reply);
|
||||
}
|
||||
};
|
||||
@ -123,16 +155,24 @@ CIMPushManager.innerOnConnectionClosed = function (e) {
|
||||
|
||||
CIMPushManager.sendRequest = function (body) {
|
||||
let data = body.serializeBinary();
|
||||
let protobuf = new Uint8Array(data.length);
|
||||
protobuf.set(data, 0);
|
||||
let protobuf = new Uint8Array(data.length + 1);
|
||||
protobuf[0] = SENT_BODY;
|
||||
protobuf.set(data, 1);
|
||||
socket.send(protobuf);
|
||||
};
|
||||
|
||||
CIMPushManager.pong = function () {
|
||||
let pong = new Uint8Array(PONG_BODY.byteLength + 1);
|
||||
pong[0] = PONG;
|
||||
pong.set(PONG_BODY,1);
|
||||
socket.send(pong);
|
||||
};
|
||||
|
||||
function onInterceptMessageReceived(message) {
|
||||
/*
|
||||
*被强制下线之后,不再继续连接服务端
|
||||
*/
|
||||
if (message.action == ACTION_999) {
|
||||
if (message.action === ACTION_999) {
|
||||
manualStop = true;
|
||||
}
|
||||
/*
|
||||
@ -174,7 +214,7 @@ function generateUUID() {
|
||||
let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
let r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
return uuid.replace(/-/g, '');
|
||||
}
|
||||
|
Binary file not shown.
@ -1,23 +0,0 @@
|
||||
//
|
||||
// CIMHeader.h
|
||||
// CIMKit
|
||||
//
|
||||
// Created by mason on 2020/11/13.
|
||||
//
|
||||
|
||||
#ifndef CIMHeader_h
|
||||
#define CIMHeader_h
|
||||
|
||||
|
||||
#import "GCDAsyncSocket.h"
|
||||
#import "SentBody.pbobjc.h"
|
||||
#import "Message.pbobjc.h"
|
||||
#import "NSData+IM.h"
|
||||
#import "NSString+IM.h"
|
||||
|
||||
#import "CIMSendMessageData.h"
|
||||
#import "CIMService.h"
|
||||
|
||||
|
||||
|
||||
#endif /* CIMHeader_h */
|
@ -1,103 +0,0 @@
|
||||
//
|
||||
// CIMMessageObserver.h
|
||||
// CIMKit
|
||||
//
|
||||
// Created by mason on 2020/11/18.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "GCDAsyncSocket.h"
|
||||
#import "CIMMessageModel.h"
|
||||
|
||||
@class CIMService;
|
||||
/// 消息回调
|
||||
@protocol CIMPeerMessageObserver <NSObject>
|
||||
|
||||
/// 接受到消息
|
||||
/// @param msg msg description
|
||||
-(void)cimHandleMessage:(CIMMessageModel * _Nonnull)msg;
|
||||
|
||||
/// 消息解析失败
|
||||
/// @param data data description
|
||||
-(void)cimHandleMessageError:(NSData * _Nonnull)data;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
/// 服务器连接回调
|
||||
@protocol CIMConnectionObserver <NSObject>
|
||||
@optional
|
||||
|
||||
/// 用户绑定成功
|
||||
/// @param bindSuccess bindSuccess description
|
||||
-(void)cimDidBindUserSuccess:(BOOL)bindSuccess;
|
||||
|
||||
/// 连接成功
|
||||
-(void)cimDidConnectSuccess;
|
||||
|
||||
/// 断开连接
|
||||
-(void)cimDidConnectClose;
|
||||
|
||||
/// 连接失败
|
||||
/// @param error res description
|
||||
-(void)cimDidConnectError:(NSError *_Nullable)error;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CIMService : NSObject
|
||||
|
||||
+(CIMService*)instance;
|
||||
|
||||
/// 配置IM服务器
|
||||
/// @param host host description
|
||||
/// @param port port description
|
||||
-(void)configHost:(NSString *)host onPort:(NSInteger)port;
|
||||
|
||||
/// 连接服务器并绑定用户
|
||||
/// @param userId userId description
|
||||
-(void)connectionBindUserId:(NSString *)userId;
|
||||
|
||||
/// 添加消息监听回调
|
||||
/// @param observer observer description (可添加多个)不同时记得Remove
|
||||
-(void)addMessageObserver:(id<CIMPeerMessageObserver>)observer;
|
||||
|
||||
/// 添加连接状态监听回调
|
||||
/// @param observer observer description (可添加多个)不同时记得Remove
|
||||
-(void)addConnectionObserver:(id<CIMConnectionObserver>)observer;
|
||||
|
||||
/// 移除监听
|
||||
/// @param observer observer description
|
||||
-(void)removeMessageObserver:(id<CIMPeerMessageObserver>)observer;
|
||||
|
||||
/// 移除监听回调
|
||||
/// @param observer observer description
|
||||
-(void)removeConnectionObserver:(id<CIMConnectionObserver>)observer;
|
||||
|
||||
/// 退出后台 断开连接
|
||||
-(void)enterBackground;
|
||||
|
||||
/// 进入前台重新连接
|
||||
-(void)enterForeground;
|
||||
|
||||
/// 重新连接
|
||||
-(void)reconnect;
|
||||
|
||||
/// 断开连接
|
||||
-(void)disconnect;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Binary file not shown.
@ -1,5 +0,0 @@
|
||||
#### iOS版本SDK介绍
|
||||
iOS版本SDK是由开发者Siter(siterwu@gmail.com)贡献提供的,感谢Siter在百忙之中做出的贡献。
|
||||
---
|
||||
## 源码和集成方式参见下面地址
|
||||
## [https://gitee.com/Siter/cimkit](https://gitee.com/Siter/cimkit)
|
Binary file not shown.
@ -1 +0,0 @@
|
||||
集成方式请参考文档和cim-use-examples中对应的demo
|
@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: com.google.protobuf:protobuf-lite:3.0.1" level="project" />
|
||||
<orderEntry type="module-library">
|
||||
<library name="Maven: android:android:8.0.0">
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/libs/android.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
Binary file not shown.
@ -6,46 +6,151 @@
|
||||
|
||||
<groupId>com.farsunset</groupId>
|
||||
<artifactId>cim-android-sdk</artifactId>
|
||||
<version>3.8.2</version>
|
||||
<version>4.2.15</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.groupId}:${project.artifactId}</name>
|
||||
<description>Netty based instant messaging android sdk</description>
|
||||
<url>http://farsunset.com</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License</name>
|
||||
<url>http://www.apache.org/licenses/</url>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<connection>https://github.com/farsunset/cim.git</connection>
|
||||
<url>https://github.com/farsunset/cim</url>
|
||||
</scm>
|
||||
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<protobuf.lite.version>3.0.1</protobuf.lite.version>
|
||||
<google.protobuf.version>3.25.4</google.protobuf.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-lite</artifactId>
|
||||
<version>${protobuf.lite.version}</version>
|
||||
<artifactId>protobuf-javalite</artifactId>
|
||||
<version>${google.protobuf.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>android</groupId>
|
||||
<artifactId>android</artifactId>
|
||||
<scope>system</scope>
|
||||
<version>8.0.0</version>
|
||||
<version>15.0.0</version>
|
||||
<systemPath>${project.basedir}/libs/android.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Source -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<show>private</show>
|
||||
<nohelp>true</nohelp>
|
||||
<charset>UTF-8</charset>
|
||||
<encoding>UTF-8</encoding>
|
||||
<docencoding>UTF-8</docencoding>
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
<!-- TODO 临时解决不规范的javadoc生成报错,后面要规范化后把这行去掉 -->
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- GPG -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--Compiler -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<fork>true</fork>
|
||||
<verbose>true</verbose>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!--Release -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Jun Xia</name>
|
||||
<email>3979434@qq.com</email>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
</project>
|
1
cim-client-sdk/cim-android-sdk/release.bat
Normal file
1
cim-client-sdk/cim-android-sdk/release.bat
Normal file
@ -0,0 +1 @@
|
||||
mvn clean install deploy -P release
|
1
cim-client-sdk/cim-android-sdk/release.sh
Executable file
1
cim-client-sdk/cim-android-sdk/release.sh
Executable file
@ -0,0 +1 @@
|
||||
mvn clean install deploy -P release
|
@ -29,7 +29,7 @@ import android.net.Uri;
|
||||
|
||||
class CIMCacheManager {
|
||||
|
||||
public static final String KEY_ACCOUNT = "KEY_ACCOUNT";
|
||||
public static final String KEY_UID = "KEY_UID";
|
||||
|
||||
public static final String KEY_DEVICE_ID = "KEY_DEVICE_ID";
|
||||
|
||||
@ -43,8 +43,21 @@ class CIMCacheManager {
|
||||
|
||||
public static final String KEY_CIM_CONNECTION_STATE = "KEY_CIM_CONNECTION_STATE";
|
||||
|
||||
public static final String KEY_NTC_SWITCH = "KEY_NTC_SWITCH";
|
||||
|
||||
public static final String KEY_NTC_CHANNEL_NAME = "KEY_NTC_CHANNEL_NAME";
|
||||
|
||||
public static final String KEY_NTC_CHANNEL_MESSAGE = "KEY_NTC_CHANNEL_MESSAGE";
|
||||
|
||||
public static final String KEY_NTC_CHANNEL_ICON = "KEY_NTC_ICON";
|
||||
|
||||
public static final String CONTENT_URI = "content://%s.cim.provider";
|
||||
|
||||
static final String COLUMN_KEY = "key";
|
||||
|
||||
static final String COLUMN_VALUE = "value";
|
||||
|
||||
|
||||
|
||||
public static void remove(Context context, String key) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
@ -55,8 +68,8 @@ class CIMCacheManager {
|
||||
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("value", value);
|
||||
values.put("key", key);
|
||||
values.put(COLUMN_KEY, key);
|
||||
values.put(COLUMN_VALUE, value);
|
||||
resolver.insert(Uri.parse(String.format(CONTENT_URI, context.getPackageName())), values);
|
||||
|
||||
}
|
||||
@ -100,5 +113,4 @@ class CIMCacheManager {
|
||||
String value = getString(context, key);
|
||||
return value == null ? 0 : Integer.parseInt(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public class CIMCacheProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public int delete(Uri arg0, String key, String[] arg2) {
|
||||
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().remove(key).apply();
|
||||
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().remove(key).commit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -44,9 +44,9 @@ public class CIMCacheProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri arg0, ContentValues values) {
|
||||
String key = values.getAsString("key");
|
||||
String value = values.getAsString("value");
|
||||
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().putString(key, value).apply();
|
||||
String key = values.getAsString(CIMCacheManager.COLUMN_KEY);
|
||||
String value = values.getAsString(CIMCacheManager.COLUMN_VALUE);
|
||||
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().putString(key, value).commit();
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ public class CIMCacheProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri arg0, String[] arg1, String key, String[] arg3, String arg4) {
|
||||
MatrixCursor cursor = new MatrixCursor(new String[]{"value"});
|
||||
MatrixCursor cursor = new MatrixCursor(new String[]{CIMCacheManager.COLUMN_VALUE});
|
||||
String value = getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).getString(arg1[0], null);
|
||||
cursor.addRow(new Object[]{value});
|
||||
return cursor;
|
||||
|
@ -24,47 +24,45 @@ package com.farsunset.cim.sdk.android;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Process;
|
||||
import com.farsunset.cim.sdk.android.logger.CIMLogger;
|
||||
import com.farsunset.cim.sdk.android.coder.ClientMessageDecoder;
|
||||
import com.farsunset.cim.sdk.android.coder.ClientMessageEncoder;
|
||||
import com.farsunset.cim.sdk.android.constant.BundleKey;
|
||||
import com.farsunset.cim.sdk.android.constant.CIMConstant;
|
||||
import com.farsunset.cim.sdk.android.constant.IntentAction;
|
||||
import com.farsunset.cim.sdk.android.logger.CIMLogger;
|
||||
import com.farsunset.cim.sdk.android.model.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/*
|
||||
* 连接服务端管理,cim核心处理类,管理连接,以及消息处理
|
||||
*/
|
||||
class CIMConnectorManager {
|
||||
|
||||
private static CIMConnectorManager manager;
|
||||
class CIMConnectManager {
|
||||
|
||||
private static final int READ_BUFFER_SIZE = 2048;
|
||||
|
||||
private static final int WRITE_BUFFER_SIZE = 1024;
|
||||
|
||||
private static final int CONNECT_TIME_OUT = 10 * 1000;
|
||||
private static final int CONNECT_TIME_OUT = 5 * 1000;
|
||||
|
||||
/*
|
||||
服务端在连接写空闲120秒的时候发送心跳请求给客户端,所以客户端在空闲150秒后都没有收到任何数据,则关闭链接,并重新创建
|
||||
*/
|
||||
private static final int CONNECT_ALIVE_TIME_OUT = 150 * 1000;
|
||||
private static final int CONNECT_ALIVE_TIME_OUT = 120 * 1000;
|
||||
|
||||
private static final CIMLogger LOGGER = CIMLogger.getLogger();
|
||||
|
||||
private static final HandlerThread IDLE_HANDLER_THREAD = new HandlerThread("READ-IDLE", Process.THREAD_PRIORITY_BACKGROUND);
|
||||
|
||||
private volatile SocketChannel socketChannel;
|
||||
private SocketChannel socketChannel;
|
||||
|
||||
private final Context context;
|
||||
|
||||
@ -77,37 +75,27 @@ class CIMConnectorManager {
|
||||
private final ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
|
||||
private final ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
|
||||
|
||||
static {
|
||||
IDLE_HANDLER_THREAD.start();
|
||||
}
|
||||
private final Random random = new Random();
|
||||
|
||||
private CIMConnectorManager(Context context) {
|
||||
private final AtomicBoolean connecting = new AtomicBoolean(false);
|
||||
|
||||
public CIMConnectManager(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public synchronized static CIMConnectorManager getManager(Context context) {
|
||||
|
||||
if (manager == null) {
|
||||
manager = new CIMConnectorManager(context);
|
||||
}
|
||||
|
||||
return manager;
|
||||
|
||||
}
|
||||
|
||||
public void connect(final String host, final int port) {
|
||||
|
||||
if (!CIMPushManager.isNetworkConnected(context)) {
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
|
||||
intent.setAction(IntentAction.ACTION_CONNECT_FAILED);
|
||||
context.sendBroadcast(intent);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isConnected()) {
|
||||
if (isConnected() || connecting.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -123,6 +111,8 @@ class CIMConnectorManager {
|
||||
|
||||
try {
|
||||
|
||||
connecting.set(true);
|
||||
|
||||
socketChannel = SocketChannel.open();
|
||||
socketChannel.configureBlocking(true);
|
||||
socketChannel.socket().setTcpNoDelay(true);
|
||||
@ -134,6 +124,8 @@ class CIMConnectorManager {
|
||||
|
||||
handleConnectedEvent();
|
||||
|
||||
connecting.set(false);
|
||||
|
||||
/*
|
||||
*开始读取来自服务端的消息,先读取3个字节的消息头
|
||||
*/
|
||||
@ -146,10 +138,12 @@ class CIMConnectorManager {
|
||||
*/
|
||||
close();
|
||||
|
||||
} catch (ConnectException | SocketTimeoutException ignore) {
|
||||
handleConnectAbortedEvent();
|
||||
} catch (IOException ignore) {
|
||||
} catch (ConnectException | SocketTimeoutException | UnknownHostException exception) {
|
||||
handleConnectFailedEvent(exception);
|
||||
} catch (IOException exception) {
|
||||
handleDisconnectedEvent();
|
||||
}finally {
|
||||
connecting.set(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -160,19 +154,14 @@ class CIMConnectorManager {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
socketChannel.close();
|
||||
} catch (IOException ignore) {
|
||||
} finally {
|
||||
this.onSessionClosed();
|
||||
}
|
||||
this.closeForce();
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return socketChannel != null && socketChannel.isConnected();
|
||||
}
|
||||
|
||||
public void sendHeartbeat() {
|
||||
public void pong() {
|
||||
send(Pong.getInstance());
|
||||
}
|
||||
|
||||
@ -205,13 +194,22 @@ class CIMConnectorManager {
|
||||
|
||||
}
|
||||
|
||||
private void closeForce(){
|
||||
try {
|
||||
socketChannel.close();
|
||||
} catch (IOException ignore) {
|
||||
} finally {
|
||||
this.onSessionClosed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onSessionCreated() {
|
||||
LOGGER.sessionCreated(socketChannel);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FINISHED);
|
||||
intent.setAction(IntentAction.ACTION_CONNECT_FINISHED);
|
||||
context.sendBroadcast(intent);
|
||||
|
||||
}
|
||||
@ -224,7 +222,7 @@ class CIMConnectorManager {
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED);
|
||||
intent.setAction(IntentAction.ACTION_CONNECTION_CLOSED);
|
||||
context.sendBroadcast(intent);
|
||||
|
||||
}
|
||||
@ -243,7 +241,7 @@ class CIMConnectorManager {
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED);
|
||||
intent.setAction(IntentAction.ACTION_MESSAGE_RECEIVED);
|
||||
intent.putExtra(Message.class.getName(), (Message) obj);
|
||||
context.sendBroadcast(intent);
|
||||
|
||||
@ -252,7 +250,7 @@ class CIMConnectorManager {
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_REPLY_RECEIVED);
|
||||
intent.setAction(IntentAction.ACTION_REPLY_RECEIVED);
|
||||
intent.putExtra(ReplyBody.class.getName(), (ReplyBody) obj);
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
@ -266,33 +264,45 @@ class CIMConnectorManager {
|
||||
if (message instanceof SentBody) {
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_SEND_FINISHED);
|
||||
intent.setAction(IntentAction.ACTION_SEND_FINISHED);
|
||||
intent.putExtra(SentBody.class.getName(), (SentBody) message);
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private final Handler idleHandler = new Handler(IDLE_HANDLER_THREAD.getLooper()) {
|
||||
private final Handler idleHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(android.os.Message m) {
|
||||
onSessionIdle();
|
||||
workerExecutor.execute(CIMConnectManager.this::onSessionIdle);
|
||||
}
|
||||
};
|
||||
|
||||
private void handleDisconnectedEvent() {
|
||||
close();
|
||||
closeForce();
|
||||
}
|
||||
|
||||
private void handleConnectAbortedEvent() {
|
||||
private void handleConnectFailedEvent(Exception exception) {
|
||||
|
||||
long interval = CIMConstant.RECONNECT_INTERVAL_TIME - (5 * 1000 - new Random().nextInt(15 * 1000));
|
||||
long retryAfter;
|
||||
|
||||
LOGGER.connectFailure(interval);
|
||||
if (exception instanceof UnknownHostException){
|
||||
/*
|
||||
* 通常是网络由WIFI切换为移动网出现这个异常
|
||||
*/
|
||||
retryAfter = 3000L;
|
||||
}else {
|
||||
/*
|
||||
* 随机3-10秒后重连
|
||||
*/
|
||||
retryAfter = 3000L + random.nextInt(7001) ;
|
||||
}
|
||||
|
||||
LOGGER.connectFailure(retryAfter);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
|
||||
intent.putExtra("interval", interval);
|
||||
intent.setAction(IntentAction.ACTION_CONNECT_FAILED);
|
||||
intent.putExtra(BundleKey.KEY_RECONNECT_AFTER, retryAfter);
|
||||
context.sendBroadcast(intent);
|
||||
|
||||
}
|
@ -25,8 +25,10 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Build;
|
||||
import com.farsunset.cim.sdk.android.constant.BundleKey;
|
||||
import com.farsunset.cim.sdk.android.constant.CIMConstant;
|
||||
import com.farsunset.cim.sdk.android.constant.IntentAction;
|
||||
import com.farsunset.cim.sdk.android.constant.ServiceAction;
|
||||
import com.farsunset.cim.sdk.android.model.Message;
|
||||
import com.farsunset.cim.sdk.android.model.ReplyBody;
|
||||
import com.farsunset.cim.sdk.android.model.SentBody;
|
||||
@ -38,7 +40,6 @@ public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
protected Context context;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
@ -50,6 +51,7 @@ public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
|
||||
* 操作事件广播,用于提高service存活率
|
||||
*/
|
||||
if (Intent.ACTION_USER_PRESENT.equals(action)
|
||||
|| Intent.ACTION_BOOT_COMPLETED.equals(action)
|
||||
|| Intent.ACTION_POWER_CONNECTED.equals(action)
|
||||
|| Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
|
||||
startPushService();
|
||||
@ -58,7 +60,7 @@ public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
|
||||
/*
|
||||
* 设备网络状态变化事件
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_NETWORK_CHANGED.equals(action)
|
||||
if (IntentAction.ACTION_NETWORK_CHANGED.equals(action)
|
||||
|| ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
|
||||
|
||||
onDevicesNetworkChanged();
|
||||
@ -67,36 +69,36 @@ public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
|
||||
/*
|
||||
* cim断开服务器事件
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED.equals(action)) {
|
||||
if (IntentAction.ACTION_CONNECTION_CLOSED.equals(action)) {
|
||||
onInnerConnectionClosed();
|
||||
}
|
||||
|
||||
/*
|
||||
* cim连接服务器失败事件
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_CONNECT_FAILED.equals(action)) {
|
||||
long interval = intent.getLongExtra("interval", CIMConstant.RECONNECT_INTERVAL_TIME);
|
||||
if (IntentAction.ACTION_CONNECT_FAILED.equals(action)) {
|
||||
long interval = intent.getLongExtra(BundleKey.KEY_RECONNECT_AFTER, CIMConstant.RECONNECT_INTERVAL_TIME);
|
||||
onInnerConnectFailed(interval);
|
||||
}
|
||||
|
||||
/*
|
||||
* cim连接服务器成功事件
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_CONNECT_FINISHED.equals(action)) {
|
||||
if (IntentAction.ACTION_CONNECT_FINISHED.equals(action)) {
|
||||
onInnerConnectFinished();
|
||||
}
|
||||
|
||||
/*
|
||||
* 收到推送消息事件
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED.equals(action)) {
|
||||
if (IntentAction.ACTION_MESSAGE_RECEIVED.equals(action)) {
|
||||
onInnerMessageReceived((Message) intent.getSerializableExtra(Message.class.getName()), intent);
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取收到replyBody成功事件
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_REPLY_RECEIVED.equals(action)) {
|
||||
if (IntentAction.ACTION_REPLY_RECEIVED.equals(action)) {
|
||||
onReplyReceived((ReplyBody) intent.getSerializableExtra(ReplyBody.class.getName()));
|
||||
}
|
||||
|
||||
@ -104,49 +106,36 @@ public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
|
||||
/*
|
||||
* 获取sendBody发送成功事件
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_SEND_FINISHED.equals(action)) {
|
||||
if (IntentAction.ACTION_SEND_FINISHED.equals(action)) {
|
||||
onSentSucceed((SentBody) intent.getSerializableExtra(SentBody.class.getName()));
|
||||
}
|
||||
|
||||
/*
|
||||
* 重新连接,如果断开的话
|
||||
*/
|
||||
if (CIMConstant.IntentAction.ACTION_CONNECTION_RECOVERY.equals(action)) {
|
||||
if (IntentAction.ACTION_CONNECTION_RECOVERY.equals(action)) {
|
||||
connect(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void startPushService() {
|
||||
|
||||
Intent intent = new Intent(context, CIMPushService.class);
|
||||
intent.setAction(CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
intent.setAction(ServiceAction.ACTION_ACTIVATE_PUSH_SERVICE);
|
||||
CIMPushManager.startService(context,intent);
|
||||
}
|
||||
|
||||
private void onInnerConnectionClosed() {
|
||||
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
|
||||
|
||||
if (CIMPushManager.isNetworkConnected(context)) {
|
||||
connect(0);
|
||||
}
|
||||
|
||||
connect(0L);
|
||||
onConnectionClosed();
|
||||
}
|
||||
|
||||
private void onInnerConnectFailed(long interval) {
|
||||
|
||||
if (CIMPushManager.isNetworkConnected(context)) {
|
||||
onConnectFailed();
|
||||
|
||||
onConnectFailed();
|
||||
connect(interval);
|
||||
|
||||
connect(interval);
|
||||
}
|
||||
}
|
||||
|
||||
private void onInnerConnectFinished() {
|
||||
@ -157,18 +146,13 @@ public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private void onDevicesNetworkChanged() {
|
||||
|
||||
if (CIMPushManager.isNetworkConnected(context)) {
|
||||
connect(0);
|
||||
}
|
||||
|
||||
onNetworkChanged();
|
||||
}
|
||||
|
||||
private void connect(long delay) {
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.putExtra(CIMPushService.KEY_DELAYED_TIME, delay);
|
||||
serviceIntent.setAction(CIMPushManager.ACTION_CREATE_CIM_CONNECTION);
|
||||
serviceIntent.putExtra(BundleKey.KEY_DELAYED_TIME, delay);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_CREATE_CONNECTION);
|
||||
CIMPushManager.startService(context, serviceIntent);
|
||||
}
|
||||
|
||||
@ -181,13 +165,11 @@ public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
private boolean isForceOfflineMessage(String action) {
|
||||
return CIMConstant.MessageAction.ACTION_999.equals(action);
|
||||
return CIMConstant.ACTION_999.equals(action);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 接收消息实现方法
|
||||
*
|
||||
* @param message
|
||||
* @param intent
|
||||
*/
|
||||
|
@ -33,35 +33,30 @@ public interface CIMEventListener {
|
||||
|
||||
/**
|
||||
* 当收到服务端推送过来的消息时调用
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
void onMessageReceived(Message message);
|
||||
|
||||
/**
|
||||
* 当调用CIMPushManager.sendRequest()向服务端发送请求,获得相应时调用
|
||||
*
|
||||
* 当调用CIMPushManager.sendRequest()向服务端发送请求,获得服务端响应时调用
|
||||
* @param body
|
||||
*/
|
||||
void onReplyReceived(ReplyBody body);
|
||||
|
||||
/**
|
||||
* 当调用CIMPushManager.sendRequest()向服务端发送请求成功时
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
void onSendFinished(SentBody body);
|
||||
|
||||
/**
|
||||
* 当手机网络发生变化时调用
|
||||
*
|
||||
* @param info
|
||||
*/
|
||||
void onNetworkChanged(NetworkInfo info);
|
||||
|
||||
/**
|
||||
* 当连接服务器成功时回调
|
||||
*
|
||||
* @param hasAutoBind true 已经自动绑定账号到服务器了,不需要再手动调用bindAccount
|
||||
*/
|
||||
void onConnectFinished(boolean hasAutoBind);
|
||||
@ -82,4 +77,5 @@ public interface CIMEventListener {
|
||||
* @return 排序 值越大优先级越高
|
||||
*/
|
||||
int getEventDispatchOrder();
|
||||
|
||||
}
|
||||
|
@ -22,22 +22,21 @@
|
||||
package com.farsunset.cim.sdk.android;
|
||||
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
import com.farsunset.cim.sdk.android.model.Message;
|
||||
import com.farsunset.cim.sdk.android.model.ReplyBody;
|
||||
import com.farsunset.cim.sdk.android.model.SentBody;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CIM 消息监听器管理
|
||||
*/
|
||||
public class CIMListenerManager {
|
||||
|
||||
private static final ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>();
|
||||
private static final List<CIMEventListener> cimListeners = new LinkedList<>();
|
||||
private static final ReceiveComparator comparator = new ReceiveComparator();
|
||||
|
||||
private CIMListenerManager() {
|
||||
@ -48,70 +47,57 @@ public class CIMListenerManager {
|
||||
|
||||
if (!cimListeners.contains(listener)) {
|
||||
cimListeners.add(listener);
|
||||
Collections.sort(cimListeners, comparator);
|
||||
Collections.sort(cimListeners,comparator);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeMessageListener(CIMEventListener listener) {
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
CIMEventListener target = iterable.next();
|
||||
if (listener.getClass() == target.getClass()) {
|
||||
iterable.remove();
|
||||
}
|
||||
}
|
||||
cimListeners.remove(listener);
|
||||
}
|
||||
|
||||
public static void notifyOnNetworkChanged(NetworkInfo info) {
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
iterable.next().onNetworkChanged(info);
|
||||
for (CIMEventListener cimListener : getListeners()) {
|
||||
cimListener.onNetworkChanged(info);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyOnConnectFinished(boolean hasAutoBind) {
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
iterable.next().onConnectFinished(hasAutoBind);
|
||||
for (CIMEventListener cimListener : getListeners()) {
|
||||
cimListener.onConnectFinished(hasAutoBind);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyOnMessageReceived(Message message) {
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
iterable.next().onMessageReceived(message);
|
||||
for (CIMEventListener cimListener : getListeners()) {
|
||||
cimListener.onMessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyOnConnectionClosed() {
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
iterable.next().onConnectionClosed();
|
||||
for (CIMEventListener cimListener : getListeners()) {
|
||||
cimListener.onConnectionClosed();
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyOnConnectFailed() {
|
||||
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
iterable.next().onConnectFailed();
|
||||
for (CIMEventListener cimListener : getListeners()) {
|
||||
cimListener.onConnectFailed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void notifyOnReplyReceived(ReplyBody body) {
|
||||
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
iterable.next().onReplyReceived(body);
|
||||
for (CIMEventListener cimListener : getListeners()) {
|
||||
cimListener.onReplyReceived(body);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void notifyOnSendFinished(SentBody body) {
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
iterable.next().onSendFinished(body);
|
||||
for (CIMEventListener cimListener : getListeners()) {
|
||||
cimListener.onSendFinished(body);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,11 +105,8 @@ public class CIMListenerManager {
|
||||
cimListeners.clear();
|
||||
}
|
||||
|
||||
public static void logListenersName() {
|
||||
Iterator<CIMEventListener> iterable = cimListeners.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
Log.i(CIMEventListener.class.getSimpleName(), "#######" + iterable.next().getClass().getName() + "#######");
|
||||
}
|
||||
public static List<CIMEventListener> getListeners() {
|
||||
return new LinkedList<>(cimListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,7 +116,6 @@ public class CIMListenerManager {
|
||||
|
||||
@Override
|
||||
public int compare(CIMEventListener arg1, CIMEventListener arg2) {
|
||||
|
||||
int order1 = arg1.getEventDispatchOrder();
|
||||
int order2 = arg2.getEventDispatchOrder();
|
||||
return Integer.compare(order2, order1);
|
||||
|
@ -28,39 +28,29 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.text.TextUtils;
|
||||
import com.farsunset.cim.sdk.android.constant.BundleKey;
|
||||
import com.farsunset.cim.sdk.android.constant.IntentAction;
|
||||
import com.farsunset.cim.sdk.android.constant.RequestKey;
|
||||
import com.farsunset.cim.sdk.android.constant.ServiceAction;
|
||||
import com.farsunset.cim.sdk.android.logger.CIMLogger;
|
||||
import com.farsunset.cim.sdk.android.constant.CIMConstant;
|
||||
import com.farsunset.cim.sdk.android.model.SentBody;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* CIM 功能接口
|
||||
* CIM客户端sdk功能接口
|
||||
*/
|
||||
public class CIMPushManager {
|
||||
|
||||
|
||||
protected static final String ACTION_CREATE_CIM_CONNECTION = "ACTION_CREATE_CIM_CONNECTION";
|
||||
|
||||
protected static final String ACTION_DESTROY_CIM_SERVICE = "ACTION_DESTROY_CIM_SERVICE";
|
||||
|
||||
protected static final String ACTION_ACTIVATE_PUSH_SERVICE = "ACTION_ACTIVATE_PUSH_SERVICE";
|
||||
|
||||
protected static final String ACTION_SEND_REQUEST_BODY = "ACTION_SEND_REQUEST_BODY";
|
||||
|
||||
protected static final String ACTION_CLOSE_CIM_CONNECTION = "ACTION_CLOSE_CIM_CONNECTION";
|
||||
|
||||
protected static final String ACTION_SET_LOGGER_EATABLE = "ACTION_SET_LOGGER_EATABLE";
|
||||
|
||||
protected static final String ACTION_SHOW_PERSIST_NOTIFICATION = "ACTION_SHOW_PERSIST_NOTIFICATION";
|
||||
|
||||
protected static final String ACTION_HIDE_PERSIST_NOTIFICATION = "ACTION_HIDE_PERSIST_NOTIFICATION";
|
||||
|
||||
protected static final String ACTION_CIM_CONNECTION_PONG = "ACTION_CIM_CONNECTION_PONG";
|
||||
|
||||
/**
|
||||
* 初始化,连接服务端,在程序启动页或者 在Application里调用
|
||||
* @param context
|
||||
* @param host cim服务端IP或者域名
|
||||
* @param port cim服务端端口
|
||||
*/
|
||||
public static void connect(Context context, String host, int port) {
|
||||
|
||||
@ -69,100 +59,160 @@ public class CIMPushManager {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CIMCacheManager.putString(context, CIMCacheManager.KEY_CIM_SERVER_HOST, host);
|
||||
CIMCacheManager.putInt(context, CIMCacheManager.KEY_CIM_SERVER_PORT, port);
|
||||
|
||||
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, false);
|
||||
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
|
||||
|
||||
CIMCacheManager.remove(context, CIMCacheManager.KEY_ACCOUNT);
|
||||
CIMCacheManager.remove(context, CIMCacheManager.KEY_UID);
|
||||
|
||||
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.setAction(ACTION_CREATE_CIM_CONNECTION);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_CREATE_CONNECTION);
|
||||
startService(context, serviceIntent);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置SDK日志打印开关
|
||||
* @param context
|
||||
* @param enable
|
||||
*/
|
||||
public static void setLoggerEnable(Context context, boolean enable) {
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.putExtra(CIMPushService.KEY_LOGGER_ENABLE, enable);
|
||||
serviceIntent.setAction(ACTION_SET_LOGGER_EATABLE);
|
||||
serviceIntent.putExtra(BundleKey.KEY_LOGGER_ENABLE, enable);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_SET_LOGGER_EATABLE);
|
||||
startService(context, serviceIntent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 开启常驻通知栏
|
||||
* @param context
|
||||
* @param icon 通知图标
|
||||
* @param channel 通知channel
|
||||
* @param message 显示内容
|
||||
*/
|
||||
public static void startForeground(Context context,int icon, String channel , String message) {
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.putExtra(CIMPushService.KEY_NOTIFICATION_MESSAGE, message);
|
||||
serviceIntent.putExtra(CIMPushService.KEY_NOTIFICATION_CHANNEL, channel);
|
||||
serviceIntent.putExtra(CIMPushService.KEY_NOTIFICATION_ICON, icon);
|
||||
serviceIntent.setAction(ACTION_SHOW_PERSIST_NOTIFICATION);
|
||||
startService(context, serviceIntent);
|
||||
}
|
||||
|
||||
public static void cancelForeground(Context context) {
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.setAction(ACTION_HIDE_PERSIST_NOTIFICATION);
|
||||
serviceIntent.putExtra(BundleKey.KEY_NOTIFICATION_MESSAGE, message);
|
||||
serviceIntent.putExtra(BundleKey.KEY_NOTIFICATION_CHANNEL, channel);
|
||||
serviceIntent.putExtra(BundleKey.KEY_NOTIFICATION_ICON, icon);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_SHOW_PERSIST_NOTIFICATION);
|
||||
startService(context, serviceIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置一个账号登录到服务端
|
||||
* 关闭常驻通知栏
|
||||
* @param context
|
||||
*/
|
||||
public static void bindAccount(Context context, String account) {
|
||||
public static void cancelForeground(Context context) {
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_HIDE_PERSIST_NOTIFICATION);
|
||||
startService(context, serviceIntent);
|
||||
}
|
||||
|
||||
if (isDestroyed(context) || account == null || account.trim().length() == 0) {
|
||||
/**
|
||||
* bind账户
|
||||
* 通知服务端 长连接和uid进行关联
|
||||
* @param context
|
||||
* @param uid 用户标识
|
||||
*/
|
||||
public static void bind(Context context, long uid) {
|
||||
bind(context,String.valueOf(uid));
|
||||
}
|
||||
|
||||
/**
|
||||
* bind账户
|
||||
* 通知服务端 长连接和uid进行关联
|
||||
* @param context
|
||||
* @param uid 用户标识
|
||||
*/
|
||||
public static void bind(Context context, String uid) {
|
||||
|
||||
if (isDestroyed(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendBindRequest(context, account);
|
||||
sendBindRequest(context, uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知服务端给当前长连接设置tag
|
||||
* @param context
|
||||
* @param tag 标识
|
||||
*/
|
||||
public static void setTag(Context context, String tag) {
|
||||
|
||||
SentBody sent = new SentBody();
|
||||
sent.setKey(RequestKey.CLIENT_SET_TAG);
|
||||
sent.put("tag", tag);
|
||||
sendRequest(context, sent);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知服务端清除tag
|
||||
* @param context
|
||||
*/
|
||||
public static void removeTag(Context context) {
|
||||
|
||||
SentBody sent = new SentBody();
|
||||
sent.setKey(RequestKey.CLIENT_REMOVE_TAG);
|
||||
sendRequest(context, sent);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 长连接发送一次心跳响应
|
||||
* @param context
|
||||
*/
|
||||
public static void pong(Context context) {
|
||||
if (isDestroyed(context) || isStopped(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.setAction(ACTION_CIM_CONNECTION_PONG);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_CREATE_CONNECTION);
|
||||
startService(context, serviceIntent);
|
||||
}
|
||||
|
||||
private static void sendBindRequest(Context context, String account) {
|
||||
|
||||
|
||||
private static void sendBindRequest(Context context, String uid) {
|
||||
|
||||
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
|
||||
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, account);
|
||||
|
||||
CIMCacheManager.putString(context, CIMCacheManager.KEY_UID, uid);
|
||||
|
||||
SentBody sent = new SentBody();
|
||||
sent.setKey(CIMConstant.RequestKey.CLIENT_BIND);
|
||||
sent.put("account", account);
|
||||
sent.put("deviceId", getDeviceId(context));
|
||||
sent.setKey(RequestKey.CLIENT_BIND);
|
||||
sent.put("uid", String.valueOf(uid));
|
||||
sent.put("channel", "android");
|
||||
sent.put("device", Build.MODEL);
|
||||
sent.put("deviceId", getDeviceId(context));
|
||||
sent.put("deviceName", Build.MODEL);
|
||||
sent.put("appVersion", getVersionName(context));
|
||||
sent.put("osVersion", Build.VERSION.RELEASE);
|
||||
sent.put("packageName", context.getPackageName());
|
||||
sent.put("language", getLanguage());
|
||||
sent.setTimestamp(System.currentTimeMillis());
|
||||
sendRequest(context, sent);
|
||||
}
|
||||
|
||||
protected static boolean autoBindAccount(Context context) {
|
||||
|
||||
String account = CIMCacheManager.getString(context, CIMCacheManager.KEY_ACCOUNT);
|
||||
if (account == null || account.trim().length() == 0 || isDestroyed(context)) {
|
||||
String uid = CIMCacheManager.getString(context, CIMCacheManager.KEY_UID);
|
||||
|
||||
if (uid == null || isDestroyed(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sendBindRequest(context, account);
|
||||
sendBindRequest(context, uid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送一个CIM请求
|
||||
* 向服务端发送一次自定义业务请求
|
||||
* @param context
|
||||
* @param body 请求体
|
||||
*/
|
||||
public static void sendRequest(Context context, SentBody body) {
|
||||
|
||||
@ -171,8 +221,8 @@ public class CIMPushManager {
|
||||
}
|
||||
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.putExtra(CIMPushService.KEY_SEND_BODY, body);
|
||||
serviceIntent.setAction(ACTION_SEND_REQUEST_BODY);
|
||||
serviceIntent.putExtra(BundleKey.KEY_SEND_BODY, body);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_SEND_REQUEST_BODY);
|
||||
startService(context, serviceIntent);
|
||||
|
||||
}
|
||||
@ -189,7 +239,7 @@ public class CIMPushManager {
|
||||
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, true);
|
||||
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.setAction(ACTION_CLOSE_CIM_CONNECTION);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_CREATE_CONNECTION);
|
||||
startService(context, serviceIntent);
|
||||
|
||||
}
|
||||
@ -200,10 +250,10 @@ public class CIMPushManager {
|
||||
public static void destroy(Context context) {
|
||||
|
||||
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, true);
|
||||
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, null);
|
||||
CIMCacheManager.remove(context, CIMCacheManager.KEY_UID);
|
||||
|
||||
Intent serviceIntent = new Intent(context, CIMPushService.class);
|
||||
serviceIntent.setAction(ACTION_DESTROY_CIM_SERVICE);
|
||||
serviceIntent.setAction(ServiceAction.ACTION_CREATE_CONNECTION);
|
||||
startService(context, serviceIntent);
|
||||
|
||||
}
|
||||
@ -220,33 +270,59 @@ public class CIMPushManager {
|
||||
autoBindAccount(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取sdk是否已经销毁的
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean isDestroyed(Context context) {
|
||||
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否暂停接收消息
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean isStopped(Context context) {
|
||||
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_MANUAL_STOP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断于服务端连接是否正常
|
||||
* @param context
|
||||
*/
|
||||
public static boolean isConnected(Context context) {
|
||||
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断客户端网络连接是否正常
|
||||
* @param context
|
||||
*/
|
||||
public static boolean isNetworkConnected(Context context) {
|
||||
NetworkInfo networkInfo = getNetworkInfo(context);
|
||||
return networkInfo != null && networkInfo.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务端网络信息
|
||||
* @param context
|
||||
*/
|
||||
public static NetworkInfo getNetworkInfo(Context context) {
|
||||
return ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
|
||||
}
|
||||
|
||||
|
||||
public static void startService(Context context, Intent intent) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
protected static void startService(Context context, Intent intent) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
context.startService(intent);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
context.startForegroundService(intent);
|
||||
}catch (Exception ignore){
|
||||
context.sendBroadcast(new Intent(IntentAction.ACTION_CONNECTION_RECOVERY));
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,8 +332,7 @@ public class CIMPushManager {
|
||||
try {
|
||||
PackageInfo mPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
return mPackageInfo.versionName;
|
||||
} catch (NameNotFoundException ignore) {
|
||||
}
|
||||
} catch (NameNotFoundException ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -276,4 +351,10 @@ public class CIMPushManager {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
private static String getLanguage(){
|
||||
|
||||
Locale locale = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? LocaleList.getDefault().get(0) : Locale.getDefault();
|
||||
|
||||
return locale.getLanguage() + "-" + locale.getCountry();
|
||||
}
|
||||
}
|
||||
|
@ -26,18 +26,22 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import com.farsunset.cim.sdk.android.constant.CIMConstant;
|
||||
import com.farsunset.cim.sdk.android.constant.BundleKey;
|
||||
import com.farsunset.cim.sdk.android.constant.IntentAction;
|
||||
import com.farsunset.cim.sdk.android.constant.ServiceAction;
|
||||
import com.farsunset.cim.sdk.android.logger.CIMLogger;
|
||||
import com.farsunset.cim.sdk.android.model.Pong;
|
||||
import com.farsunset.cim.sdk.android.model.SentBody;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* 与服务端连接服务
|
||||
@ -46,63 +50,44 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
*/
|
||||
public class CIMPushService extends Service {
|
||||
|
||||
static final String KEY_SEND_BODY = "KEY_SEND_BODY";
|
||||
static final String KEY_DELAYED_TIME = "KEY_DELAYED_TIME";
|
||||
static final String KEY_LOGGER_ENABLE = "KEY_LOGGER_ENABLE";
|
||||
static final String KEY_NOTIFICATION_MESSAGE = "KEY_NOTIFICATION_MESSAGE";
|
||||
static final String KEY_NOTIFICATION_CHANNEL = "KEY_NOTIFICATION_CHANNEL";
|
||||
static final String KEY_NOTIFICATION_ICON = "KEY_NOTIFICATION_ICON";
|
||||
|
||||
private static final String TRANSIENT_NTC_CHANNEL_ID = "CIM_PUSH_TRANSIENT_NTC_ID";
|
||||
private static final String PERSIST_NTC_CHANNEL_ID = "CIM_PUSH_PERSIST_NTC_ID";
|
||||
|
||||
private static final String TRANSIENT_NTC_CHANNEL_ID = "PUSH_TRANSIENT_NTC_ID";
|
||||
private static final String PERSIST_NTC_CHANNEL_ID = "PUSH_PERSIST_NTC_ID";
|
||||
private static final int NOTIFICATION_ID = Integer.MAX_VALUE;
|
||||
|
||||
private static final int PERSIST_NOTIFICATION_ID = Integer.MIN_VALUE;
|
||||
private CIMConnectManager connectManager;
|
||||
|
||||
private CIMConnectorManager connectorManager;
|
||||
private KeepAliveBroadcastReceiver keepAliveReceiver;
|
||||
private ConnectivityManager connectivityManager;
|
||||
private NotificationManager notificationManager;
|
||||
private final AtomicBoolean persistHolder = new AtomicBoolean(false);
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
connectorManager = CIMConnectorManager.getManager(this.getApplicationContext());
|
||||
|
||||
connectManager = new CIMConnectManager(this);
|
||||
|
||||
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
keepAliveReceiver = new KeepAliveBroadcastReceiver();
|
||||
registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter());
|
||||
}
|
||||
keepAliveReceiver = new KeepAliveBroadcastReceiver();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
this.registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter());
|
||||
|
||||
connectivityManager = getSystemService(ConnectivityManager.class);
|
||||
connectivityManager = getSystemService(ConnectivityManager.class);
|
||||
|
||||
connectivityManager.registerDefaultNetworkCallback(networkCallback);
|
||||
connectivityManager.registerDefaultNetworkCallback(networkCallback);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private final ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
|
||||
sendBroadcast(intent);
|
||||
sendBroadcast(new Intent(IntentAction.ACTION_NETWORK_CHANGED).setPackage(getPackageName()));
|
||||
handleKeepAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnavailable() {
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(getPackageName());
|
||||
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
|
||||
sendBroadcast(intent);
|
||||
public void onLost(Network network) {
|
||||
sendBroadcast(new Intent(IntentAction.ACTION_NETWORK_CHANGED).setPackage(getPackageName()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final Handler connectHandler = new Handler() {
|
||||
@ -115,67 +100,82 @@ public class CIMPushService extends Service {
|
||||
private final Handler notificationHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(android.os.Message message) {
|
||||
if (persistHolder.get()){
|
||||
return;
|
||||
if (!CIMCacheManager.getBoolean(CIMPushService.this,CIMCacheManager.KEY_NTC_SWITCH)){
|
||||
stopForeground(true);
|
||||
}
|
||||
stopForeground(true);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
return super.registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter(),Context.RECEIVER_EXPORTED);
|
||||
}else {
|
||||
return super.registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter());
|
||||
}
|
||||
}
|
||||
|
||||
private void startForegroundNotification(int id, Notification notification) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(id,notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING);
|
||||
}else {
|
||||
startForeground(id,notification);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
String action = intent == null ? CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE : intent.getAction();
|
||||
Intent newIntent = intent == null ? new Intent(ServiceAction.ACTION_ACTIVATE_PUSH_SERVICE) : intent;
|
||||
|
||||
if (!persistHolder.get()) {
|
||||
createNotification();
|
||||
String action = newIntent.getAction();
|
||||
|
||||
createTransientNotification();
|
||||
|
||||
if (ServiceAction.ACTION_CREATE_CONNECTION.equals(action)) {
|
||||
this.prepareConnect(newIntent.getLongExtra(BundleKey.KEY_DELAYED_TIME, 0));
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_CREATE_CIM_CONNECTION.equals(action)) {
|
||||
this.prepareConnect(intent.getLongExtra(KEY_DELAYED_TIME, 0));
|
||||
if (ServiceAction.ACTION_SEND_REQUEST_BODY.equals(action)) {
|
||||
connectManager.send((SentBody) newIntent.getSerializableExtra(BundleKey.KEY_SEND_BODY));
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_SEND_REQUEST_BODY.equals(action)) {
|
||||
connectorManager.send((SentBody) intent.getSerializableExtra(KEY_SEND_BODY));
|
||||
if (ServiceAction.ACTION_CLOSE_CONNECTION.equals(action)) {
|
||||
connectManager.close();
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_CLOSE_CIM_CONNECTION.equals(action)) {
|
||||
connectorManager.close();
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE.equals(action)) {
|
||||
if (ServiceAction.ACTION_ACTIVATE_PUSH_SERVICE.equals(action)) {
|
||||
handleKeepAlive();
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_DESTROY_CIM_SERVICE.equals(action)) {
|
||||
connectorManager.close();
|
||||
if (ServiceAction.ACTION_DESTROY_SERVICE.equals(action)) {
|
||||
connectManager.close();
|
||||
this.stopSelf();
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_CIM_CONNECTION_PONG.equals(action)) {
|
||||
connectorManager.send(Pong.getInstance());
|
||||
if (ServiceAction.ACTION_CONNECTION_PONG.equals(action)) {
|
||||
connectManager.send(Pong.getInstance());
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_SET_LOGGER_EATABLE.equals(action)) {
|
||||
boolean enable = intent.getBooleanExtra(KEY_LOGGER_ENABLE, true);
|
||||
if (ServiceAction.ACTION_SET_LOGGER_EATABLE.equals(action)) {
|
||||
boolean enable = newIntent.getBooleanExtra(BundleKey.KEY_LOGGER_ENABLE, true);
|
||||
CIMLogger.getLogger().debugMode(enable);
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_SHOW_PERSIST_NOTIFICATION.equals(action)) {
|
||||
createPersistNotification(intent.getStringExtra(KEY_NOTIFICATION_CHANNEL),
|
||||
intent.getStringExtra(KEY_NOTIFICATION_MESSAGE),
|
||||
intent.getIntExtra(KEY_NOTIFICATION_ICON,0));
|
||||
persistHolder.set(true);
|
||||
if (ServiceAction.ACTION_HIDE_PERSIST_NOTIFICATION.equals(action)) {
|
||||
this.stopForeground(true);
|
||||
CIMCacheManager.putBoolean(this, CIMCacheManager.KEY_NTC_SWITCH,false);
|
||||
}
|
||||
|
||||
if (CIMPushManager.ACTION_HIDE_PERSIST_NOTIFICATION.equals(action)) {
|
||||
stopForeground(true);
|
||||
persistHolder.set(false);
|
||||
if (ServiceAction.ACTION_SHOW_PERSIST_NOTIFICATION.equals(action)) {
|
||||
createPersistNotification(newIntent.getStringExtra(BundleKey.KEY_NOTIFICATION_CHANNEL),
|
||||
newIntent.getStringExtra(BundleKey.KEY_NOTIFICATION_MESSAGE),
|
||||
newIntent.getIntExtra(BundleKey.KEY_NOTIFICATION_ICON,0));
|
||||
|
||||
return super.onStartCommand(intent,flags,startId);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notificationHandler.sendEmptyMessageDelayed(0, 200);
|
||||
}
|
||||
notificationHandler.sendEmptyMessageDelayed(0, 200);
|
||||
|
||||
return super.onStartCommand(intent,flags,startId);
|
||||
}
|
||||
@ -187,6 +187,8 @@ public class CIMPushService extends Service {
|
||||
return;
|
||||
}
|
||||
|
||||
connectHandler.removeMessages(0);
|
||||
|
||||
connectHandler.sendEmptyMessageDelayed(0, delayMillis);
|
||||
}
|
||||
|
||||
@ -199,12 +201,12 @@ public class CIMPushService extends Service {
|
||||
String host = CIMCacheManager.getString(this, CIMCacheManager.KEY_CIM_SERVER_HOST);
|
||||
int port = CIMCacheManager.getInt(this, CIMCacheManager.KEY_CIM_SERVER_PORT);
|
||||
|
||||
if (host == null || host.trim().length() == 0 || port <= 0) {
|
||||
if (host == null || host.trim().isEmpty() || port <= 0) {
|
||||
Log.e(this.getClass().getSimpleName(), "Invalid hostname or port. host:" + host + " port:" + port);
|
||||
return;
|
||||
}
|
||||
|
||||
connectorManager.connect(host, port);
|
||||
connectManager.connect(host, port);
|
||||
|
||||
}
|
||||
|
||||
@ -212,8 +214,8 @@ public class CIMPushService extends Service {
|
||||
|
||||
CIMLogger.getLogger().connectState(true, CIMPushManager.isStopped(this), CIMPushManager.isDestroyed(this));
|
||||
|
||||
if (connectorManager.isConnected()) {
|
||||
connectorManager.sendHeartbeat();
|
||||
if (connectManager.isConnected()) {
|
||||
connectManager.pong();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -228,32 +230,31 @@ public class CIMPushService extends Service {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
release();
|
||||
}
|
||||
|
||||
private void release() {
|
||||
|
||||
connectHandler.removeMessages(0);
|
||||
|
||||
stopForeground(true);
|
||||
|
||||
persistHolder.set(false);
|
||||
unregisterReceiver(keepAliveReceiver);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
unregisterReceiver(keepAliveReceiver);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
connectivityManager.unregisterNetworkCallback(networkCallback);
|
||||
}
|
||||
connectivityManager.unregisterNetworkCallback(networkCallback);
|
||||
}
|
||||
|
||||
private void createNotification() {
|
||||
private void createTransientNotification() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
if (notificationManager.getNotificationChannel(PERSIST_NTC_CHANNEL_ID) != null) {
|
||||
|
||||
int icon = CIMCacheManager.getInt(this, CIMCacheManager.KEY_NTC_CHANNEL_ICON);
|
||||
String title = CIMCacheManager.getString(this, CIMCacheManager.KEY_NTC_CHANNEL_NAME);
|
||||
String message = CIMCacheManager.getString(this, CIMCacheManager.KEY_NTC_CHANNEL_MESSAGE);
|
||||
|
||||
Notification notification = makeNotification(PERSIST_NTC_CHANNEL_ID,icon,title,message);
|
||||
|
||||
startForegroundNotification(NOTIFICATION_ID, notification);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (notificationManager.getNotificationChannel(TRANSIENT_NTC_CHANNEL_ID) == null) {
|
||||
NotificationChannel channel = new NotificationChannel(TRANSIENT_NTC_CHANNEL_ID, getClass().getSimpleName(), NotificationManager.IMPORTANCE_LOW);
|
||||
channel.enableLights(false);
|
||||
@ -262,17 +263,21 @@ public class CIMPushService extends Service {
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
Notification notification = new Notification.Builder(this,TRANSIENT_NTC_CHANNEL_ID)
|
||||
.setContentTitle(CIMPushService.class.getSimpleName())
|
||||
.build();
|
||||
Notification notification = makeNotification(TRANSIENT_NTC_CHANNEL_ID,0, CIMPushService.class.getSimpleName(),null);
|
||||
|
||||
startForegroundNotification(NOTIFICATION_ID, notification);
|
||||
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
|
||||
private void createPersistNotification(String channelName ,String message,int icon) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(PERSIST_NTC_CHANNEL_ID) == null) {
|
||||
CIMCacheManager.putString(this, CIMCacheManager.KEY_NTC_CHANNEL_NAME,channelName);
|
||||
CIMCacheManager.putString(this, CIMCacheManager.KEY_NTC_CHANNEL_MESSAGE,message);
|
||||
CIMCacheManager.putInt(this, CIMCacheManager.KEY_NTC_CHANNEL_ICON,icon);
|
||||
CIMCacheManager.putBoolean(this, CIMCacheManager.KEY_NTC_SWITCH,true);
|
||||
|
||||
if (notificationManager.getNotificationChannel(PERSIST_NTC_CHANNEL_ID) == null) {
|
||||
NotificationChannel channel = new NotificationChannel(PERSIST_NTC_CHANNEL_ID,channelName, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.enableLights(false);
|
||||
channel.setShowBadge(false);
|
||||
@ -281,26 +286,36 @@ public class CIMPushService extends Service {
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
Notification notification = makeNotification(PERSIST_NTC_CHANNEL_ID,icon,channelName,message);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setPackage(getPackageName());
|
||||
startForegroundNotification(NOTIFICATION_ID,notification);
|
||||
}
|
||||
|
||||
|
||||
private Notification makeNotification(String channel,int icon,String title,String message){
|
||||
|
||||
Notification.Builder builder;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
|
||||
builder = new Notification.Builder(this,PERSIST_NTC_CHANNEL_ID);
|
||||
}else {
|
||||
builder = new Notification.Builder(this);
|
||||
}
|
||||
builder = new Notification.Builder(this, channel);
|
||||
|
||||
builder.setAutoCancel(false)
|
||||
.setOngoing(false)
|
||||
.setSmallIcon(icon)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setContentIntent(PendingIntent.getActivity(this, 0, intent, 0))
|
||||
.setContentTitle(channelName)
|
||||
.setContentText(message);
|
||||
.setOngoing(false)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setContentIntent(getPendingIntent())
|
||||
.setContentTitle(title)
|
||||
.setContentText(message);
|
||||
|
||||
startForeground(PERSIST_NOTIFICATION_ID, builder.build());
|
||||
if (icon > 0){
|
||||
builder.setSmallIcon(icon);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private PendingIntent getPendingIntent(){
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
|
||||
intent.setPackage(getPackageName());
|
||||
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
private class KeepAliveBroadcastReceiver extends BroadcastReceiver {
|
||||
@ -315,6 +330,8 @@ public class CIMPushService extends Service {
|
||||
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
|
||||
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
|
||||
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
|
||||
intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
|
||||
intentFilter.addAction(IntentAction.ACTION_CONNECTION_RECOVERY);
|
||||
return intentFilter;
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,9 @@
|
||||
package com.farsunset.cim.sdk.android.coder;
|
||||
|
||||
|
||||
import com.farsunset.cim.sdk.android.constant.CIMConstant;
|
||||
import com.farsunset.cim.sdk.android.model.Ping;
|
||||
import com.farsunset.cim.sdk.android.constant.ProtobufType;
|
||||
import com.farsunset.cim.sdk.android.model.Message;
|
||||
import com.farsunset.cim.sdk.android.model.Ping;
|
||||
import com.farsunset.cim.sdk.android.model.ReplyBody;
|
||||
import com.farsunset.cim.sdk.android.model.proto.MessageProto;
|
||||
import com.farsunset.cim.sdk.android.model.proto.ReplyBodyProto;
|
||||
@ -72,12 +72,12 @@ public class ClientMessageDecoder {
|
||||
/*
|
||||
消息读取完成后,通过type来解析成对应的消息体
|
||||
*/
|
||||
if (CIMConstant.ProtobufType.S_H_RQ == type) {
|
||||
if (ProtobufType.PING == type) {
|
||||
return Ping.getInstance();
|
||||
}
|
||||
|
||||
if (CIMConstant.ProtobufType.REPLY_BODY == type) {
|
||||
ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bodyBuffer.array());
|
||||
if (ProtobufType.REPLY_BODY == type) {
|
||||
ReplyBodyProto.ReplyModel bodyProto = ReplyBodyProto.ReplyModel.parseFrom(bodyBuffer.array());
|
||||
ReplyBody body = new ReplyBody();
|
||||
body.setKey(bodyProto.getKey());
|
||||
body.setTimestamp(bodyProto.getTimestamp());
|
||||
@ -87,7 +87,7 @@ public class ClientMessageDecoder {
|
||||
return body;
|
||||
}
|
||||
|
||||
MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bodyBuffer.array());
|
||||
MessageProto.MessageModel bodyProto = MessageProto.MessageModel.parseFrom(bodyBuffer.array());
|
||||
Message message = new Message();
|
||||
message.setId(bodyProto.getId());
|
||||
message.setAction(bodyProto.getAction());
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user