修复push消息时连续for循环中推送时,android , java 客户端会收不到消息的问题

This commit is contained in:
远方夕阳 2020-03-07 15:49:09 +08:00
parent f3b7ec63e0
commit 5dc06714ff
78 changed files with 2092 additions and 2253 deletions

View File

@ -25,45 +25,45 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.2.2.RELEASE" level="project" />
<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.29" 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.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.2.2.RELEASE" 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.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.10.1" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.10.1" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.10.1" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.10.1" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.1" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.10.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-tomcat:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-core:9.0.29" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-el:9.0.29" level="project" />
<orderEntry type="library" name="Maven: org.apache.tomcat.embed:tomcat-embed-websocket:9.0.29" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.1" 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.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.2.2.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-freemarker:2.2.2.RELEASE" 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.2.RELEASE" 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>
@ -74,7 +74,7 @@
</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.29" 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" />

View File

@ -77,6 +77,23 @@
<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>
@ -111,70 +128,70 @@
<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.1">
<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.1/jackson-annotations-2.10.1.jar!/" />
<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.1/jackson-annotations-2.10.1-javadoc.jar!/" />
<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.1/jackson-annotations-2.10.1-sources.jar!/" />
<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.1">
<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.1/jackson-core-2.10.1.jar!/" />
<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.1/jackson-core-2.10.1-javadoc.jar!/" />
<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.1/jackson-core-2.10.1-sources.jar!/" />
<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.1">
<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.1/jackson-databind-2.10.1.jar!/" />
<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.1/jackson-databind-2.10.1-javadoc.jar!/" />
<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.1/jackson-databind-2.10.1-sources.jar!/" />
<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.1">
<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.1/jackson-datatype-jdk8-2.10.1.jar!/" />
<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.1/jackson-datatype-jdk8-2.10.1-javadoc.jar!/" />
<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.1/jackson-datatype-jdk8-2.10.1-sources.jar!/" />
<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.1">
<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.1/jackson-datatype-jsr310-2.10.1.jar!/" />
<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.1/jackson-datatype-jsr310-2.10.1-javadoc.jar!/" />
<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.1/jackson-datatype-jsr310-2.10.1-sources.jar!/" />
<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.1">
<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.1/jackson-module-parameter-names-2.10.1.jar!/" />
<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.1/jackson-module-parameter-names-2.10.1-javadoc.jar!/" />
<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.1/jackson-module-parameter-names-2.10.1-sources.jar!/" />
<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">
@ -298,15 +315,15 @@
<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.1">
<library name="Maven: jakarta.validation:jakarta.validation-api:2.0.2">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/jakarta/validation/jakarta.validation-api/2.0.1/jakarta.validation-api-2.0.1.jar!/" />
<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.1/jakarta.validation-api-2.0.1-javadoc.jar!/" />
<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.1/jakarta.validation-api-2.0.1-sources.jar!/" />
<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">
@ -353,37 +370,37 @@
<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.29">
<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.29/tomcat-embed-core-9.0.29.jar!/" />
<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.29/tomcat-embed-core-9.0.29-javadoc.jar!/" />
<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.29/tomcat-embed-core-9.0.29-sources.jar!/" />
<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.29">
<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.29/tomcat-embed-el-9.0.29.jar!/" />
<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.29/tomcat-embed-el-9.0.29-javadoc.jar!/" />
<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.29/tomcat-embed-el-9.0.29-sources.jar!/" />
<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.29">
<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.29/tomcat-embed-websocket-9.0.29.jar!/" />
<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.29/tomcat-embed-websocket-9.0.29-javadoc.jar!/" />
<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.29/tomcat-embed-websocket-9.0.29-sources.jar!/" />
<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">
@ -419,224 +436,224 @@
<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.29">
<library name="Maven: org.slf4j:jul-to-slf4j:1.7.30">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/jul-to-slf4j/1.7.29/jul-to-slf4j-1.7.29.jar!/" />
<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.29/jul-to-slf4j-1.7.29-javadoc.jar!/" />
<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.29/jul-to-slf4j-1.7.29-sources.jar!/" />
<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.29">
<library name="Maven: org.slf4j:slf4j-api:1.7.30">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.29/slf4j-api-1.7.29.jar!/" />
<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.29/slf4j-api-1.7.29-javadoc.jar!/" />
<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.29/slf4j-api-1.7.29-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-autoconfigure-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-autoconfigure-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-autoconfigure-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-starter-freemarker-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-starter-freemarker-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-starter-freemarker-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-starter-json-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-starter-json-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-starter-json-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-starter-logging-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-starter-logging-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-starter-logging-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-starter-tomcat-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-starter-tomcat-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-starter-tomcat-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-starter-validation-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-starter-validation-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-starter-validation-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-starter-web-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-starter-web-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-starter-web-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-starter-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-starter-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-starter-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-boot-2.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-boot-2.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-boot-2.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-aop:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-aop/5.2.2.RELEASE/spring-aop-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-aop-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-aop-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-beans:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-beans/5.2.2.RELEASE/spring-beans-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-beans-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-beans-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<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.2.RELEASE/spring-context-support-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-context-support-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-context-support-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-context:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-context/5.2.2.RELEASE/spring-context-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-context-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-context-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-core:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-core/5.2.2.RELEASE/spring-core-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-core-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-core-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-expression:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-expression/5.2.2.RELEASE/spring-expression-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-expression-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-expression-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-jcl:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-jcl/5.2.2.RELEASE/spring-jcl-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-jcl-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-jcl-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-web:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-web/5.2.2.RELEASE/spring-web-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-web-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-web-5.2.2.RELEASE-sources.jar!/" />
<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.2.RELEASE">
<library name="Maven: org.springframework:spring-webmvc:5.2.4.RELEASE">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/springframework/spring-webmvc/5.2.2.RELEASE/spring-webmvc-5.2.2.RELEASE.jar!/" />
<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.2.RELEASE/spring-webmvc-5.2.2.RELEASE-javadoc.jar!/" />
<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.2.RELEASE/spring-webmvc-5.2.2.RELEASE-sources.jar!/" />
<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">

View File

@ -23,7 +23,6 @@
<component name="ProjectId" id="1PxWQToGq56jcmMz176UjXWLSaU" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showExcludedFiles" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
@ -34,7 +33,7 @@
<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$/lib" />
<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" />
@ -74,18 +73,6 @@
</list>
</recent_temporary>
</component>
<component name="ServiceViewManager">
<option name="viewStates">
<list>
<serviceView>
<treeState>
<expand />
<select />
</treeState>
</serviceView>
</list>
</option>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
@ -115,13 +102,23 @@
<workItem from="1568597528002" duration="523000" />
<workItem from="1579076046499" duration="14355000" />
<workItem from="1579226829168" duration="8852000" />
<workItem from="1583472574255" duration="526000" />
<workItem from="1583473153031" duration="14749000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
<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>
@ -130,38 +127,46 @@
<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="1867" height="615" key="GridCell.Tab.0.bottom" timestamp="1579243696749">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.0.bottom" timestamp="1583566851136">
<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="1867" height="615" key="GridCell.Tab.0.center" timestamp="1579243696749">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.0.bottom/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<state width="1870" height="565" key="GridCell.Tab.0.center" timestamp="1583566851135">
<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="1867" height="615" key="GridCell.Tab.0.left" timestamp="1579243696749">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.0.center/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851135" />
<state width="1870" height="565" key="GridCell.Tab.0.left" timestamp="1583566851135">
<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="1867" height="615" key="GridCell.Tab.0.right" timestamp="1579243696749">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.0.left/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851135" />
<state width="1870" height="565" key="GridCell.Tab.0.right" timestamp="1583566851136">
<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="1867" height="615" key="GridCell.Tab.1.bottom" timestamp="1579243696751">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.0.right/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<state width="1870" height="565" key="GridCell.Tab.1.bottom" timestamp="1583566851137">
<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="1867" height="615" key="GridCell.Tab.1.center" timestamp="1579243696750">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.1.bottom/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851137" />
<state width="1870" height="565" key="GridCell.Tab.1.center" timestamp="1583566851136">
<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="1867" height="615" key="GridCell.Tab.1.left" timestamp="1579243696749">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.1.center/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<state width="1870" height="565" key="GridCell.Tab.1.left" timestamp="1583566851136">
<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="1867" height="615" key="GridCell.Tab.1.right" timestamp="1579243696751">
<screen x="0" y="0" width="1920" height="1040" />
<state width="1870" height="565" key="GridCell.Tab.1.left/0.23.1920.1057@0.23.1920.1057" timestamp="1583566851136" />
<state width="1870" height="565" key="GridCell.Tab.1.right" timestamp="1583566851137">
<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="1583566851137" />
<state width="1867" height="386" key="GridCell.Tab.2.bottom" timestamp="1579156159339">
<screen x="0" y="0" width="1920" height="1040" />
</state>
@ -178,18 +183,20 @@
<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="193" width="771" height="654" key="find.popup" timestamp="1579242964438">
<screen x="0" y="0" width="1920" height="1040" />
<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="5" width="840" height="1034" key="search.everywhere.popup" timestamp="1579235575382">
<screen x="0" y="0" width="1920" height="1040" />
<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>
@ -204,6 +211,11 @@
<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>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.5.RELEASE</version>
<relativePath/>
</parent>

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -21,12 +21,11 @@
*/
package com.farsunset.cim.api.controller;
import com.farsunset.cim.api.controller.dto.MessageResult;
import com.farsunset.cim.push.DefaultMessagePusher;
import com.farsunset.cim.sdk.server.model.Message;
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.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@ -39,30 +38,15 @@ public class MessageController {
@Resource
private DefaultMessagePusher defaultMessagePusher;
/**
* 此方法仅仅在集群时通过服务器调用
*
* @param message
* @return
*/
@PostMapping(value = "/dispatch")
public MessageResult dispatchSend(Message message) {
return send(message);
}
@PostMapping(value = "/send")
public MessageResult send(Message message) {
MessageResult result = new MessageResult();
@PostMapping(value = "/send")
public ResponseEntity<Long> send(Message message) {
message.setId(System.currentTimeMillis());
defaultMessagePusher.push(message);
result.id = message.getId();
result.timestamp = message.getTimestamp();
return result;
return ResponseEntity.ok(message.getId());
}
}

View File

@ -1,34 +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.api.controller.dto;
import org.springframework.http.HttpStatus;
import java.util.List;
public class BaseResult {
public int code = HttpStatus.OK.value();
public String message;
public Object data;
public List<?> dataList;
}

View File

@ -1,27 +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.api.controller.dto;
public class MessageResult extends BaseResult {
public long timestamp;
public long id;
}

View File

@ -13,7 +13,6 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
@ -23,19 +22,8 @@ public class CIMConfig implements CIMRequestHandler, ApplicationListener<Applica
@Resource
private ApplicationContext applicationContext;
private HashMap<String,Class<? extends CIMRequestHandler>> appHandlerMap = new HashMap<>();
private final HashMap<String,CIMRequestHandler> appHandlerMap = new HashMap<>();
@PostConstruct
private void initHandler() {
/*
* 账号绑定handler
*/
appHandlerMap.put("client_bind", BindHandler.class);
/*
* 连接关闭handler
*/
appHandlerMap.put("client_closed", SessionClosedHandler.class);
}
@Bean(destroyMethod = "destroy")
public CIMNioSocketAcceptor getNioSocketAcceptor(@Value("${cim.app.port}") int port,
@ -52,28 +40,22 @@ public class CIMConfig implements CIMRequestHandler, ApplicationListener<Applica
@Override
public void process(CIMSession session, SentBody body) {
CIMRequestHandler handler = findHandlerByKey(body.getKey());
CIMRequestHandler handler = appHandlerMap.get(body.getKey());
if(handler == null) {return ;}
handler.process(session, body);
}
private CIMRequestHandler findHandlerByKey(String key){
Class<? extends CIMRequestHandler> handlerClass = appHandlerMap.get(key);
if (handlerClass==null){
return null;
}
return applicationContext.getBean(handlerClass);
}
/**
/*
* springboot启动完成之后再启动cim服务的避免服务正在重启时客户端会立即开始连接导致意外异常发生.
*/
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
appHandlerMap.put("client_bind",applicationContext.getBean(BindHandler.class));
appHandlerMap.put("client_closed",applicationContext.getBean(SessionClosedHandler.class));
applicationContext.getBean(CIMNioSocketAcceptor.class).bind();
}
}

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -39,7 +39,7 @@ import javax.annotation.Resource;
import java.util.Objects;
/**
/*
* 账号绑定实现
*
*/

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -33,7 +33,7 @@ import javax.annotation.Resource;
import java.util.Objects;
/**
/*
* 断开连接清除session
*
*/
@ -56,15 +56,7 @@ public class SessionClosedHandler implements CIMRequestHandler {
return;
}
CIMSession oldSession = cimSessionService.get(account.toString());
if (oldSession == null || oldSession.isApnsEnable()) {
return;
}
oldSession.setState(CIMSession.STATE_DISABLED);
oldSession.setNid(null);
cimSessionService.save(oldSession);
cimSessionService.remove(account.toString());
}
}

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -23,13 +23,13 @@ package com.farsunset.cim.push;
import com.farsunset.cim.sdk.server.model.Message;
/**
/*
* 消息发送实接口
*
*/
public interface CIMMessagePusher {
/**
/*
* 向用户发送消息
*
* @param msg

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -31,7 +31,7 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
/**
/*
* 消息发送实现类
*
*/
@ -50,7 +50,7 @@ public class DefaultMessagePusher implements CIMMessagePusher {
private ApnsService apnsService;
/**
/*
* 向用户发送消息
*
* @param message
@ -76,9 +76,10 @@ public class DefaultMessagePusher implements CIMMessagePusher {
* 如果连接到了其他服务器则转发请求到目标服务器
*/
if (session.isConnected() && !Objects.equals(host, session.getHost())) {
/**
/*
* @TODO
* 在此调用目标服务器接口来发送
* 在此调用目标服务器接口来发送如session.host = 123.123.123.123
* 调用目标服务器的消息发送接口http://123.123.123.123:8080/message/send
*/
return;
}

View File

@ -7,7 +7,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
/*
* 正式场景下使用redis或者数据库来存储session信息
*/
@Repository

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -26,7 +26,7 @@ import com.farsunset.cim.sdk.server.model.CIMSession;
import java.util.List;
/**
/*
* 集群 session管理实现示例 各位可以自行实现 AbstractSessionManager接口来实现自己的 session管理 服务器集群时
* 须要将CIMSession 信息存入数据库或者redis中 第三方存储空间中便于所有服务器都可以访问
*/

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -46,7 +46,7 @@ public class CIMSessionServiceImpl implements CIMSessionService {
sessionRepository.save(session);
}
/**
/*
*
* @param account 用户id
* @return

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright 2013-2019 Xia Jun(3979434@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -23,10 +23,4 @@ cim.websocket.port=34567
apple.apns.debug=false
apple.apns.p12.password= your p12 password
apple.apns.p12.file= /apns/lvxin.p12
##################################################################
# System Config #
##################################################################
sys.message.dispatch.url = http://%1$s:${server.port}/api/message/dispatch
apple.apns.p12.file= /apns/lvxin.p12

View File

@ -1,67 +1,67 @@
module.common.html.title = CIM\u7BA1\u7406\u7CFB\u7EDF
module.common.account = \u5e10\u53f7
module.common.password = \u5bc6\u7801
module.common.save = \u4fdd\u5b58
module.common.html.title = CIM管理系统
module.common.account = 帐号
module.common.password = 密码
module.common.save = 保存
module.common.id = ID
module.common.type =\u7c7b\u578b
module.common.name =\u540d\u79f0
module.common.time =\u65f6\u95f4
module.common.operation =\u64cd\u4f5c
module.common.query =\u67e5\u8be2
module.common.look =\u67e5\u770b
module.common.preview =\u9884\u89c8
module.common.send =\u53d1\u9001
module.common.saveing = \u6b63\u5728\u4fdd\u5b58,\u8bf7\u7a0d\u5019......
module.common.add = \u6dfb\u52a0
module.common.update = \u4fee\u6539
module.common.delete = \u5220\u9664
module.common.save.success = \u4fdd\u5b58\u6210\u529f
module.common.delete.success = \u5220\u9664\u6210\u529f
module.common.delete.loading =\u6b63\u5728\u5220\u9664,\u8bf7\u7a0d\u5019......
module.common.loading = \u52a0\u8f7d\u4e2d,\u8bf7\u7a0d\u5019......
module.common.description = \u8bf4\u660e
module.common.state = \u72b6\u6001
module.common.content = \u5185\u5bb9
module.common.location = \u4f4d\u7f6e
module.common.longitude = \u7ecf\u5ea6
module.common.latitude = \u7eac\u5ea6
module.common.sort = \u6392\u5e8f
module.common.code = \u7f16\u53f7
module.common.import = \u5bfc\u5165
module.common.headlogo = \u5934\u50cf
module.common.homepage = \u4e3b\u9875
module.common.website = \u7f51\u5740
module.common.text = \u6587\u5b57
module.common.type =类型
module.common.name =名称
module.common.time =时间
module.common.operation =操作
module.common.query =查询
module.common.look =查看
module.common.preview =预览
module.common.send =发送
module.common.saveing = 正在保存,请稍候......
module.common.add = 添加
module.common.update = 修改
module.common.delete = 删除
module.common.save.success = 保存成功
module.common.delete.success = 删除成功
module.common.delete.loading =正在删除,请稍候......
module.common.loading = 加载中,请稍候......
module.common.description = 说明
module.common.state = 状态
module.common.content = 内容
module.common.location = 位置
module.common.longitude = 经度
module.common.latitude = 纬度
module.common.sort = 排序
module.common.code = 编号
module.common.import = 导入
module.common.headlogo = 头像
module.common.homepage = 主页
module.common.website = 网址
module.common.text = 文字
module.global.error.500.hint = \u670d\u52a1\u7a0b\u5e8f\u53d1\u751f\u5185\u90e8\u9519\u8bef
module.global.error.400.hint = \u8bf7\u6c42\u53c2\u6570\u7c7b\u578b\u4e0d\u6b63\u786e
module.global.error.404.hint = \u8bbf\u95ee\u7684\u8d44\u6e90\u4e0d\u5b58\u5728
module.global.pager.next = \u4e0b\u4e00\u9875
module.global.pager.previou = \u4e0a\u4e00\u9875
module.global.pager.description = \u5171{0}\u6761\u8bb0\u5f55,{1}\u9875
module.console.about = \u5173\u4e8e
module.console.about.version = v3.5.0
module.console.about.author = \u4f5c\u8005: \u8fdc\u65b9\u5915\u9633
module.console.about.author.wechat = \u5fae\u4fe1: 3979434
module.global.error.500.hint = 服务程序发生内部错误
module.global.error.400.hint = 请求参数类型不正确
module.global.error.404.hint = 访问的资源不存在
module.global.pager.next = 下一页
module.global.pager.previou = 上一页
module.global.pager.description = 共{0}条记录,{1}页
module.console.about = 关于
module.console.about.version = 1.0.0
module.console.about.author = 作者: 远方夕阳
module.console.about.author.wechat = 微信: 3979434
module.console.about.author.qq = Q Q: 3979434
module.console.about.copyright = \u57FA\u4E8EJava\u670D\u52A1\u7AEF\u7684\u5373\u65F6\u901A\u4FE1\u89E3\u51B3\u65B9\u6848,\u4E0Eandroid \u5BA2\u6237\u7AEF\u5B8C\u7F8E\u7ED3\u5408\uFF0C\u540C\u65F6\u652F\u6301\u5176\u4ED6\u8BED\u8A00\u7684\u79FB\u52A8\u5E94\u7528\uFF0C\u7269\u8054\u7F51\uFF0C\u667A\u80FD\u5BB6\u5C45\uFF0C\u684C\u9762\u5E94\u7528\uFF0CWEB\u5E94\u7528\u4EE5\u53CA\u540E\u53F0\u7CFB\u7EDF\u4E4B\u95F4\u7684\u5373\u65F6\u6D88\u4EA4\u4E92\uFF0C\u4E3A\u4F60\u89E3\u51B3\u4E86\u957F\u8FDE\u63A5\u5404\u79CD\u6D88\u606F\u4E8B\u4EF6\uFF0C\u65AD\u7EBF\u91CD\u8FDE\u7B49\u7E41\u7410\u7684\u5904\u7406\uFF0C\u4F7F\u7528\u65B9\u4FBF\u6613\u4E8E\u96C6\u6210\u3002<br/><a target="_blank" href="https://gitee.com/farsunset/cim">https://gitee.com/farsunset/cim</a>
module.console.menu.onlineuser = \u5728\u7ebf\u7528\u6237
module.console.cimsession.sending = \u6b63\u5728\u53d1\u9001\uff0c\u8bf7\u7a0d\u5019......
module.console.cimsession.send.success = \u53d1\u9001\u6210\u529f
module.console.cimsession.logo =\u5934\u50cf
module.console.cimsession.account =\u5e10\u53f7
module.console.cimsession.nid = \u8fde\u63a5ID
module.console.cimsession.channel = \u7ec8\u7aef
module.console.cimsession.app.version =\u5e94\u7528\u7248\u672c
module.console.cimsession.os.version =\u7cfb\u7edf\u7248\u672c
module.console.cimsession.deviceid =\u8bbe\u5907\u7f16\u53f7
module.console.cimsession.device.model =\u7ec8\u7aef\u578b\u53f7
module.console.cimsession.online.time =\u5728\u7ebf\u65f6\u957f(\u79d2)
module.console.cimsession.time.format ={0}\u79d2
module.console.about.copyright = 基于Java服务端的即时通信解决方案,与android 客户端完美结合同时支持其他语言的移动应用物联网智能家居桌面应用WEB应用以及后台系统之间的即时消交互为你解决了长连接各种消息事件断线重连等繁琐的处理使用方便易于集成。<br/><a target="_blank" href="https://gitee.com/farsunset/cim">https://gitee.com/farsunset/cim</a>
module.console.menu.onlineuser = 在线用户
module.console.cimsession.sending = 正在发送,请稍候......
module.console.cimsession.send.success = 发送成功
module.console.cimsession.logo =头像
module.console.cimsession.account =帐号
module.console.cimsession.nid = 连接ID
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.online.time =在线时长(秒)
module.console.cimsession.time.format ={0}秒
module.console.cimsession.send.message =发送消息
module.console.cimsession.receiver = 接收帐号
module.console.cimsession.message = 消息内容
module.console.cimsession.send.message =\u53d1\u9001\u6d88\u606f
module.console.cimsession.receiver = \u63a5\u6536\u5e10\u53f7
module.console.cimsession.message = \u6d88\u606f\u5185\u5bb9

24
cim-client-sdk/cim-android-sdk/cim-android-sdk.iml Normal file → Executable file
View File

@ -1,2 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />
<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="library" name="Maven: org.slf4j:slf4j-api:1.7.30" 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>

0
cim-client-sdk/cim-android-sdk/jar.bat Normal file → Executable file
View File

0
cim-client-sdk/cim-android-sdk/jar.sh Normal file → Executable file
View File

0
cim-client-sdk/cim-android-sdk/pom.xml Normal file → Executable file
View File

View File

@ -29,76 +29,74 @@ import android.net.Uri;
class CIMCacheManager {
public static final String CIM_CONFIG_INFO = "CIM_CONFIG_INFO";
public static final String KEY_ACCOUNT = "KEY_ACCOUNT";
public static final String KEY_ACCOUNT = "KEY_ACCOUNT";
public static final String KEY_DEVICE_ID = "KEY_DEVICE_ID";
public static final String KEY_DEVICE_ID = "KEY_DEVICE_ID";
public static final String KEY_MANUAL_STOP = "KEY_MANUAL_STOP";
public static final String KEY_MANUAL_STOP = "KEY_MANUAL_STOP";
public static final String KEY_CIM_DESTROYED = "KEY_CIM_DESTROYED";
public static final String KEY_CIM_DESTROYED = "KEY_CIM_DESTROYED";
public static final String KEY_CIM_SERVER_HOST = "KEY_CIM_SERVER_HOST";
public static final String KEY_CIM_SERVER_HOST = "KEY_CIM_SERVER_HOST";
public static final String KEY_CIM_SERVER_PORT = "KEY_CIM_SERVER_PORT";
public static final String KEY_CIM_SERVER_PORT = "KEY_CIM_SERVER_PORT";
public static final String KEY_CIM_CONNECTION_STATE = "KEY_CIM_CONNECTION_STATE";
public static final String KEY_CIM_CONNECTION_STATE = "KEY_CIM_CONNECTION_STATE";
public static final String CONTENT_URI = "content://%s.cim.provider";
public static final String CONTENT_URI = "content://%s.cim.provider";
public static void remove(Context context, String key) {
ContentResolver resolver = context.getContentResolver();
resolver.delete(Uri.parse(String.format(CONTENT_URI,context.getPackageName())), key, null);
}
public static void remove(Context context, String key) {
ContentResolver resolver = context.getContentResolver();
resolver.delete(Uri.parse(String.format(CONTENT_URI, context.getPackageName())), key, null);
}
public static void putString(Context context, String key, String value) {
public static void putString(Context context, String key, String value) {
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
values.put("value", value);
values.put("key", key);
resolver.insert(Uri.parse(String.format(CONTENT_URI,context.getPackageName())), values);
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
values.put("value", value);
values.put("key", key);
resolver.insert(Uri.parse(String.format(CONTENT_URI, context.getPackageName())), values);
}
}
public static String getString(Context context, String key) {
String value = null;
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(Uri.parse(String.format(CONTENT_URI,context.getPackageName())), new String[] { key }, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
value = cursor.getString(0);
}
closeQuietly(cursor);
return value;
}
public static String getString(Context context, String key) {
String value = null;
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(Uri.parse(String.format(CONTENT_URI, context.getPackageName())), new String[]{key}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
value = cursor.getString(0);
}
closeQuietly(cursor);
return value;
}
private static void closeQuietly(Cursor cursor) {
try {
if (cursor != null) {
cursor.close();
}
} catch (Exception ignore) {
}
}
private static void closeQuietly(Cursor cursor) {
try {
if (cursor != null) {
cursor.close();
}
} catch (Exception ignore) {
}
}
public static void putBoolean(Context context, String key, boolean value) {
putString(context, key, Boolean.toString(value));
}
public static void putBoolean(Context context, String key, boolean value) {
putString(context, key, Boolean.toString(value));
}
public static boolean getBoolean(Context context, String key) {
String value = getString(context, key);
return Boolean.parseBoolean(value);
}
public static boolean getBoolean(Context context, String key) {
String value = getString(context, key);
return Boolean.parseBoolean(value);
}
public static void putInt(Context context, String key, int value) {
putString(context, key, String.valueOf(value));
}
public static void putInt(Context context, String key, int value) {
putString(context, key, String.valueOf(value));
}
public static int getInt(Context context, String key) {
String value = getString(context, key);
return value == null ? 0 : Integer.parseInt(value);
}
public static int getInt(Context context, String key) {
String value = getString(context, key);
return value == null ? 0 : Integer.parseInt(value);
}
}

View File

@ -29,43 +29,43 @@ import android.database.MatrixCursor;
import android.net.Uri;
public class CIMCacheProvider extends ContentProvider {
static final String MODEL_KEY = "PRIVATE_CIM_CONFIG";
static final String MODEL_KEY = "PRIVATE_CIM_CONFIG";
@Override
public int delete(Uri arg0, String key, String[] arg2) {
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().remove(key).apply();
return 0;
}
@Override
public int delete(Uri arg0, String key, String[] arg2) {
getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).edit().remove(key).apply();
return 0;
}
@Override
public String getType(Uri arg0) {
return null;
}
@Override
public String getType(Uri arg0) {
return null;
}
@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();
return null;
}
@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();
return null;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri arg0, String[] arg1, String key, String[] arg3, String arg4) {
MatrixCursor cursor = new MatrixCursor(new String[] { "value" });
String value = getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).getString(arg1[0], null);
cursor.addRow(new Object[] { value });
return cursor;
}
@Override
public Cursor query(Uri arg0, String[] arg1, String key, String[] arg3, String arg4) {
MatrixCursor cursor = new MatrixCursor(new String[]{"value"});
String value = getContext().getSharedPreferences(MODEL_KEY, Context.MODE_PRIVATE).getString(arg1[0], null);
cursor.addRow(new Object[]{value});
return cursor;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
return 0;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
return 0;
}
}

View File

@ -26,7 +26,7 @@ import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import com.farsunset.cim.sdk.android.coder.CIMLogger;
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.CIMConstant;
@ -41,368 +41,312 @@ import java.nio.channels.SocketChannel;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;
/**
/*
* 连接服务端管理cim核心处理类管理连接以及消息处理
*
* @author 3979434@qq.com
*/
class CIMConnectorManager{
class CIMConnectorManager {
private static CIMConnectorManager manager;
private static final int READ_BUFFER_SIZE = 2048;
private static final int WRITE_BUFFER_SIZE = 1024;
private static CIMConnectorManager manager;
private static final int READ_IDLE_TIME = 120 * 1000;
private static final int CONNECT_TIME_OUT = 10 * 1000;
private static final int READ_BUFFER_SIZE = 2048;
private static final int WRITE_BUFFER_SIZE = 1024;
private static final int READ_IDLE_TIME = 120 * 1000;
private static final int CONNECT_TIME_OUT = 10 * 1000;
private static final int CONNECT_ALIVE_TIME_OUT = 150 * 1000;
private static final int CONNECT_ALIVE_TIME_OUT = 150 * 1000;
private static final AtomicLong LAST_READ_TIME = new AtomicLong(0);
private static final CIMLogger LOGGER = CIMLogger.getLogger();
private static final HandlerThread IDLE_HANDLER_THREAD = new HandlerThread("READ-IDLE", Process.THREAD_PRIORITY_BACKGROUND);
private Semaphore semaphore = new Semaphore(1, true);
private volatile SocketChannel socketChannel ;
private Context context;
private ByteBuffer readBuffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
private ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"worker-"));
private ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"boss-"));
private ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
private ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
static {
IDLE_HANDLER_THREAD.start();
}
private CIMConnectorManager(Context context) {
this.context = context;
}
public synchronized static CIMConnectorManager getManager(Context context) {
if (manager == null) {
manager = new CIMConnectorManager(context);
}
return manager;
}
private volatile SocketChannel socketChannel;
public void connect(final String host, final int port) {
private final Context context;
if (!CIMPushManager.isNetworkConnected(context)) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
context.sendBroadcast(intent);
return;
}
if (isConnected()) {
return;
}
bossExecutor.execute(() -> {
if (isConnected()) {
return;
}
LOGGER.startConnect(host, port);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
try {
semaphore.acquire();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);
socketChannel.socket().setTcpNoDelay(true);
socketChannel.socket().setKeepAlive(true);
socketChannel.socket().setReceiveBufferSize(READ_BUFFER_SIZE);
socketChannel.socket().setSendBufferSize(WRITE_BUFFER_SIZE);
socketChannel.socket().connect(new InetSocketAddress(host, port),CONNECT_TIME_OUT);
semaphore.release();
handelConnectedEvent();
private final ByteBuffer headerBuffer = ByteBuffer.allocate(CIMConstant.DATA_HEADER_LENGTH);
int result = -1;
private final ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r, "worker-"));
while((result = socketChannel.read(readBuffer)) > 0) {
private final ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r, "boss-"));
if(readBuffer.position() == readBuffer.capacity()) {
extendByteBuffer();
}
private final ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
private final ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
handelSocketReadEvent(result);
static {
IDLE_HANDLER_THREAD.start();
}
}
private CIMConnectorManager(Context context) {
this.context = context;
}
handelSocketReadEvent(result);
}catch(ConnectException | SocketTimeoutException ignore){
semaphore.release();
handleConnectAbortedEvent();
} catch(IOException ignore) {
semaphore.release();
handelDisconnectedEvent();
}catch(InterruptedException ignore) {
semaphore.release();
}
});
}
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);
context.sendBroadcast(intent);
return;
}
if (isConnected()) {
return;
}
bossExecutor.execute(() -> {
if (isConnected()) {
return;
}
LOGGER.startConnect(host, port);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
try {
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);
socketChannel.socket().setTcpNoDelay(true);
socketChannel.socket().setKeepAlive(true);
socketChannel.socket().setReceiveBufferSize(READ_BUFFER_SIZE);
socketChannel.socket().setSendBufferSize(WRITE_BUFFER_SIZE);
socketChannel.socket().connect(new InetSocketAddress(host, port), CONNECT_TIME_OUT);
handleConnectedEvent();
/*
*开始读取来自服务端的消息先读取3个字节的消息头
*/
while (socketChannel.read(headerBuffer) > 0) {
handleSocketReadEvent();
}
/*
*read 返回 <= 0的情况发生了意外需要断开重链
*/
closeSession();
} catch (ConnectException | SocketTimeoutException ignore) {
handleConnectAbortedEvent();
} catch (IOException ignore) {
handleDisconnectedEvent();
}
});
}
public void destroy() {
closeSession();
closeSession();
}
}
public void closeSession() {
public void closeSession() {
if(!isConnected()) {
return;
}
try {
socketChannel.close();
} catch (IOException ignore) {
}finally {
this.sessionClosed();
}
}
if (!isConnected()) {
return;
}
public boolean isConnected() {
return socketChannel != null && socketChannel.isConnected();
}
public void send(final Protobufable body) {
if(!isConnected()) {
return;
}
workerExecutor.execute(() -> {
int result = 0;
try {
try {
socketChannel.close();
} catch (IOException ignore) {
} finally {
this.sessionClosed();
}
}
semaphore.acquire();
ByteBuffer buffer = messageEncoder.encode(body);
while(buffer.hasRemaining()){
result += socketChannel.write(buffer);
}
} catch (Exception e) {
result = -1;
}finally {
semaphore.release();
if(result <= 0) {
closeSession();
}else {
messageSent(body);
}
}
});
}
private void sessionCreated() {
LOGGER.sessionCreated(socketChannel);
LAST_READ_TIME.set(System.currentTimeMillis());
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FINISHED);
context.sendBroadcast(intent);
}
private void sessionClosed() {
idleHandler.removeMessages(0);
LAST_READ_TIME.set(0);
LOGGER.sessionClosed(socketChannel);
readBuffer.clear();
if(readBuffer.capacity() > READ_BUFFER_SIZE) {
readBuffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
}
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED);
context.sendBroadcast(intent);
}
private void sessionIdle() {
LOGGER.sessionIdle(socketChannel);
/*
* 用于解决wifi情况下偶而路由器与服务器断开连接时客户端并没及时收到关闭事件 导致这样的情况下当前连接无效也不会重连的问题
*/
if (System.currentTimeMillis() - LAST_READ_TIME.get() >= CONNECT_ALIVE_TIME_OUT) {
closeSession();
}
}
public boolean isConnected() {
return socketChannel != null && socketChannel.isConnected();
}
private void messageReceived(Object obj) {
public void send(final Protobufable body) {
if (obj instanceof Message) {
if (!isConnected()) {
return;
}
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED);
intent.putExtra(Message.class.getName(), (Message) obj);
context.sendBroadcast(intent);
workerExecutor.execute(() -> {
int result = 0;
try {
}
if (obj instanceof ReplyBody) {
ByteBuffer buffer = messageEncoder.encode(body);
while (buffer.hasRemaining()) {
result += socketChannel.write(buffer);
}
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_REPLY_RECEIVED);
intent.putExtra(ReplyBody.class.getName(), (ReplyBody) obj);
context.sendBroadcast(intent);
}
}
} catch (Exception e) {
result = -1;
} finally {
private void messageSent(Object message) {
LOGGER.messageSent(socketChannel, message);
if (message instanceof SentBody) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_SEND_FINISHED);
intent.putExtra(SentBody.class.getName(), (SentBody) message);
context.sendBroadcast(intent);
}
}
if (result <= 0) {
closeSession();
} else {
messageSent(body);
}
}
});
}
private void sessionCreated() {
LOGGER.sessionCreated(socketChannel);
LAST_READ_TIME.set(System.currentTimeMillis());
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FINISHED);
context.sendBroadcast(intent);
}
private void sessionClosed() {
idleHandler.removeMessages(0);
LAST_READ_TIME.set(0);
LOGGER.sessionClosed(socketChannel);
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED);
context.sendBroadcast(intent);
}
private void sessionIdle() {
LOGGER.sessionIdle(socketChannel);
/*
* 用于解决wifi情况下偶而路由器与服务器断开连接时客户端并没及时收到关闭事件 导致这样的情况下当前连接无效也不会重连的问题
*/
if (System.currentTimeMillis() - LAST_READ_TIME.get() >= CONNECT_ALIVE_TIME_OUT) {
closeSession();
}
}
private void messageReceived(Object obj) {
if (obj instanceof Message) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED);
intent.putExtra(Message.class.getName(), (Message) obj);
context.sendBroadcast(intent);
}
if (obj instanceof ReplyBody) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_REPLY_RECEIVED);
intent.putExtra(ReplyBody.class.getName(), (ReplyBody) obj);
context.sendBroadcast(intent);
}
}
private void messageSent(Object message) {
LOGGER.messageSent(socketChannel, message);
if (message instanceof SentBody) {
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_SEND_FINISHED);
intent.putExtra(SentBody.class.getName(), (SentBody) message);
context.sendBroadcast(intent);
}
}
private final Handler idleHandler = new Handler(IDLE_HANDLER_THREAD.getLooper()) {
@Override
public void handleMessage(android.os.Message m) {
sessionIdle();
}
};
private void handleDisconnectedEvent() {
closeSession();
}
private Handler idleHandler = new Handler(IDLE_HANDLER_THREAD.getLooper()) {
@Override
public void handleMessage(android.os.Message m) {
sessionIdle();
}
};
private void handelDisconnectedEvent() {
closeSession();
}
private void handleConnectAbortedEvent() {
long interval = CIMConstant.RECONNECT_INTERVAL_TIME - (5 * 1000 - new Random().nextInt(15 * 1000));
LOGGER.connectFailure(interval);
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
intent.putExtra("interval", interval);
context.sendBroadcast(intent);
long interval = CIMConstant.RECONNECT_INTERVAL_TIME - (5 * 1000 - new Random().nextInt(15 * 1000));
}
private void handelConnectedEvent() {
sessionCreated();
idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME);
}
private void handelSocketReadEvent(int result) {
if(result == -1) {
closeSession();
return;
}
markLastReadTime();
readBuffer.position(0);
Object message = messageDecoder.doDecode(readBuffer);
if(message == null) {
return;
}
LOGGER.messageReceived(socketChannel,message);
LOGGER.connectFailure(interval);
Intent intent = new Intent();
intent.setPackage(context.getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_CONNECT_FAILED);
intent.putExtra("interval", interval);
context.sendBroadcast(intent);
}
private void handleConnectedEvent() {
sessionCreated();
idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME);
}
private void handleSocketReadEvent() throws IOException {
onMessageDecodeFinished(messageDecoder.doDecode(headerBuffer,socketChannel));
markLastReadTime();
}
private void onMessageDecodeFinished(Object message){
LOGGER.messageReceived(socketChannel, message);
if (message instanceof HeartbeatRequest) {
send(HeartbeatResponse.getInstance());
return;
}
this.messageReceived(message);
}
if(isHeartbeatRequest(message)) {
send(HeartbeatResponse.getInstance());
return;
}
this.messageReceived(message);
}
private void extendByteBuffer() {
ByteBuffer newBuffer = ByteBuffer.allocate(readBuffer.capacity() + READ_BUFFER_SIZE / 2);
readBuffer.position(0);
newBuffer.put(readBuffer);
readBuffer.clear();
readBuffer = newBuffer;
}
private void markLastReadTime() {
LAST_READ_TIME.set(System.currentTimeMillis());
idleHandler.removeMessages(0);
idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME);
}
LAST_READ_TIME.set(System.currentTimeMillis());
idleHandler.removeMessages(0);
private boolean isHeartbeatRequest(Object data) {
return data instanceof HeartbeatRequest;
}
idleHandler.sendEmptyMessageDelayed(0, READ_IDLE_TIME);
}
}

View File

@ -31,189 +31,190 @@ import com.farsunset.cim.sdk.android.model.Message;
import com.farsunset.cim.sdk.android.model.ReplyBody;
import com.farsunset.cim.sdk.android.model.SentBody;
/**
/*
* 消息入口所有消息都会经过这里
*/
public abstract class CIMEventBroadcastReceiver extends BroadcastReceiver {
protected Context context;
protected Context context;
@SuppressWarnings("deprecation")
@Override
public void onReceive(Context context, Intent intent) {
@SuppressWarnings("deprecation")
@Override
public void onReceive(Context context, Intent intent) {
this.context = context;
this.context = context;
String action = intent.getAction();
String action = intent.getAction();
/*
* 操作事件广播用于提高service存活率
*/
if (Intent.ACTION_USER_PRESENT.equals(action)
|| Intent.ACTION_POWER_CONNECTED.equals(action)
|| Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
startPushService();
}
/*
* 设备网络状态变化事件
*/
if (CIMConstant.IntentAction.ACTION_NETWORK_CHANGED.equals(action)
||ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
onDevicesNetworkChanged();
}
/*
* cim断开服务器事件
*/
if (CIMConstant.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);
onInnerConnectFailed(interval);
}
/*
* cim连接服务器成功事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECT_FINISHED.equals(action)) {
onInnerConnectFinished();
}
/*
* 收到推送消息事件
*/
if (CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED.equals(action)) {
onInnerMessageReceived((Message) intent.getSerializableExtra(Message.class.getName()), intent);
}
/*
* 获取收到replyBody成功事件
*/
if (CIMConstant.IntentAction.ACTION_REPLY_RECEIVED.equals(action)) {
onReplyReceived((ReplyBody) intent.getSerializableExtra(ReplyBody.class.getName()));
}
/*
* 获取sendBody发送成功事件
*/
if (CIMConstant.IntentAction.ACTION_SEND_FINISHED.equals(action)) {
onSentSucceed((SentBody) intent.getSerializableExtra(SentBody.class.getName()));
}
/*
* 重新连接如果断开的话
*/
if (CIMConstant.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);
/*
* 操作事件广播用于提高service存活率
*/
if (Intent.ACTION_USER_PRESENT.equals(action)
|| Intent.ACTION_POWER_CONNECTED.equals(action)
|| Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
startPushService();
}
}
private void onInnerConnectionClosed() {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
/*
* 设备网络状态变化事件
*/
if (CIMConstant.IntentAction.ACTION_NETWORK_CHANGED.equals(action)
|| ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
if (CIMPushManager.isNetworkConnected(context)) {
connect(0);
}
onDevicesNetworkChanged();
}
onConnectionClosed();
}
/*
* cim断开服务器事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECTION_CLOSED.equals(action)) {
onInnerConnectionClosed();
}
private void onInnerConnectFailed(long interval) {
/*
* cim连接服务器失败事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECT_FAILED.equals(action)) {
long interval = intent.getLongExtra("interval", CIMConstant.RECONNECT_INTERVAL_TIME);
onInnerConnectFailed(interval);
}
if (CIMPushManager.isNetworkConnected(context)) {
onConnectFailed();
connect(interval);
}
}
/*
* cim连接服务器成功事件
*/
if (CIMConstant.IntentAction.ACTION_CONNECT_FINISHED.equals(action)) {
onInnerConnectFinished();
}
private void onInnerConnectFinished() {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, true);
/*
* 收到推送消息事件
*/
if (CIMConstant.IntentAction.ACTION_MESSAGE_RECEIVED.equals(action)) {
onInnerMessageReceived((Message) intent.getSerializableExtra(Message.class.getName()), intent);
}
boolean autoBind = CIMPushManager.autoBindAccount(context);
onConnectFinished(autoBind);
}
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);
CIMPushManager.startService(context,serviceIntent);
}
private void onInnerMessageReceived(Message message, Intent intent) {
if (isForceOfflineMessage(message.getAction())) {
CIMPushManager.stop(context);
}
onMessageReceived(message, intent);
}
private boolean isForceOfflineMessage(String action) {
return CIMConstant.MessageAction.ACTION_999.equals(action);
}
/*
* 获取收到replyBody成功事件
*/
if (CIMConstant.IntentAction.ACTION_REPLY_RECEIVED.equals(action)) {
onReplyReceived((ReplyBody) intent.getSerializableExtra(ReplyBody.class.getName()));
}
/**
* 接收消息实现方法
* @param message
* @param intent
*/
public abstract void onMessageReceived(Message message, Intent intent);
/*
* 获取sendBody发送成功事件
*/
if (CIMConstant.IntentAction.ACTION_SEND_FINISHED.equals(action)) {
onSentSucceed((SentBody) intent.getSerializableExtra(SentBody.class.getName()));
}
public void onNetworkChanged() {
CIMListenerManager.notifyOnNetworkChanged(CIMPushManager.getNetworkInfo(context));
}
/*
* 重新连接如果断开的话
*/
if (CIMConstant.IntentAction.ACTION_CONNECTION_RECOVERY.equals(action)) {
connect(0);
}
}
public void onConnectFinished(boolean hasAutoBind) {
CIMListenerManager.notifyOnConnectFinished(hasAutoBind);
}
private void startPushService() {
public void onConnectFailed() {
CIMListenerManager.notifyOnConnectFailed();
}
Intent intent = new Intent(context, CIMPushService.class);
intent.setAction(CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE);
public void onConnectionClosed() {
CIMListenerManager.notifyOnConnectionClosed();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
}
private void onInnerConnectionClosed() {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, false);
if (CIMPushManager.isNetworkConnected(context)) {
connect(0);
}
onConnectionClosed();
}
private void onInnerConnectFailed(long interval) {
if (CIMPushManager.isNetworkConnected(context)) {
onConnectFailed();
connect(interval);
}
}
private void onInnerConnectFinished() {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE, true);
boolean autoBind = CIMPushManager.autoBindAccount(context);
onConnectFinished(autoBind);
}
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);
CIMPushManager.startService(context, serviceIntent);
}
private void onInnerMessageReceived(Message message, Intent intent) {
if (isForceOfflineMessage(message.getAction())) {
CIMPushManager.stop(context);
}
onMessageReceived(message, intent);
}
private boolean isForceOfflineMessage(String action) {
return CIMConstant.MessageAction.ACTION_999.equals(action);
}
public void onReplyReceived(ReplyBody body) {
CIMListenerManager.notifyOnReplyReceived(body);
}
/**
* 接收消息实现方法
*
* @param message
* @param intent
*/
public abstract void onMessageReceived(Message message, Intent intent);
public void onSentSucceed(SentBody body) {
CIMListenerManager.notifyOnSendFinished(body);
}
public void onNetworkChanged() {
CIMListenerManager.notifyOnNetworkChanged(CIMPushManager.getNetworkInfo(context));
}
public void onConnectFinished(boolean hasAutoBind) {
CIMListenerManager.notifyOnConnectFinished(hasAutoBind);
}
public void onConnectFailed() {
CIMListenerManager.notifyOnConnectFailed();
}
public void onConnectionClosed() {
CIMListenerManager.notifyOnConnectionClosed();
}
public void onReplyReceived(ReplyBody body) {
CIMListenerManager.notifyOnReplyReceived(body);
}
public void onSentSucceed(SentBody body) {
CIMListenerManager.notifyOnSendFinished(body);
}
}

View File

@ -31,57 +31,55 @@ import com.farsunset.cim.sdk.android.model.SentBody;
*/
public interface CIMEventListener {
/**
* 当收到服务端推送过来的消息时调用
*
* @param message
*/
void onMessageReceived(Message message);
/**
* 当收到服务端推送过来的消息时调用
*
* @param message
*/
void onMessageReceived(Message message);
/**
* 当调用CIMPushManager.sendRequest()向服务端发送请求获得相应时调用
*
* @param body
*/
void onReplyReceived(ReplyBody body);
/**
* 当调用CIMPushManager.sendRequest()向服务端发送请求获得相应时调用
*
* @param body
*/
void onReplyReceived(ReplyBody body);
/**
* 当调用CIMPushManager.sendRequest()向服务端发送请求成功时
*
* @param body
*/
void onSendFinished(SentBody body);
/**
* 当调用CIMPushManager.sendRequest()向服务端发送请求成功时
*
* @param body
*/
void onSendFinished(SentBody body);
/**
* 当手机网络发生变化时调用
*
* @param info
*/
void onNetworkChanged(NetworkInfo info);
/**
* 当手机网络发生变化时调用
*
* @param info
*/
void onNetworkChanged(NetworkInfo info);
/**
* 当连接服务器成功时回调
*
* @param hasAutoBind
* true 已经自动绑定账号到服务器了不需要再手动调用bindAccount
*/
void onConnectFinished(boolean hasAutoBind);
/**
* 当连接服务器成功时回调
*
* @param hasAutoBind true 已经自动绑定账号到服务器了不需要再手动调用bindAccount
*/
void onConnectFinished(boolean hasAutoBind);
/**
* 当断开服务器连接的时候回调
*
*/
void onConnectionClosed();
/**
* 当断开服务器连接的时候回调
*/
void onConnectionClosed();
/**
* 当连接服务器失败的时候回调
*
*/
void onConnectFailed();
/**
* 当连接服务器失败的时候回调
*/
void onConnectFailed();
/**
* 监听器在容器里面的排序值越大则越先接收
* @return 排序 值越大优先级越高
*/
int getEventDispatchOrder();
/**
* 监听器在容器里面的排序值越大则越先接收
*
* @return 排序 值越大优先级越高
*/
int getEventDispatchOrder();
}

View File

@ -37,96 +37,96 @@ import java.util.Iterator;
*/
public class CIMListenerManager {
private static ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>();
private static ReceiveComparator comparator = new ReceiveComparator();
private static final ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>();
private static final ReceiveComparator comparator = new ReceiveComparator();
private CIMListenerManager(){
private CIMListenerManager() {
}
}
public static void registerMessageListener(CIMEventListener listener) {
public static void registerMessageListener(CIMEventListener listener) {
if (!cimListeners.contains(listener)) {
cimListeners.add(listener);
Collections.sort(cimListeners,comparator);
}
}
if (!cimListeners.contains(listener)) {
cimListeners.add(listener);
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();
}
}
}
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();
}
}
}
public static void notifyOnNetworkChanged(NetworkInfo info) {
for (CIMEventListener listener : cimListeners) {
listener.onNetworkChanged(info);
}
}
public static void notifyOnNetworkChanged(NetworkInfo info) {
for (CIMEventListener listener : cimListeners) {
listener.onNetworkChanged(info);
}
}
public static void notifyOnConnectFinished(boolean hasAutoBind) {
for (CIMEventListener listener : cimListeners) {
listener.onConnectFinished(hasAutoBind);
}
}
public static void notifyOnConnectFinished(boolean hasAutoBind) {
for (CIMEventListener listener : cimListeners) {
listener.onConnectFinished(hasAutoBind);
}
}
public static void notifyOnMessageReceived(Message message) {
for (CIMEventListener listener : cimListeners) {
listener.onMessageReceived(message);
}
}
public static void notifyOnMessageReceived(Message message) {
for (CIMEventListener listener : cimListeners) {
listener.onMessageReceived(message);
}
}
public static void notifyOnConnectionClosed() {
for (CIMEventListener listener : cimListeners) {
listener.onConnectionClosed();
}
}
public static void notifyOnConnectionClosed() {
for (CIMEventListener listener : cimListeners) {
listener.onConnectionClosed();
}
}
public static void notifyOnConnectFailed() {
for (CIMEventListener listener : cimListeners) {
listener.onConnectFailed();
}
}
public static void notifyOnConnectFailed() {
for (CIMEventListener listener : cimListeners) {
listener.onConnectFailed();
}
}
public static void notifyOnReplyReceived(ReplyBody body) {
for (CIMEventListener listener : cimListeners) {
listener.onReplyReceived(body);
}
}
public static void notifyOnReplyReceived(ReplyBody body) {
for (CIMEventListener listener : cimListeners) {
listener.onReplyReceived(body);
}
}
public static void notifyOnSendFinished(SentBody body) {
for (CIMEventListener listener : cimListeners) {
listener.onSendFinished(body);
}
}
public static void notifyOnSendFinished(SentBody body) {
for (CIMEventListener listener : cimListeners) {
listener.onSendFinished(body);
}
}
public static void destroy() {
cimListeners.clear();
}
public static void destroy() {
cimListeners.clear();
}
public static void logListenersName() {
for (CIMEventListener listener : cimListeners) {
Log.i(CIMEventListener.class.getSimpleName(), "#######" + listener.getClass().getName() + "#######");
}
}
public static void logListenersName() {
for (CIMEventListener listener : cimListeners) {
Log.i(CIMEventListener.class.getSimpleName(), "#######" + listener.getClass().getName() + "#######");
}
}
/**
* 消息接收activity的接收顺序排序CIM_RECEIVE_ORDER倒序
*/
private static class ReceiveComparator implements Comparator<CIMEventListener> {
/**
* 消息接收activity的接收顺序排序CIM_RECEIVE_ORDER倒序
*/
private static class ReceiveComparator implements Comparator<CIMEventListener> {
@Override
public int compare(CIMEventListener arg1, CIMEventListener arg2) {
@Override
public int compare(CIMEventListener arg1, CIMEventListener arg2) {
int order1 = arg1.getEventDispatchOrder();
int order2 = arg2.getEventDispatchOrder();
return Integer.compare(order2,order1);
}
int order1 = arg1.getEventDispatchOrder();
int order2 = arg2.getEventDispatchOrder();
return Integer.compare(order2, order1);
}
}
}
}

View File

@ -29,7 +29,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.text.TextUtils;
import com.farsunset.cim.sdk.android.coder.CIMLogger;
import com.farsunset.cim.sdk.android.logger.CIMLogger;
import com.farsunset.cim.sdk.android.constant.CIMConstant;
import com.farsunset.cim.sdk.android.model.SentBody;
@ -40,208 +40,211 @@ import java.util.UUID;
*/
public class CIMPushManager {
protected static String ACTION_ACTIVATE_PUSH_SERVICE = "ACTION_ACTIVATE_PUSH_SERVICE";
protected static final String ACTION_ACTIVATE_PUSH_SERVICE = "ACTION_ACTIVATE_PUSH_SERVICE";
protected static String ACTION_CREATE_CIM_CONNECTION = "ACTION_CREATE_CIM_CONNECTION";
protected static final String ACTION_CREATE_CIM_CONNECTION = "ACTION_CREATE_CIM_CONNECTION";
protected static String ACTION_SEND_REQUEST_BODY = "ACTION_SEND_REQUEST_BODY";
protected static final String ACTION_SEND_REQUEST_BODY = "ACTION_SEND_REQUEST_BODY";
protected static String ACTION_CLOSE_CIM_CONNECTION = "ACTION_CLOSE_CIM_CONNECTION";
protected static final String ACTION_CLOSE_CIM_CONNECTION = "ACTION_CLOSE_CIM_CONNECTION";
protected static String ACTION_SET_LOGGER_EATABLE = "ACTION_SET_LOGGER_EATABLE";
protected static final String ACTION_DESTROY_CIM_SERVICE = "ACTION_DESTROY_CIM_SERVICE";
protected static String KEY_SEND_BODY = "KEY_SEND_BODY";
protected static final String ACTION_SET_LOGGER_EATABLE = "ACTION_SET_LOGGER_EATABLE";
/**
* 初始化,连接服务端在程序启动页或者 在Application里调用
*/
public static void connect(Context context, String host, int port) {
protected static final String KEY_SEND_BODY = "KEY_SEND_BODY";
if(TextUtils.isEmpty(host) || port == 0) {
CIMLogger.getLogger().invalidHostPort(host, port);
return;
}
/**
* 初始化,连接服务端在程序启动页或者 在Application里调用
*/
public static void connect(Context context, String host, int port) {
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);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CREATE_CIM_CONNECTION);
startService(context,serviceIntent);
}
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);
startService(context,serviceIntent);
}
/**
* 设置一个账号登录到服务端
*/
public static void bindAccount(Context context, String account) {
if (isDestroyed(context) || account == null || account.trim().length() == 0) {
return;
}
sendBindRequest(context, account);
}
private static void sendBindRequest(Context context, String account) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, account);
SentBody sent = new SentBody();
sent.setKey(CIMConstant.RequestKey.CLIENT_BIND);
sent.put("account", account);
sent.put("deviceId", getDeviceId(context));
sent.put("channel", "android");
sent.put("device", Build.MODEL);
sent.put("appVersion", getVersionName(context));
sent.put("osVersion", Build.VERSION.RELEASE);
sent.put("packageName", context.getPackageName());
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)) {
return false;
}
sendBindRequest(context, account);
return true;
}
/**
* 发送一个CIM请求
*/
public static void sendRequest(Context context, SentBody body) {
if (isDestroyed(context) || isStopped(context)) {
return;
}
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(KEY_SEND_BODY, body);
serviceIntent.setAction(ACTION_SEND_REQUEST_BODY);
startService(context,serviceIntent);
}
/**
* 停止接受推送将会退出当前账号登录端口与服务端的连接
*/
public static void stop(Context context) {
if (isDestroyed(context)) {
return;
}
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, true);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CLOSE_CIM_CONNECTION);
startService(context,serviceIntent);
}
/**
* 完全销毁CIM一般用于完全退出程序调用resume将不能恢复
*/
public static void destroy(Context context) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, true);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, null);
context.stopService(new Intent(context, CIMPushService.class));
}
/**
* 重新恢复接收推送重新连接服务端并登录当前账号
*/
public static void resume(Context context) {
if (isDestroyed(context)) {
return;
}
autoBindAccount(context);
}
public static boolean isDestroyed(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED);
}
public static boolean isStopped(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_MANUAL_STOP);
}
public static boolean isConnected(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE);
}
public static boolean isNetworkConnected(Context context) {
NetworkInfo networkInfo = getNetworkInfo(context);
return networkInfo != null && networkInfo.isConnected();
}
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 {
context.startService(intent);
if (TextUtils.isEmpty(host) || port == 0) {
CIMLogger.getLogger().invalidHostPort(host, port);
return;
}
}
private static String getVersionName(Context context) {
try {
PackageInfo mPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return mPackageInfo.versionName;
} catch (NameNotFoundException ignore) {
}
return null;
}
private static String getDeviceId(Context context){
CIMCacheManager.putString(context, CIMCacheManager.KEY_CIM_SERVER_HOST, host);
CIMCacheManager.putInt(context, CIMCacheManager.KEY_CIM_SERVER_PORT, port);
String currDeviceId = CIMCacheManager.getString(context, CIMCacheManager.KEY_DEVICE_ID);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, false);
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
if (!TextUtils.isEmpty(currDeviceId)) {
return currDeviceId;
}
CIMCacheManager.remove(context, CIMCacheManager.KEY_ACCOUNT);
String deviceId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
CIMCacheManager.putString(context, CIMCacheManager.KEY_DEVICE_ID,deviceId);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CREATE_CIM_CONNECTION);
startService(context, serviceIntent);
return deviceId;
}
}
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);
startService(context, serviceIntent);
}
/**
* 设置一个账号登录到服务端
*/
public static void bindAccount(Context context, String account) {
if (isDestroyed(context) || account == null || account.trim().length() == 0) {
return;
}
sendBindRequest(context, account);
}
private static void sendBindRequest(Context context, String account) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, false);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, account);
SentBody sent = new SentBody();
sent.setKey(CIMConstant.RequestKey.CLIENT_BIND);
sent.put("account", account);
sent.put("deviceId", getDeviceId(context));
sent.put("channel", "android");
sent.put("device", Build.MODEL);
sent.put("appVersion", getVersionName(context));
sent.put("osVersion", Build.VERSION.RELEASE);
sent.put("packageName", context.getPackageName());
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)) {
return false;
}
sendBindRequest(context, account);
return true;
}
/**
* 发送一个CIM请求
*/
public static void sendRequest(Context context, SentBody body) {
if (isDestroyed(context) || isStopped(context)) {
return;
}
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.putExtra(KEY_SEND_BODY, body);
serviceIntent.setAction(ACTION_SEND_REQUEST_BODY);
startService(context, serviceIntent);
}
/**
* 停止接受推送将会退出当前账号登录端口与服务端的连接
*/
public static void stop(Context context) {
if (isDestroyed(context)) {
return;
}
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_MANUAL_STOP, true);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_CLOSE_CIM_CONNECTION);
startService(context, serviceIntent);
}
/**
* 完全销毁CIM一般用于完全退出程序调用resume将不能恢复
*/
public static void destroy(Context context) {
CIMCacheManager.putBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED, true);
CIMCacheManager.putString(context, CIMCacheManager.KEY_ACCOUNT, null);
Intent serviceIntent = new Intent(context, CIMPushService.class);
serviceIntent.setAction(ACTION_DESTROY_CIM_SERVICE);
startService(context, serviceIntent);
}
/**
* 重新恢复接收推送重新连接服务端并登录当前账号
*/
public static void resume(Context context) {
if (isDestroyed(context)) {
return;
}
autoBindAccount(context);
}
public static boolean isDestroyed(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_DESTROYED);
}
public static boolean isStopped(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_MANUAL_STOP);
}
public static boolean isConnected(Context context) {
return CIMCacheManager.getBoolean(context, CIMCacheManager.KEY_CIM_CONNECTION_STATE);
}
public static boolean isNetworkConnected(Context context) {
NetworkInfo networkInfo = getNetworkInfo(context);
return networkInfo != null && networkInfo.isConnected();
}
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 {
context.startService(intent);
}
}
private static String getVersionName(Context context) {
try {
PackageInfo mPackageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return mPackageInfo.versionName;
} catch (NameNotFoundException ignore) {
}
return null;
}
private static String getDeviceId(Context context) {
String currDeviceId = CIMCacheManager.getString(context, CIMCacheManager.KEY_DEVICE_ID);
if (!TextUtils.isEmpty(currDeviceId)) {
return currDeviceId;
}
String deviceId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
CIMCacheManager.putString(context, CIMCacheManager.KEY_DEVICE_ID, deviceId);
return deviceId;
}
}

View File

@ -35,199 +35,211 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import com.farsunset.cim.sdk.android.coder.CIMLogger;
import com.farsunset.cim.sdk.android.logger.CIMLogger;
import com.farsunset.cim.sdk.android.constant.CIMConstant;
import com.farsunset.cim.sdk.android.model.SentBody;
/**
* 与服务端连接服务
*
* @author 3979434
*
* @author 3979434
*/
public class CIMPushService extends Service {
public final static String KEY_DELAYED_TIME = "KEY_DELAYED_TIME";
public final static String KEY_LOGGER_ENABLE = "KEY_LOGGER_ENABLE";
public class CIMPushService extends Service {
public final static String KEY_DELAYED_TIME = "KEY_DELAYED_TIME";
public final static String KEY_LOGGER_ENABLE = "KEY_LOGGER_ENABLE";
private final static int NOTIFICATION_ID = Integer.MAX_VALUE;
private CIMConnectorManager manager;
private KeepAliveBroadcastReceiver keepAliveReceiver;
private final static int NOTIFICATION_ID = Integer.MAX_VALUE;
private CIMConnectorManager manager;
private KeepAliveBroadcastReceiver keepAliveReceiver;
private ConnectivityManager connectivityManager;
@Override
public void onCreate() {
manager = CIMConnectorManager.getManager(this.getApplicationContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@Override
public void onCreate() {
manager = CIMConnectorManager.getManager(this.getApplicationContext());
keepAliveReceiver = new KeepAliveBroadcastReceiver();
registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.registerDefaultNetworkCallback(networkCallback);
keepAliveReceiver = new KeepAliveBroadcastReceiver();
registerReceiver(keepAliveReceiver, keepAliveReceiver.getIntentFilter());
}
}
}
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);
}
@Override
public void onUnavailable() {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
sendBroadcast(intent);
}
};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Handler connectHandler = new Handler() {
@Override
public void handleMessage(android.os.Message message) {
connect();
}
};
connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Handler notificationHandler = new Handler() {
@Override
public void handleMessage(android.os.Message message) {
stopForeground(true);
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
connectivityManager.registerDefaultNetworkCallback(networkCallback);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(getClass().getSimpleName(),getClass().getSimpleName(), NotificationManager.IMPORTANCE_LOW);
channel.enableLights(false);
channel.enableVibration(false);
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this, channel.getId())
.setContentTitle("Push service")
.setContentText("Push service is running")
.build();
startForeground(NOTIFICATION_ID,notification);
}
}
}
String action = intent == null ? CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE : intent.getAction();
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);
}
if (CIMPushManager.ACTION_CREATE_CIM_CONNECTION.equals(action)) {
connect(intent.getLongExtra(KEY_DELAYED_TIME, 0));
}
@Override
public void onUnavailable() {
Intent intent = new Intent();
intent.setPackage(getPackageName());
intent.setAction(CIMConstant.IntentAction.ACTION_NETWORK_CHANGED);
sendBroadcast(intent);
}
if (CIMPushManager.ACTION_SEND_REQUEST_BODY.equals(action)) {
manager.send((SentBody) intent.getSerializableExtra(CIMPushManager.KEY_SEND_BODY));
}
};
if (CIMPushManager.ACTION_CLOSE_CIM_CONNECTION.equals(action)) {
manager.closeSession();
}
final Handler connectHandler = new Handler() {
@Override
public void handleMessage(android.os.Message message) {
connect();
}
};
if (CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE.equals(action)) {
handleKeepAlive();
}
final Handler notificationHandler = new Handler() {
@Override
public void handleMessage(android.os.Message message) {
stopForeground(true);
}
};
if (CIMPushManager.ACTION_SET_LOGGER_EATABLE.equals(action)) {
boolean enable = intent.getBooleanExtra(KEY_LOGGER_ENABLE, true);
CIMLogger.getLogger().debugMode(enable);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationHandler.sendEmptyMessageDelayed(0, 1000);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(getClass().getSimpleName(), getClass().getSimpleName(), NotificationManager.IMPORTANCE_LOW);
channel.enableLights(false);
channel.enableVibration(false);
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
Notification notification = new Notification.Builder(this, channel.getId())
.setContentTitle("Push service")
.setContentText("Push service is running")
.build();
startForeground(NOTIFICATION_ID, notification);
}
return super.onStartCommand(intent, flags, startId);
}
String action = intent == null ? CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE : intent.getAction();
private void connect(long delayMillis) {
if (CIMPushManager.ACTION_CREATE_CIM_CONNECTION.equals(action)) {
connect(intent.getLongExtra(KEY_DELAYED_TIME, 0));
}
if(delayMillis <= 0) {
connect();
return;
}
connectHandler.sendEmptyMessageDelayed(0, delayMillis);
if (CIMPushManager.ACTION_SEND_REQUEST_BODY.equals(action)) {
manager.send((SentBody) intent.getSerializableExtra(CIMPushManager.KEY_SEND_BODY));
}
}
if (CIMPushManager.ACTION_CLOSE_CIM_CONNECTION.equals(action)) {
manager.closeSession();
}
private void connect() {
if (CIMPushManager.ACTION_ACTIVATE_PUSH_SERVICE.equals(action)) {
handleKeepAlive();
}
if(CIMPushManager.isDestroyed(this) || CIMPushManager.isStopped(this)) {
return;
}
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) {
Log.e(this.getClass().getSimpleName(), "Invalid hostname or port. host:" + host + " port:" + port);
return;
}
manager.connect(host, port);
if (CIMPushManager.ACTION_SET_LOGGER_EATABLE.equals(action)) {
boolean enable = intent.getBooleanExtra(KEY_LOGGER_ENABLE, true);
CIMLogger.getLogger().debugMode(enable);
}
}
private void handleKeepAlive() {
if (manager.isConnected()) {
CIMLogger.getLogger().connectState(true);
return;
}
connect();
}
if (CIMPushManager.ACTION_DESTROY_CIM_SERVICE.equals(action)) {
manager.destroy();
stopSelf();
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationHandler.sendEmptyMessageDelayed(0, 1000);
}
@Override
public void onDestroy() {
super.onDestroy();
manager.destroy();
connectHandler.removeMessages(0);
notificationHandler.removeMessages(0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
unregisterReceiver(keepAliveReceiver);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.unregisterNetworkCallback(networkCallback);
}
}
return super.onStartCommand(intent, flags, startId);
}
public class KeepAliveBroadcastReceiver extends BroadcastReceiver {
private void connect(long delayMillis) {
@Override
public void onReceive(Context arg0, Intent arg1) {
handleKeepAlive();
}
if (delayMillis <= 0) {
connect();
return;
}
public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
return intentFilter;
}
connectHandler.sendEmptyMessageDelayed(0, delayMillis);
}
}
private void connect() {
if (CIMPushManager.isDestroyed(this) || CIMPushManager.isStopped(this)) {
return;
}
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) {
Log.e(this.getClass().getSimpleName(), "Invalid hostname or port. host:" + host + " port:" + port);
return;
}
manager.connect(host, port);
}
private void handleKeepAlive() {
if (manager.isConnected()) {
CIMLogger.getLogger().connectState(true,CIMPushManager.isStopped(this),CIMPushManager.isDestroyed(this));
return;
}
connect();
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
public void release() {
connectHandler.removeMessages(0);
notificationHandler.removeMessages(0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
unregisterReceiver(keepAliveReceiver);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
connectivityManager.unregisterNetworkCallback(networkCallback);
}
}
@Override
public void onDestroy() {
super.onDestroy();
release();
}
public class KeepAliveBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0, Intent arg1) {
handleKeepAlive();
}
public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
return intentFilter;
}
}
}

View File

@ -1,138 +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.sdk.android.coder;
import android.util.Log;
import java.nio.channels.SocketChannel;
/**
* 日志打印添加session 的id和ip address
*/
public class CIMLogger {
private final static String TAG = "CIM";
private boolean debug = true;
public static CIMLogger getLogger() {
return LoggerHolder.logger;
}
private CIMLogger() {
}
private static class LoggerHolder{
private static CIMLogger logger = new CIMLogger();
}
public void debugMode(boolean mode) {
debug = mode;
}
public void messageReceived(SocketChannel session, Object message) {
if(debug) {
Log.i(TAG,String.format("[RECEIVED]" + getSessionInfo(session) + "\n%s", message));
}
}
public void messageSent(SocketChannel session, Object message) {
if(debug) {
Log.i(TAG,String.format("[ SENT ]" + getSessionInfo(session) + "\n%s", message));
}
}
public void sessionCreated( SocketChannel session) {
if(debug) {
Log.i(TAG,"[ OPENED ]" + getSessionInfo(session));
}
}
public void sessionIdle( SocketChannel session) {
if(debug) {
Log.d(TAG,"[ IDLE ]" + getSessionInfo(session));
}
}
public void sessionClosed( SocketChannel session) {
if(debug) {
Log.w(TAG,"[ CLOSED ] ID = " + session.hashCode());
}
}
public void connectFailure(long interval) {
if(debug) {
Log.d(TAG,"CONNECT FAILURE, TRY RECONNECT AFTER " + interval +"ms");
}
}
public void startConnect(String host , int port) {
if(debug) {
Log.i(TAG,"START CONNECT REMOTE HOST:" + host + " PORT:" + port);
}
}
public void invalidHostPort(String host , int port) {
if(debug) {
Log.d(TAG,"INVALID SOCKET ADDRESS -> HOST:" + host + " PORT:" + port);
}
}
public void connectState(boolean isConnected) {
if(debug) {
Log.d(TAG,"CONNECTED:" + isConnected);
}
}
public void connectState(boolean isConnected,boolean isManualStop,boolean isDestroyed) {
if(debug) {
Log.d(TAG,"CONNECTED:" + isConnected + " STOPPED:"+isManualStop+ " DESTROYED:"+isDestroyed);
}
}
private String getSessionInfo(SocketChannel session) {
StringBuilder builder = new StringBuilder();
if (session == null) {
return "";
}
builder.append(" [");
builder.append("id:").append(session.hashCode());
try {
if (session.socket().getLocalAddress() != null) {
builder.append(" L:").append(session.socket().getLocalAddress()).append(":").append(session.socket().getLocalPort());
}
} catch (Exception ignore) {
}
try {
if (session.socket().getRemoteSocketAddress() != null) {
builder.append(" R:").append(session.socket().getRemoteSocketAddress().toString());
}
} catch (Exception ignore) {
}
builder.append("]");
return builder.toString();
}
}

View File

@ -28,98 +28,86 @@ import com.farsunset.cim.sdk.android.model.Message;
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;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/**
* 客户端消息解码
*/
public class ClientMessageDecoder {
/**
*
* @param headerBuffer 读取到的消息头
* @param socketChannel
* @return
*/
public Object doDecode(ByteBuffer headerBuffer , SocketChannel socketChannel) throws IOException {
public Object doDecode(ByteBuffer buffer) {
/*
* 消息头3位
*/
if (buffer.remaining() < CIMConstant.DATA_HEADER_LENGTH) {
return null;
}
headerBuffer.position(0);
buffer.mark();
byte type = headerBuffer.get();
byte lv = headerBuffer.get();
byte hv = headerBuffer.get();
byte type = buffer.get();
headerBuffer.clear();
byte lv = buffer.get();
byte hv = buffer.get();
/*
* 先通过消息头拿到消息的长度然后进行定长读取
* 解决消息的断包和粘包情况
*/
int length = getContentLength(lv, hv);
int dataLength = getContentLength(lv, hv);
/*
*如果消息体没有接收完整则重置读取等待下一次重新读取
*/
if (length > buffer.remaining()) {
buffer.reset();
return null;
}
ByteBuffer bodyBuffer = ByteBuffer.allocate(dataLength);
byte[] dataBytes = new byte[length];
buffer.get(dataBytes, 0, length);
/*
* 如果读取的消息长度不够则进行等待后续消息到来
* 当读取的消息长度(bodyBuffer.position() == dataLength)时意味着一个完整的消息已经接收完成
*/
do {
socketChannel.read(bodyBuffer);
} while (bodyBuffer.position() != dataLength);
buffer.position(0);
try {
return mappingMessageObject(dataBytes, type);
} catch (InvalidProtocolBufferException e) {
return null;
}
}
private Object mappingMessageObject(byte[] bytes, byte type) throws InvalidProtocolBufferException {
/*
消息读取完成后通过type来解析成对应的消息体
*/
if (CIMConstant.ProtobufType.S_H_RQ == type) {
return HeartbeatRequest.getInstance();
}
if (CIMConstant.ProtobufType.S_H_RQ == type) {
return HeartbeatRequest.getInstance();
}
if (CIMConstant.ProtobufType.REPLY_BODY == type) {
ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bodyBuffer.array());
ReplyBody body = new ReplyBody();
body.setKey(bodyProto.getKey());
body.setTimestamp(bodyProto.getTimestamp());
body.putAll(bodyProto.getDataMap());
body.setCode(bodyProto.getCode());
body.setMessage(bodyProto.getMessage());
return body;
}
if (CIMConstant.ProtobufType.REPLY_BODY == type) {
ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bytes);
ReplyBody body = new ReplyBody();
body.setKey(bodyProto.getKey());
body.setTimestamp(bodyProto.getTimestamp());
body.putAll(bodyProto.getDataMap());
body.setCode(bodyProto.getCode());
body.setMessage(bodyProto.getMessage());
return body;
}
MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bodyBuffer.array());
Message message = new Message();
message.setId(bodyProto.getId());
message.setAction(bodyProto.getAction());
message.setContent(bodyProto.getContent());
message.setSender(bodyProto.getSender());
message.setReceiver(bodyProto.getReceiver());
message.setTitle(bodyProto.getTitle());
message.setExtra(bodyProto.getExtra());
message.setTimestamp(bodyProto.getTimestamp());
message.setFormat(bodyProto.getFormat());
return message;
}
if (CIMConstant.ProtobufType.MESSAGE == type) {
MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bytes);
Message message = new Message();
message.setId(bodyProto.getId());
message.setAction(bodyProto.getAction());
message.setContent(bodyProto.getContent());
message.setSender(bodyProto.getSender());
message.setReceiver(bodyProto.getReceiver());
message.setTitle(bodyProto.getTitle());
message.setExtra(bodyProto.getExtra());
message.setTimestamp(bodyProto.getTimestamp());
message.setFormat(bodyProto.getFormat());
return message;
}
return null;
}
/**
* 解析消息体长度
*/
private int getContentLength(byte lv, byte hv) {
int l = (lv & 0xff);
int h = (hv & 0xff);
return (l | h << 8);
}
/**
* 解析消息体长度
*/
private int getContentLength(byte lv, byte hv) {
int l = (lv & 0xff);
int h = (hv & 0xff);
return (l | h << 8);
}
}

View File

@ -23,38 +23,35 @@ package com.farsunset.cim.sdk.android.coder;
import com.farsunset.cim.sdk.android.constant.CIMConstant;
import com.farsunset.cim.sdk.android.model.Protobufable;
import com.farsunset.cim.sdk.android.model.SentBody;
import java.nio.ByteBuffer;
/**
* 客户端消息发送前进行编码
*/
public class ClientMessageEncoder {
public class ClientMessageEncoder {
public ByteBuffer encode(Object object) {
public ByteBuffer encode(Protobufable body) {
Protobufable data = (Protobufable) object;
byte[] byteArray = data.getByteArray();
byte[] data = body.getByteArray();
ByteBuffer buffer = ByteBuffer.allocate(byteArray.length + CIMConstant.DATA_HEADER_LENGTH);
ByteBuffer buffer = ByteBuffer.allocate(data.length + CIMConstant.DATA_HEADER_LENGTH);
buffer.put(createHeader(data.getType(), byteArray.length));
buffer.put(byteArray);
buffer.flip();
buffer.put(createHeader(body.getType(), data.length));
buffer.put(data);
buffer.flip();
return buffer;
return buffer;
}
}
/**
* 消息体最大为65535
*/
private byte[] createHeader(byte type, int length) {
byte[] header = new byte[CIMConstant.DATA_HEADER_LENGTH];
header[0] = type;
header[1] = (byte) (length & 0xff);
header[2] = (byte) ((length >> 8) & 0xff);
return header;
}
/**
* 消息体最大为65535
*/
private byte[] createHeader(byte type, int length) {
byte[] header = new byte[CIMConstant.DATA_HEADER_LENGTH];
header[0] = type;
header[1] = (byte) (length & 0xff);
header[2] = (byte) ((length >> 8) & 0xff);
return header;
}
}

View File

@ -21,99 +21,89 @@
*/
package com.farsunset.cim.sdk.android.constant;
/**
* 常量
*/
public interface CIMConstant {
long RECONNECT_INTERVAL_TIME = 30 * 1000;
long RECONNECT_INTERVAL_TIME = 30 * 1000;
/**
消息头长度为3个字节第一个字节为消息类型第二第三字节 转换int后为消息长度
*/
int DATA_HEADER_LENGTH = 3;
/*
* 消息头长度为3个字节第一个字节为消息类型第二第三字节 转换int后为消息长度
*/
int DATA_HEADER_LENGTH = 3;
interface ReturnCode {
interface ProtobufType {
String CODE_404 = "404";
/*
客户端->服务端 发送的心跳响应
*/
byte C_H_RS = 0;
String CODE_403 = "403";
/*
服务端->客户端 发送的心跳请求
*/
byte S_H_RQ = 1;
String CODE_405 = "405";
byte MESSAGE = 2;
String CODE_200 = "200";
byte SENT_BODY = 3;
String CODE_206 = "206";
byte REPLY_BODY = 4;
}
String CODE_500 = "500";
interface RequestKey {
}
String CLIENT_BIND = "client_bind";
interface ProtobufType {
byte C_H_RS = 0;
byte S_H_RQ = 1;
byte MESSAGE = 2;
byte SENT_BODY = 3;
byte REPLY_BODY = 4;
}
}
interface RequestKey {
interface MessageAction {
String CLIENT_BIND = "client_bind";
/*
被其他设备登录挤下线消息
*/
String ACTION_999 = "999";
}
String CLIENT_LOGOUT = "client_logout";
interface IntentAction {
}
/*
消息广播action
*/
String ACTION_MESSAGE_RECEIVED = "com.farsunset.cim.MESSAGE_RECEIVED";
interface MessageAction {
/*
发送sendBody成功广播
*/
String ACTION_SEND_FINISHED = "com.farsunset.cim.SEND_FINISHED";
/*
被其他设备登录挤下线消息
*/
String ACTION_999 = "999";
}
/*
链接意外关闭广播
*/
String ACTION_CONNECTION_CLOSED = "com.farsunset.cim.CONNECTION_CLOSED";
interface IntentAction {
/*
链接失败广播
*/
String ACTION_CONNECT_FAILED = "com.farsunset.cim.CONNECT_FAILED";
/*
消息广播action
*/
String ACTION_MESSAGE_RECEIVED = "com.farsunset.cim.MESSAGE_RECEIVED";
/*
链接成功广播
*/
String ACTION_CONNECT_FINISHED = "com.farsunset.cim.CONNECT_FINISHED";
/*
发送sendBody成功广播
*/
String ACTION_SEND_FINISHED = "com.farsunset.cim.SEND_FINISHED";
/*
发送sendBody成功后获得replayBody回应广播
*/
String ACTION_REPLY_RECEIVED = "com.farsunset.cim.REPLY_RECEIVED";
/*
链接意外关闭广播
*/
String ACTION_CONNECTION_CLOSED = "com.farsunset.cim.CONNECTION_CLOSED";
/*
网络变化广播
*/
String ACTION_NETWORK_CHANGED = "com.farsunset.cim.NETWORK_CHANGED";
/*
链接失败广播
*/
String ACTION_CONNECT_FAILED = "com.farsunset.cim.CONNECT_FAILED";
/*
链接成功广播
*/
String ACTION_CONNECT_FINISHED = "com.farsunset.cim.CONNECT_FINISHED";
/*
发送sendBody成功后获得replayBody回应广播
*/
String ACTION_REPLY_RECEIVED = "com.farsunset.cim.REPLY_RECEIVED";
/*
网络变化广播
*/
String ACTION_NETWORK_CHANGED = "com.farsunset.cim.NETWORK_CHANGED";
/*
重试连接
*/
String ACTION_CONNECTION_RECOVERY = "com.farsunset.cim.CONNECTION_RECOVERY";
}
/*
重试连接
*/
String ACTION_CONNECTION_RECOVERY = "com.farsunset.cim.CONNECTION_RECOVERY";
}
}

View File

@ -0,0 +1,133 @@
/*
* 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.sdk.android.logger;
import android.util.Log;
import java.nio.channels.SocketChannel;
/**
* 日志打印添加session 的id和ip address
*/
public class CIMLogger {
private final static String TAG = "CIM";
private boolean debug = true;
public static CIMLogger getLogger() {
return LoggerHolder.logger;
}
private CIMLogger() {
}
private static class LoggerHolder {
private static final CIMLogger logger = new CIMLogger();
}
public void debugMode(boolean mode) {
debug = mode;
}
public void messageReceived(SocketChannel session, Object message) {
if (debug) {
Log.i(TAG, String.format("[RECEIVED]" + getSessionInfo(session) + "\n%s", message));
}
}
public void messageSent(SocketChannel session, Object message) {
if (debug) {
Log.i(TAG, String.format("[ SENT ]" + getSessionInfo(session) + "\n%s", message));
}
}
public void sessionCreated(SocketChannel session) {
if (debug) {
Log.i(TAG, "[ OPENED ]" + getSessionInfo(session));
}
}
public void sessionIdle(SocketChannel session) {
if (debug) {
Log.d(TAG, "[ IDLE ]" + getSessionInfo(session));
}
}
public void sessionClosed(SocketChannel session) {
if (debug) {
Log.w(TAG, "[ CLOSED ] ID = " + session.hashCode());
}
}
public void connectFailure(long interval) {
if (debug) {
Log.d(TAG, "CONNECT FAILURE, TRY RECONNECT AFTER " + interval + "ms");
}
}
public void startConnect(String host, int port) {
if (debug) {
Log.i(TAG, "START CONNECT REMOTE HOST:" + host + " PORT:" + port);
}
}
public void invalidHostPort(String host, int port) {
if (debug) {
Log.d(TAG, "INVALID SOCKET ADDRESS -> HOST:" + host + " PORT:" + port);
}
}
public void connectState(boolean isConnected, boolean isManualStop, boolean isDestroyed) {
if (debug) {
Log.d(TAG, "CONNECTED:" + isConnected + " STOPPED:" + isManualStop + " DESTROYED:" + isDestroyed);
}
}
private String getSessionInfo(SocketChannel session) {
StringBuilder builder = new StringBuilder();
if (session == null) {
return "";
}
builder.append(" [");
builder.append("id:").append(session.hashCode());
try {
if (session.socket().getLocalAddress() != null) {
builder.append(" L:").append(session.socket().getLocalAddress()).append(":").append(session.socket().getLocalPort());
}
} catch (Exception ignore) {
}
try {
if (session.socket().getRemoteSocketAddress() != null) {
builder.append(" R:").append(session.socket().getRemoteSocketAddress().toString());
}
} catch (Exception ignore) {
}
builder.append("]");
return builder.toString();
}
}

View File

@ -27,37 +27,36 @@ import java.io.Serializable;
/**
* 服务端心跳请求
*
*/
public class HeartbeatRequest implements Serializable, Protobufable {
private static final long serialVersionUID = 1L;
private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
private static final String CMD_HEARTBEAT_REQUEST = "SR";
private static final long serialVersionUID = 1L;
private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
private static final String CMD_HEARTBEAT_REQUEST = "SR";
private static HeartbeatRequest object = new HeartbeatRequest();
private static final HeartbeatRequest object = new HeartbeatRequest();
private HeartbeatRequest() {
private HeartbeatRequest() {
}
}
public static HeartbeatRequest getInstance() {
return object;
}
public static HeartbeatRequest getInstance() {
return object;
}
@Override
public byte[] getByteArray() {
return CMD_HEARTBEAT_REQUEST.getBytes();
}
@Override
public byte[] getByteArray() {
return CMD_HEARTBEAT_REQUEST.getBytes();
}
@Override
public String toString() {
return TAG;
}
@Override
public String toString() {
return TAG;
}
@Override
public byte getType() {
return CIMConstant.ProtobufType.S_H_RQ;
}
@Override
public byte getType() {
return CIMConstant.ProtobufType.S_H_RQ;
}
}

View File

@ -30,33 +30,33 @@ import java.io.Serializable;
*/
public class HeartbeatResponse implements Serializable, Protobufable {
private static final long serialVersionUID = 1L;
private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE";
private static final String CMD_HEARTBEAT_RESPONSE = "CR";
private static final long serialVersionUID = 1L;
private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE";
private static final String CMD_HEARTBEAT_RESPONSE = "CR";
private static HeartbeatResponse object = new HeartbeatResponse();
private static final HeartbeatResponse object = new HeartbeatResponse();
private HeartbeatResponse() {
private HeartbeatResponse() {
}
}
public static HeartbeatResponse getInstance() {
return object;
}
public static HeartbeatResponse getInstance() {
return object;
}
@Override
public byte[] getByteArray() {
return CMD_HEARTBEAT_RESPONSE.getBytes();
}
@Override
public byte[] getByteArray() {
return CMD_HEARTBEAT_RESPONSE.getBytes();
}
@Override
public String toString() {
return TAG;
}
@Override
public String toString() {
return TAG;
}
@Override
public byte getType() {
return CIMConstant.ProtobufType.C_H_RS;
}
@Override
public byte getType() {
return CIMConstant.ProtobufType.C_H_RS;
}
}

View File

@ -28,143 +28,143 @@ import java.io.Serializable;
*/
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
/**
* 消息类型用户自定义消息类别
*/
private long id;
/**
* 消息类型用户自定义消息类别
*/
private long id;
/**
* 消息类型用户自定义消息类别
*/
private String action;
/**
* 消息标题
*/
private String title;
/**
* 消息类容于action 组合为任何类型消息content 根据 format 可表示为 text,json ,xml数据格式
*/
private String content;
/**
* 消息类型用户自定义消息类别
*/
private String action;
/**
* 消息标题
*/
private String title;
/**
* 消息类容于action 组合为任何类型消息content 根据 format 可表示为 text,json ,xml数据格式
*/
private String content;
/**
* 消息发送者账号
*/
private String sender;
/**
* 消息发送者接收者
*/
private String receiver;
/**
* 消息发送者账号
*/
private String sender;
/**
* 消息发送者接收者
*/
private String receiver;
/**
* content 内容格式
*/
private String format;
/**
* content 内容格式
*/
private String format;
/**
* 附加内容 内容
*/
private String extra;
/**
* 附加内容 内容
*/
private String extra;
private long timestamp;
private long timestamp;
public Message() {
timestamp = System.currentTimeMillis();
}
public Message() {
timestamp = System.currentTimeMillis();
}
public long getTimestamp() {
return timestamp;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public String getAction() {
return action;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public void setAction(String action) {
this.action = action;
}
public String getTitle() {
return title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public void setContent(String content) {
this.content = content;
}
public String getSender() {
return sender;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public String getReceiver() {
return receiver;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getFormat() {
return format;
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public void setFormat(String format) {
this.format = format;
}
public String getExtra() {
return extra;
}
public String getExtra() {
return extra;
}
public void setExtra(String extra) {
this.extra = extra;
}
public void setExtra(String extra) {
this.extra = extra;
}
@Override
public String toString() {
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("#Message#").append("\n");
buffer.append("id:").append(id).append("\n");
buffer.append("action:").append(action).append("\n");
buffer.append("title:").append(title).append("\n");
buffer.append("content:").append(content).append("\n");
buffer.append("extra:").append(extra).append("\n");
buffer.append("sender:").append(sender).append("\n");
buffer.append("receiver:").append(receiver).append("\n");
buffer.append("format:").append(format).append("\n");
buffer.append("timestamp:").append(timestamp);
return buffer.toString();
}
public long getId() {
return id;
}
StringBuffer buffer = new StringBuffer();
buffer.append("#Message#").append("\n");
buffer.append("id:").append(id).append("\n");
buffer.append("action:").append(action).append("\n");
buffer.append("title:").append(title).append("\n");
buffer.append("content:").append(content).append("\n");
buffer.append("extra:").append(extra).append("\n");
buffer.append("sender:").append(sender).append("\n");
buffer.append("receiver:").append(receiver).append("\n");
buffer.append("format:").append(format).append("\n");
buffer.append("timestamp:").append(timestamp);
return buffer.toString();
}
public void setId(long id) {
this.id = id;
}
public boolean isNotEmpty(String txt) {
return txt != null && txt.trim().length() != 0;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isNotEmpty(String txt) {
return txt != null && txt.trim().length() != 0;
}
}

View File

@ -26,7 +26,7 @@ package com.farsunset.cim.sdk.android.model;
*/
public interface Protobufable {
byte[] getByteArray();
byte[] getByteArray();
byte getType();
byte getType();
}

View File

@ -28,101 +28,100 @@ import java.util.Set;
/**
* 请求应答对象
*
*/
public class ReplyBody implements Serializable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
/**
* 请求key
*/
private String key;
/**
* 请求key
*/
private String key;
/**
* 返回码
*/
private String code;
/**
* 返回码
*/
private String code;
/**
* 返回说明
*/
private String message;
/**
* 返回说明
*/
private String message;
private long timestamp;
private long timestamp;
/**
* 返回数据集合
*/
private Hashtable<String, String> data = new Hashtable<String, String>();
/**
* 返回数据集合
*/
private final Hashtable<String, String> data = new Hashtable<String, String>();
public long getTimestamp() {
return timestamp;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public String getKey() {
return key;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public void setKey(String key) {
this.key = key;
}
public void put(String k, String v) {
data.put(k, v);
}
public void put(String k, String v) {
data.put(k, v);
}
public String get(String k) {
return data.get(k);
}
public String get(String k) {
return data.get(k);
}
public void remove(String k) {
data.remove(k);
}
public void remove(String k) {
data.remove(k);
}
public String getMessage() {
return message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void setMessage(String message) {
this.message = message;
}
public void putAll(Map<String, String> map) {
data.putAll(map);
}
public void putAll(Map<String, String> map) {
data.putAll(map);
}
public Set<String> getKeySet() {
return data.keySet();
}
public Set<String> getKeySet() {
return data.keySet();
}
public String getCode() {
return code;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("#ReplyBody#").append("\n");
buffer.append("key:").append(this.getKey()).append("\n");
buffer.append("timestamp:").append(timestamp).append("\n");
buffer.append("code:").append(code).append("\n");
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("#ReplyBody#").append("\n");
buffer.append("key:").append(this.getKey()).append("\n");
buffer.append("timestamp:").append(timestamp).append("\n");
buffer.append("code:").append(code).append("\n");
buffer.append("data{").append("\n");
for (String key : getKeySet()) {
buffer.append(key).append(":").append(this.get(key)).append("\n");
}
buffer.append("}");
buffer.append("data{").append("\n");
for (String key : getKeySet()) {
buffer.append(key).append(":").append(this.get(key)).append("\n");
}
buffer.append("}");
return buffer.toString();
}
return buffer.toString();
}
}

View File

@ -30,85 +30,84 @@ import java.util.Set;
/**
* java |android 客户端请求结构
*
*/
public class SentBody implements Serializable, Protobufable {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
private String key;
private String key;
private Hashtable<String, String> data = new Hashtable<String, String>();;
private final Hashtable<String, String> data = new Hashtable<String, String>();
private long timestamp;
private long timestamp;
public SentBody() {
timestamp = System.currentTimeMillis();
}
public SentBody() {
timestamp = System.currentTimeMillis();
}
public String getKey() {
return key;
}
public String getKey() {
return key;
}
public String get(String k) {
return data.get(k);
}
public String get(String k) {
return data.get(k);
}
public long getTimestamp() {
return timestamp;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public void setKey(String key) {
this.key = key;
}
public void setKey(String key) {
this.key = key;
}
public void put(String k, String v) {
if (k == null || v == null) {
return;
}
data.put(k, v);
}
public void put(String k, String v) {
if (k == null || v == null) {
return;
}
data.put(k, v);
}
public Set<String> getKeySet() {
return data.keySet();
}
public Set<String> getKeySet() {
return data.keySet();
}
public void remove(String k) {
data.remove(k);
}
public void remove(String k) {
data.remove(k);
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("#SentBody#").append("\n");
buffer.append("key:").append(key).append("\n");
buffer.append("timestamp:").append(timestamp).append("\n");
buffer.append("data{").append("\n");
for (String key : getKeySet()) {
buffer.append(key).append(":").append(this.get(key)).append("\n");
}
buffer.append("}");
return buffer.toString();
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("#SentBody#").append("\n");
buffer.append("key:").append(key).append("\n");
buffer.append("timestamp:").append(timestamp).append("\n");
buffer.append("data{").append("\n");
for (String key : getKeySet()) {
buffer.append(key).append(":").append(this.get(key)).append("\n");
}
buffer.append("}");
return buffer.toString();
}
@Override
public byte[] getByteArray() {
SentBodyProto.Model.Builder builder = SentBodyProto.Model.newBuilder();
builder.setKey(key);
builder.setTimestamp(timestamp);
if (!data.isEmpty()) {
builder.putAllData(data);
}
return builder.build().toByteArray();
}
@Override
public byte[] getByteArray() {
SentBodyProto.Model.Builder builder = SentBodyProto.Model.newBuilder();
builder.setKey(key);
builder.setTimestamp(timestamp);
if (!data.isEmpty()) {
builder.putAllData(data);
}
return builder.build().toByteArray();
}
@Override
public byte getType() {
return CIMConstant.ProtobufType.SENT_BODY;
}
@Override
public byte getType() {
return CIMConstant.ProtobufType.SENT_BODY;
}
}

View File

@ -1,15 +1,15 @@
syntax = "proto3";
package com.farsunset.cim.sdk.android.model.proto;
option java_outer_classname="MessageProto";
option java_outer_classname = "MessageProto";
message Model {
int64 id = 1;
string action = 2;
string content = 3;
string sender = 4;
string receiver = 5;
string extra = 6;
string title = 7;
string format = 8;
int64 timestamp = 9;
int64 id = 1;
string action = 2;
string content = 3;
string sender = 4;
string receiver = 5;
string extra = 6;
string title = 7;
string format = 8;
int64 timestamp = 9;
}

View File

@ -1,13 +1,13 @@
syntax = "proto3";
package com.farsunset.cim.sdk.android.model.proto;
option java_outer_classname="ReplyBodyProto";
option java_outer_classname = "ReplyBodyProto";
message Model {
string key = 1;
string code = 2;
string message = 3;
int64 timestamp =4;
map<string,string> data =5;
string key = 1;
string code = 2;
string message = 3;
int64 timestamp = 4;
map<string, string> data = 5;
}

View File

@ -1,11 +1,11 @@
syntax = "proto3";
package com.farsunset.cim.sdk.android.model.proto;
option java_outer_classname="SentBodyProto";
option java_outer_classname = "SentBodyProto";
message Model {
string key = 1;
int64 timestamp =2;
map<string,string> data =3;
string key = 1;
int64 timestamp = 2;
map<string, string> data = 3;
}

15
cim-client-sdk/cim-java-sdk/cim-java-sdk.iml Normal file → Executable file
View File

@ -1,2 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />
<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-java:3.11.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
</component>
</module>

0
cim-client-sdk/cim-java-sdk/jar.bat Normal file → Executable file
View File

0
cim-client-sdk/cim-java-sdk/jar.sh Normal file → Executable file
View File

0
cim-client-sdk/cim-java-sdk/pom.xml Normal file → Executable file
View File

View File

@ -25,7 +25,7 @@ import java.util.HashMap;
class CIMCacheManager {
private static HashMap<String, String> CIM_CONFIG_INFO = new HashMap<String, String>();
private static final HashMap<String, String> CIM_CONFIG_INFO = new HashMap<String, String>();
public static final String KEY_MANUAL_STOP = "KEY_MANUAL_STOP";

View File

@ -30,7 +30,6 @@ import java.nio.channels.SocketChannel;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import com.farsunset.cim.sdk.client.coder.CIMLogger;
import com.farsunset.cim.sdk.client.coder.ClientMessageDecoder;
@ -64,18 +63,17 @@ class CIMConnectorManager {
private ByteBuffer readBuffer = ByteBuffer.allocate(READ_BUFFER_SIZE);
private ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"worker-"));
private ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"boss-"));
private ExecutorService eventExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"event-"));
private final ExecutorService workerExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"worker-"));
private final ExecutorService bossExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"boss-"));
private final ExecutorService eventExecutor = Executors.newFixedThreadPool(1, r -> new Thread(r,"event-"));
private Semaphore semaphore = new Semaphore(1, true);
private ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
private ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
private final ClientMessageEncoder messageEncoder = new ClientMessageEncoder();
private final ClientMessageDecoder messageDecoder = new ClientMessageDecoder();
private final ByteBuffer headerBuffer = ByteBuffer.allocate(CIMConstant.DATA_HEADER_LENGTH);
public synchronized static CIMConnectorManager getManager() {
if (manager == null) {
@ -105,8 +103,6 @@ class CIMConnectorManager {
try {
semaphore.acquire();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);
socketChannel.socket().setTcpNoDelay(true);
@ -116,40 +112,30 @@ class CIMConnectorManager {
socketChannel.socket().connect(new InetSocketAddress(host, port),CONNECT_TIME_OUT);
semaphore.release();
handelConnectedEvent();
handleConnectedEvent();
int result = -1;
while((result = socketChannel.read(readBuffer)) > 0) {
if(readBuffer.position() == readBuffer.capacity()) {
extendByteBuffer();
}
handelSocketReadEvent(result);
/*
*开始读取来自服务端的消息先读取3个字节的消息头
*/
while (socketChannel.read(headerBuffer) > 0) {
handleSocketReadEvent();
}
handelSocketReadEvent(result);
/*
*read 返回 <= 0的情况发生了意外需要断开重链
*/
closeSession();
}catch(ConnectException | SocketTimeoutException ignore){
semaphore.release();
handleConnectAbortedEvent();
} catch(IOException ignore) {
semaphore.release();
handelDisconnectedEvent();
}catch (InterruptedException ignore) {
semaphore.release();
handleDisconnectedEvent();
}
});
}
private void handelDisconnectedEvent() {
private void handleDisconnectedEvent() {
closeSession();
}
@ -166,51 +152,27 @@ class CIMConnectorManager {
}
private void handelConnectedEvent() {
private void handleConnectedEvent() {
sessionCreated();
}
private void handelSocketReadEvent(int result) {
if(result == -1) {
closeSession();
return;
}
readBuffer.position(0);
Object message = messageDecoder.doDecode(readBuffer);
if(message == null) {
private void handleSocketReadEvent() throws IOException {
Object message = messageDecoder.doDecode(headerBuffer,socketChannel);
LOGGER.messageReceived(socketChannel, message);
if (isHeartbeatRequest(message)) {
send(HeartbeatResponse.getInstance());
return;
}
LOGGER.messageReceived(socketChannel,message);
if(isHeartbeatRequest(message)) {
send(getHeartbeatResponse());
return;
}
this.messageReceived(message);
}
private void extendByteBuffer() {
ByteBuffer newBuffer = ByteBuffer.allocate(readBuffer.capacity() + READ_BUFFER_SIZE / 2);
readBuffer.position(0);
newBuffer.put(readBuffer);
readBuffer.clear();
readBuffer = newBuffer;
}
public void send(final Protobufable body) {
if(!isConnected()) {
@ -221,8 +183,6 @@ class CIMConnectorManager {
int result = 0;
try {
semaphore.acquire();
ByteBuffer buffer = messageEncoder.encode(body);
while(buffer.hasRemaining()){
result += socketChannel.write(buffer);
@ -232,8 +192,6 @@ class CIMConnectorManager {
result = -1;
}finally {
semaphore.release();
if(result <= 0) {
closeSession();
}else {
@ -302,10 +260,6 @@ class CIMConnectorManager {
}
}
public HeartbeatResponse getHeartbeatResponse() {
return HeartbeatResponse.getInstance();
}
public boolean isHeartbeatRequest(Object data) {
return data instanceof HeartbeatRequest;
}

View File

@ -38,7 +38,7 @@ public class CIMEventBroadcastReceiver {
private static CIMEventBroadcastReceiver receiver;
private CIMEventListener listener;
private ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, r -> {
private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, r -> {
Thread thread = new Thread(r);
thread.setName("cim-reconnect-");
return thread;

View File

@ -36,8 +36,8 @@ import com.farsunset.cim.sdk.client.model.ReplyBody;
*/
public class CIMListenerManager {
private static ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>();
private static CIMMessageReceiveComparator comparator = new CIMMessageReceiveComparator();
private static final ArrayList<CIMEventListener> cimListeners = new ArrayList<CIMEventListener>();
private static final CIMMessageReceiveComparator comparator = new CIMMessageReceiveComparator();
private static final Logger LOGGER = LoggerFactory.getLogger(CIMListenerManager.class);
public static void registerMessageListener(CIMEventListener listener) {
@ -56,9 +56,9 @@ public class CIMListenerManager {
}
}
public static void notifyOnConnectionSuccessed(boolean antoBind) {
public static void notifyOnConnectFinished(boolean autoBind) {
for (CIMEventListener listener : cimListeners) {
listener.onConnectFinished(antoBind);
listener.onConnectFinished(autoBind);
}
}
@ -80,13 +80,13 @@ public class CIMListenerManager {
}
}
public static void notifyOnConnectionFailed() {
public static void notifyOnConnectFailed() {
for (CIMEventListener listener : cimListeners) {
listener.onConnectFailed();
}
}
public static void destory() {
public static void destroy() {
cimListeners.clear();
}

View File

@ -87,10 +87,10 @@ public class CIMPushManager {
protected static void connect() {
boolean isManualStop = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualStop || isManualDestroy) {
if (isManualStopped || isManualDestroyed) {
return;
}
@ -124,8 +124,8 @@ public class CIMPushManager {
*/
public static void bindAccount(String account) {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy || account == null || account.trim().length() == 0) {
boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroyed || account == null || account.trim().length() == 0) {
return;
}
sendBindRequest(account);
@ -136,9 +136,9 @@ public class CIMPushManager {
String account = getAccount();
boolean isManualDestory = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
boolean isManualStoped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
if (isManualStoped || account == null || account.trim().length() == 0 || isManualDestory) {
boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
if (isManualStopped || account == null || account.trim().length() == 0 || isManualDestroyed) {
return false;
}
@ -154,10 +154,10 @@ public class CIMPushManager {
*/
public static void sendRequest(SentBody body) {
boolean isManualStop = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualStop || isManualDestroy) {
if (isManualStopped || isManualDestroyed) {
return;
}
@ -174,8 +174,8 @@ public class CIMPushManager {
*/
public static void stop() {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy) {
boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroyed) {
return;
}
@ -199,12 +199,12 @@ public class CIMPushManager {
}
/**
* 重新恢复接收推送重新连接服务端并登录当前账号如果aotuBind == true
* 重新恢复接收推送重新连接服务端并登录当前账号如果autoBind == true
*/
public static void resume() {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy) {
boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroyed) {
return;
}
@ -216,13 +216,13 @@ public class CIMPushManager {
}
public static int getState() {
boolean isManualDestroy = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroy) {
boolean isManualDestroyed = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_CIM_DESTROYED);
if (isManualDestroyed) {
return STATE_DESTROYED;
}
boolean isManualStop = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
if (isManualStop) {
boolean isManualStopped = CIMCacheManager.getInstance().getBoolean(CIMCacheManager.KEY_MANUAL_STOP);
if (isManualStopped) {
return STATE_STOPPED;
}

View File

@ -29,7 +29,7 @@ import com.farsunset.cim.sdk.client.model.SentBody;
*/
public class CIMPushService {
private CIMConnectorManager manager;
private final CIMConnectorManager manager;
private static CIMPushService service;

View File

@ -42,7 +42,7 @@ public class CIMLogger {
}
private static class LoggerHolder{
private static CIMLogger logger = new CIMLogger();
private static final CIMLogger logger = new CIMLogger();
}

View File

@ -22,7 +22,9 @@
package com.farsunset.cim.sdk.client.coder;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import com.farsunset.cim.sdk.client.constant.CIMConstant;
import com.farsunset.cim.sdk.client.model.HeartbeatRequest;
@ -30,60 +32,56 @@ import com.farsunset.cim.sdk.client.model.Message;
import com.farsunset.cim.sdk.client.model.ReplyBody;
import com.farsunset.cim.sdk.model.proto.MessageProto;
import com.farsunset.cim.sdk.model.proto.ReplyBodyProto;
import com.google.protobuf.InvalidProtocolBufferException;
/**
* 客户端消息解码
*/
public class ClientMessageDecoder {
/**
*
* @param headerBuffer 读取到的消息头
* @param socketChannel
* @return
*/
public Object doDecode(ByteBuffer headerBuffer , SocketChannel socketChannel) throws IOException {
headerBuffer.position(0);
byte type = headerBuffer.get();
byte lv = headerBuffer.get();
byte hv = headerBuffer.get();
headerBuffer.clear();
public Object doDecode(ByteBuffer iobuffer) {
/*
* 消息头3位
* 先通过消息头拿到消息的长度然后进行定长读取
* 解决消息的断包和粘包情况
*/
if (iobuffer.remaining() < CIMConstant.DATA_HEADER_LENGTH) {
return null;
}
iobuffer.mark();
int dataLength = getContentLength(lv, hv);
byte type = iobuffer.get();
ByteBuffer bodyBuffer = ByteBuffer.allocate(dataLength);
byte lv = iobuffer.get();
byte hv = iobuffer.get();
/*
* 如果读取的消息长度不够则进行等待后续消息到来
* 当读取的消息长度(bodyBuffer.position() == dataLength)时意味着一个完整的消息已经接收完成
*/
do {
socketChannel.read(bodyBuffer);
} while (bodyBuffer.position() != dataLength);
int length = getContentLength(lv, hv);
// 如果消息体没有接收完整则重置读取等待下一次重新读取
if (length > iobuffer.remaining()) {
iobuffer.reset();
return null;
}
byte[] dataBytes = new byte[length];
iobuffer.get(dataBytes, 0, length);
iobuffer.position(0);
try {
return mappingMessageObject(dataBytes, type);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
return null;
}
}
private Object mappingMessageObject(byte[] bytes, byte type) throws InvalidProtocolBufferException {
/*
消息读取完成后通过type来解析成对应的消息体
*/
if (CIMConstant.ProtobufType.S_H_RQ == type) {
return HeartbeatRequest.getInstance();
}
if (CIMConstant.ProtobufType.REPLY_BODY == type) {
ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bytes);
ReplyBodyProto.Model bodyProto = ReplyBodyProto.Model.parseFrom(bodyBuffer.array());
ReplyBody body = new ReplyBody();
body.setKey(bodyProto.getKey());
body.setTimestamp(bodyProto.getTimestamp());
@ -93,29 +91,22 @@ public class ClientMessageDecoder {
return body;
}
if (CIMConstant.ProtobufType.MESSAGE == type) {
MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bytes);
Message message = new Message();
message.setId(bodyProto.getId());
message.setAction(bodyProto.getAction());
message.setContent(bodyProto.getContent());
message.setSender(bodyProto.getSender());
message.setReceiver(bodyProto.getReceiver());
message.setTitle(bodyProto.getTitle());
message.setExtra(bodyProto.getExtra());
message.setTimestamp(bodyProto.getTimestamp());
message.setFormat(bodyProto.getFormat());
return message;
}
return null;
MessageProto.Model bodyProto = MessageProto.Model.parseFrom(bodyBuffer.array());
Message message = new Message();
message.setId(bodyProto.getId());
message.setAction(bodyProto.getAction());
message.setContent(bodyProto.getContent());
message.setSender(bodyProto.getSender());
message.setReceiver(bodyProto.getReceiver());
message.setTitle(bodyProto.getTitle());
message.setExtra(bodyProto.getExtra());
message.setTimestamp(bodyProto.getTimestamp());
message.setFormat(bodyProto.getFormat());
return message;
}
/**
* 解析消息体长度
*
* @return
*/
private int getContentLength(byte lv, byte hv) {
int l = (lv & 0xff);
@ -123,4 +114,5 @@ public class ClientMessageDecoder {
return (l | h << 8);
}
}

View File

@ -32,15 +32,14 @@ import com.farsunset.cim.sdk.client.model.Protobufable;
*/
public class ClientMessageEncoder {
public ByteBuffer encode(Object object) {
public ByteBuffer encode(Protobufable body) {
Protobufable data = (Protobufable) object;
byte[] byteArray = data.getByteArray();
byte[] data = body.getByteArray();
ByteBuffer ioBuffer = ByteBuffer.allocate(byteArray.length + CIMConstant.DATA_HEADER_LENGTH);
ByteBuffer ioBuffer = ByteBuffer.allocate(data.length + CIMConstant.DATA_HEADER_LENGTH);
ioBuffer.put(createHeader(data.getType(), byteArray.length));
ioBuffer.put(byteArray);
ioBuffer.put(createHeader(body.getType(), data.length));
ioBuffer.put(data);
ioBuffer.flip();
return ioBuffer;

View File

@ -27,26 +27,11 @@ package com.farsunset.cim.sdk.client.constant;
public interface CIMConstant {
long RECONNECT_INTERVAL_TIME = 30 * 1000;
/**
/*
* 消息头长度为3个字节第一个字节为消息类型第二第三字节 转换int后为消息长度
*/
int DATA_HEADER_LENGTH = 3;
interface ReturnCode {
String CODE_404 = "404";
String CODE_403 = "403";
String CODE_405 = "405";
String CODE_200 = "200";
String CODE_206 = "206";
String CODE_500 = "500";
}
interface ConfigKey {

View File

@ -35,7 +35,7 @@ public class HeartbeatRequest implements Serializable, Protobufable {
private static final String TAG = "SERVER_HEARTBEAT_REQUEST";
private static final String CMD_HEARTBEAT_RESPONSE = "SR";
private static HeartbeatRequest object = new HeartbeatRequest();
private static final HeartbeatRequest object = new HeartbeatRequest();
private HeartbeatRequest() {

View File

@ -34,7 +34,7 @@ public class HeartbeatResponse implements Serializable, Protobufable {
private static final String TAG = "CLIENT_HEARTBEAT_RESPONSE";
private static final String CMD_HEARTBEAT_RESPONSE = "CR";
private static HeartbeatResponse object = new HeartbeatResponse();
private static final HeartbeatResponse object = new HeartbeatResponse();
private HeartbeatResponse() {

View File

@ -34,7 +34,7 @@ public class Intent implements Serializable {
private String action;
private HashMap<String, Object> data = new HashMap<String, Object>();
private final HashMap<String, Object> data = new HashMap<String, Object>();
public Intent() {
}

View File

@ -1,13 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<compositeConfiguration>
<compositeBuild compositeDefinitionSource="SCRIPT" />
</compositeConfiguration>
<option name="testRunner" value="PLATFORM" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>

View File

@ -4,6 +4,8 @@
<modules>
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/cim-client-android.iml" filepath="$PROJECT_DIR$/cim-client-android.iml" />
<module fileurl="file://$PROJECT_DIR$/app/cim-client-android-app.iml" filepath="$PROJECT_DIR$/app/cim-client-android-app.iml" group="cim-client-android/app" />
<module fileurl="file://$PROJECT_DIR$/cim-use-examples-cim-client-android.iml" filepath="$PROJECT_DIR$/cim-use-examples-cim-client-android.iml" group="cim-client-android" />
</modules>
</component>
</project>

View File

@ -65,11 +65,6 @@ public class SystemMsgListViewAdapter extends BaseAdapter {
return 0;
}
@Override
public void notifyDataSetChanged() {
//Collections.sort(list, new MessageTimeDescComparator());
}
@SuppressLint("ViewHolder")
@Override
public View getView(int position, View chatItemView, ViewGroup parent) {

View File

@ -24,7 +24,7 @@ package com.farsunset.ichat.example.app;
public interface Constant {
//服务端IP地址
String CIM_SERVER_HOST = "192.168.1.88";
String CIM_SERVER_HOST = "192.168.2.103";
//注意这里的端口不是tomcat的端口没改动就使用默认的23456
int CIM_SERVER_PORT = 23456;

View File

@ -88,7 +88,7 @@ public class LoginActivity extends CIMMonitorActivity implements OnClickListener
/*
* 收到code为200的回应 账号绑定成功
*/
if (reply.getKey().equals(CIMConstant.RequestKey.CLIENT_BIND) && reply.getCode().equals(CIMConstant.ReturnCode.CODE_200)) {
if (reply.getKey().equals(CIMConstant.RequestKey.CLIENT_BIND) && reply.getCode().equals("200")) {
Intent intent = new Intent(this, SystemMessageActivity.class);
intent.putExtra("account", accountEdit.getText().toString().trim());
startActivity(intent);

View File

@ -87,9 +87,14 @@ public class SystemMessageActivity extends CIMMonitorActivity implements OnClick
startActivity(intent);
this.finish();
} else {
list.add(message);
list.add(0,message);
adapter.notifyDataSetChanged();
chatListView.setSelection(chatListView.getTop());
chatListView.postDelayed(new Runnable() {
@Override
public void run() {
chatListView.setSelection(0);
}
},500);
}
}

View File

@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.android.tools.build:gradle:3.6.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -1,7 +1,6 @@
#Mon May 13 14:19:31 CST 2019
#Fri Mar 06 13:42:48 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

View File

@ -0,0 +1,24 @@
<?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-java:3.11.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="module-library">
<library name="Maven: com.farsunset:cim-java-sdk:3.8.0">
<CLASSES>
<root url="jar://$MODULE_DIR$/libs/cim-java-sdk-3.8.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>