154 changed files with 12245 additions and 0 deletions
@ -0,0 +1,25 @@ |
|||
/target/ |
|||
!.mvn/wrapper/maven-wrapper.jar |
|||
|
|||
### STS ### |
|||
.apt_generated |
|||
.classpath |
|||
.factorypath |
|||
.project |
|||
.settings |
|||
.springBeans |
|||
.sts4-cache |
|||
|
|||
### IntelliJ IDEA ### |
|||
.idea |
|||
*.iws |
|||
*.iml |
|||
*.ipr |
|||
|
|||
### NetBeans ### |
|||
/nbproject/private/ |
|||
/build/ |
|||
/nbbuild/ |
|||
/dist/ |
|||
/nbdist/ |
|||
/.nb-gradle/ |
Binary file not shown.
@ -0,0 +1 @@ |
|||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip |
@ -0,0 +1,36 @@ |
|||
# ccmq |
|||
|
|||
#### Description |
|||
MQ(Rest) 2 User(ws,tcp text,tcp hex) |
|||
|
|||
#### Software Architecture |
|||
Software architecture description |
|||
|
|||
#### Installation |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### Instructions |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### Contribution |
|||
|
|||
1. Fork the repository |
|||
2. Create Feat_xxx branch |
|||
3. Commit your code |
|||
4. Create Pull Request |
|||
|
|||
|
|||
#### Gitee Feature |
|||
|
|||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md |
|||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com) |
|||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) |
|||
4. The most valuable open source project [GVP](https://gitee.com/gvp) |
|||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) |
|||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
@ -0,0 +1,37 @@ |
|||
# ccmq |
|||
|
|||
#### 介绍 |
|||
MQ(Rest) 2 User(ws,tcp text,tcp hex) |
|||
|
|||
#### 软件架构 |
|||
软件架构说明 |
|||
|
|||
|
|||
#### 安装教程 |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### 使用说明 |
|||
|
|||
1. xxxx |
|||
2. xxxx |
|||
3. xxxx |
|||
|
|||
#### 参与贡献 |
|||
|
|||
1. Fork 本仓库 |
|||
2. 新建 Feat_xxx 分支 |
|||
3. 提交代码 |
|||
4. 新建 Pull Request |
|||
|
|||
|
|||
#### 码云特技 |
|||
|
|||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md |
|||
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) |
|||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 |
|||
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 |
|||
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) |
|||
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) |
@ -0,0 +1,286 @@ |
|||
#!/bin/sh |
|||
# ---------------------------------------------------------------------------- |
|||
# Licensed to the Apache Software Foundation (ASF) under one |
|||
# or more contributor license agreements. See the NOTICE file |
|||
# distributed with this work for additional information |
|||
# regarding copyright ownership. The ASF licenses this file |
|||
# to you under the Apache License, Version 2.0 (the |
|||
# "License"); you may not use this file except in compliance |
|||
# with the License. You may obtain a copy of the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, |
|||
# software distributed under the License is distributed on an |
|||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|||
# KIND, either express or implied. See the License for the |
|||
# specific language governing permissions and limitations |
|||
# under the License. |
|||
# ---------------------------------------------------------------------------- |
|||
|
|||
# ---------------------------------------------------------------------------- |
|||
# Maven2 Start Up Batch script |
|||
# |
|||
# Required ENV vars: |
|||
# ------------------ |
|||
# JAVA_HOME - location of a JDK home dir |
|||
# |
|||
# Optional ENV vars |
|||
# ----------------- |
|||
# M2_HOME - location of maven2's installed home dir |
|||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven |
|||
# e.g. to debug Maven itself, use |
|||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
|||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
|||
# ---------------------------------------------------------------------------- |
|||
|
|||
if [ -z "$MAVEN_SKIP_RC" ] ; then |
|||
|
|||
if [ -f /etc/mavenrc ] ; then |
|||
. /etc/mavenrc |
|||
fi |
|||
|
|||
if [ -f "$HOME/.mavenrc" ] ; then |
|||
. "$HOME/.mavenrc" |
|||
fi |
|||
|
|||
fi |
|||
|
|||
# OS specific support. $var _must_ be set to either true or false. |
|||
cygwin=false; |
|||
darwin=false; |
|||
mingw=false |
|||
case "`uname`" in |
|||
CYGWIN*) cygwin=true ;; |
|||
MINGW*) mingw=true;; |
|||
Darwin*) darwin=true |
|||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home |
|||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html |
|||
if [ -z "$JAVA_HOME" ]; then |
|||
if [ -x "/usr/libexec/java_home" ]; then |
|||
export JAVA_HOME="`/usr/libexec/java_home`" |
|||
else |
|||
export JAVA_HOME="/Library/Java/Home" |
|||
fi |
|||
fi |
|||
;; |
|||
esac |
|||
|
|||
if [ -z "$JAVA_HOME" ] ; then |
|||
if [ -r /etc/gentoo-release ] ; then |
|||
JAVA_HOME=`java-config --jre-home` |
|||
fi |
|||
fi |
|||
|
|||
if [ -z "$M2_HOME" ] ; then |
|||
## resolve links - $0 may be a link to maven's home |
|||
PRG="$0" |
|||
|
|||
# need this for relative symlinks |
|||
while [ -h "$PRG" ] ; do |
|||
ls=`ls -ld "$PRG"` |
|||
link=`expr "$ls" : '.*-> \(.*\)$'` |
|||
if expr "$link" : '/.*' > /dev/null; then |
|||
PRG="$link" |
|||
else |
|||
PRG="`dirname "$PRG"`/$link" |
|||
fi |
|||
done |
|||
|
|||
saveddir=`pwd` |
|||
|
|||
M2_HOME=`dirname "$PRG"`/.. |
|||
|
|||
# make it fully qualified |
|||
M2_HOME=`cd "$M2_HOME" && pwd` |
|||
|
|||
cd "$saveddir" |
|||
# echo Using m2 at $M2_HOME |
|||
fi |
|||
|
|||
# For Cygwin, ensure paths are in UNIX format before anything is touched |
|||
if $cygwin ; then |
|||
[ -n "$M2_HOME" ] && |
|||
M2_HOME=`cygpath --unix "$M2_HOME"` |
|||
[ -n "$JAVA_HOME" ] && |
|||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"` |
|||
[ -n "$CLASSPATH" ] && |
|||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"` |
|||
fi |
|||
|
|||
# For Mingw, ensure paths are in UNIX format before anything is touched |
|||
if $mingw ; then |
|||
[ -n "$M2_HOME" ] && |
|||
M2_HOME="`(cd "$M2_HOME"; pwd)`" |
|||
[ -n "$JAVA_HOME" ] && |
|||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" |
|||
# TODO classpath? |
|||
fi |
|||
|
|||
if [ -z "$JAVA_HOME" ]; then |
|||
javaExecutable="`which javac`" |
|||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then |
|||
# readlink(1) is not available as standard on Solaris 10. |
|||
readLink=`which readlink` |
|||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then |
|||
if $darwin ; then |
|||
javaHome="`dirname \"$javaExecutable\"`" |
|||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" |
|||
else |
|||
javaExecutable="`readlink -f \"$javaExecutable\"`" |
|||
fi |
|||
javaHome="`dirname \"$javaExecutable\"`" |
|||
javaHome=`expr "$javaHome" : '\(.*\)/bin'` |
|||
JAVA_HOME="$javaHome" |
|||
export JAVA_HOME |
|||
fi |
|||
fi |
|||
fi |
|||
|
|||
if [ -z "$JAVACMD" ] ; then |
|||
if [ -n "$JAVA_HOME" ] ; then |
|||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
|||
# IBM's JDK on AIX uses strange locations for the executables |
|||
JAVACMD="$JAVA_HOME/jre/sh/java" |
|||
else |
|||
JAVACMD="$JAVA_HOME/bin/java" |
|||
fi |
|||
else |
|||
JAVACMD="`which java`" |
|||
fi |
|||
fi |
|||
|
|||
if [ ! -x "$JAVACMD" ] ; then |
|||
echo "Error: JAVA_HOME is not defined correctly." >&2 |
|||
echo " We cannot execute $JAVACMD" >&2 |
|||
exit 1 |
|||
fi |
|||
|
|||
if [ -z "$JAVA_HOME" ] ; then |
|||
echo "Warning: JAVA_HOME environment variable is not set." |
|||
fi |
|||
|
|||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher |
|||
|
|||
# traverses directory structure from process work directory to filesystem root |
|||
# first directory with .mvn subdirectory is considered project base directory |
|||
find_maven_basedir() { |
|||
|
|||
if [ -z "$1" ] |
|||
then |
|||
echo "Path not specified to find_maven_basedir" |
|||
return 1 |
|||
fi |
|||
|
|||
basedir="$1" |
|||
wdir="$1" |
|||
while [ "$wdir" != '/' ] ; do |
|||
if [ -d "$wdir"/.mvn ] ; then |
|||
basedir=$wdir |
|||
break |
|||
fi |
|||
# workaround for JBEAP-8937 (on Solaris 10/Sparc) |
|||
if [ -d "${wdir}" ]; then |
|||
wdir=`cd "$wdir/.."; pwd` |
|||
fi |
|||
# end of workaround |
|||
done |
|||
echo "${basedir}" |
|||
} |
|||
|
|||
# concatenates all lines of a file |
|||
concat_lines() { |
|||
if [ -f "$1" ]; then |
|||
echo "$(tr -s '\n' ' ' < "$1")" |
|||
fi |
|||
} |
|||
|
|||
BASE_DIR=`find_maven_basedir "$(pwd)"` |
|||
if [ -z "$BASE_DIR" ]; then |
|||
exit 1; |
|||
fi |
|||
|
|||
########################################################################################## |
|||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
|||
# This allows using the maven wrapper in projects that prohibit checking in binary data. |
|||
########################################################################################## |
|||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo "Found .mvn/wrapper/maven-wrapper.jar" |
|||
fi |
|||
else |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." |
|||
fi |
|||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
|||
while IFS="=" read key value; do |
|||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;; |
|||
esac |
|||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo "Downloading from: $jarUrl" |
|||
fi |
|||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" |
|||
|
|||
if command -v wget > /dev/null; then |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo "Found wget ... using wget" |
|||
fi |
|||
wget "$jarUrl" -O "$wrapperJarPath" |
|||
elif command -v curl > /dev/null; then |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo "Found curl ... using curl" |
|||
fi |
|||
curl -o "$wrapperJarPath" "$jarUrl" |
|||
else |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo "Falling back to using Java to download" |
|||
fi |
|||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" |
|||
if [ -e "$javaClass" ]; then |
|||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo " - Compiling MavenWrapperDownloader.java ..." |
|||
fi |
|||
# Compiling the Java class |
|||
("$JAVA_HOME/bin/javac" "$javaClass") |
|||
fi |
|||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
|||
# Running the downloader |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo " - Running MavenWrapperDownloader.java ..." |
|||
fi |
|||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") |
|||
fi |
|||
fi |
|||
fi |
|||
fi |
|||
########################################################################################## |
|||
# End of extension |
|||
########################################################################################## |
|||
|
|||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} |
|||
if [ "$MVNW_VERBOSE" = true ]; then |
|||
echo $MAVEN_PROJECTBASEDIR |
|||
fi |
|||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" |
|||
|
|||
# For Cygwin, switch paths to Windows format before running java |
|||
if $cygwin; then |
|||
[ -n "$M2_HOME" ] && |
|||
M2_HOME=`cygpath --path --windows "$M2_HOME"` |
|||
[ -n "$JAVA_HOME" ] && |
|||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` |
|||
[ -n "$CLASSPATH" ] && |
|||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"` |
|||
[ -n "$MAVEN_PROJECTBASEDIR" ] && |
|||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` |
|||
fi |
|||
|
|||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
|||
|
|||
exec "$JAVACMD" \ |
|||
$MAVEN_OPTS \ |
|||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ |
|||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ |
|||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" |
@ -0,0 +1,161 @@ |
|||
@REM ---------------------------------------------------------------------------- |
|||
@REM Licensed to the Apache Software Foundation (ASF) under one |
|||
@REM or more contributor license agreements. See the NOTICE file |
|||
@REM distributed with this work for additional information |
|||
@REM regarding copyright ownership. The ASF licenses this file |
|||
@REM to you under the Apache License, Version 2.0 (the |
|||
@REM "License"); you may not use this file except in compliance |
|||
@REM with the License. You may obtain a copy of the License at |
|||
@REM |
|||
@REM http://www.apache.org/licenses/LICENSE-2.0 |
|||
@REM |
|||
@REM Unless required by applicable law or agreed to in writing, |
|||
@REM software distributed under the License is distributed on an |
|||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|||
@REM KIND, either express or implied. See the License for the |
|||
@REM specific language governing permissions and limitations |
|||
@REM under the License. |
|||
@REM ---------------------------------------------------------------------------- |
|||
|
|||
@REM ---------------------------------------------------------------------------- |
|||
@REM Maven2 Start Up Batch script |
|||
@REM |
|||
@REM Required ENV vars: |
|||
@REM JAVA_HOME - location of a JDK home dir |
|||
@REM |
|||
@REM Optional ENV vars |
|||
@REM M2_HOME - location of maven2's installed home dir |
|||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands |
|||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending |
|||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven |
|||
@REM e.g. to debug Maven itself, use |
|||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
|||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
|||
@REM ---------------------------------------------------------------------------- |
|||
|
|||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' |
|||
@echo off |
|||
@REM set title of command window |
|||
title %0 |
|||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' |
|||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% |
|||
|
|||
@REM set %HOME% to equivalent of $HOME |
|||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") |
|||
|
|||
@REM Execute a user defined script before this one |
|||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre |
|||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending |
|||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" |
|||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" |
|||
:skipRcPre |
|||
|
|||
@setlocal |
|||
|
|||
set ERROR_CODE=0 |
|||
|
|||
@REM To isolate internal variables from possible post scripts, we use another setlocal |
|||
@setlocal |
|||
|
|||
@REM ==== START VALIDATION ==== |
|||
if not "%JAVA_HOME%" == "" goto OkJHome |
|||
|
|||
echo. |
|||
echo Error: JAVA_HOME not found in your environment. >&2 |
|||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
|||
echo location of your Java installation. >&2 |
|||
echo. |
|||
goto error |
|||
|
|||
:OkJHome |
|||
if exist "%JAVA_HOME%\bin\java.exe" goto init |
|||
|
|||
echo. |
|||
echo Error: JAVA_HOME is set to an invalid directory. >&2 |
|||
echo JAVA_HOME = "%JAVA_HOME%" >&2 |
|||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
|||
echo location of your Java installation. >&2 |
|||
echo. |
|||
goto error |
|||
|
|||
@REM ==== END VALIDATION ==== |
|||
|
|||
:init |
|||
|
|||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". |
|||
@REM Fallback to current working directory if not found. |
|||
|
|||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% |
|||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir |
|||
|
|||
set EXEC_DIR=%CD% |
|||
set WDIR=%EXEC_DIR% |
|||
:findBaseDir |
|||
IF EXIST "%WDIR%"\.mvn goto baseDirFound |
|||
cd .. |
|||
IF "%WDIR%"=="%CD%" goto baseDirNotFound |
|||
set WDIR=%CD% |
|||
goto findBaseDir |
|||
|
|||
:baseDirFound |
|||
set MAVEN_PROJECTBASEDIR=%WDIR% |
|||
cd "%EXEC_DIR%" |
|||
goto endDetectBaseDir |
|||
|
|||
:baseDirNotFound |
|||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR% |
|||
cd "%EXEC_DIR%" |
|||
|
|||
:endDetectBaseDir |
|||
|
|||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig |
|||
|
|||
@setlocal EnableExtensions EnableDelayedExpansion |
|||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a |
|||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% |
|||
|
|||
:endReadAdditionalConfig |
|||
|
|||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" |
|||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" |
|||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
|||
|
|||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" |
|||
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( |
|||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B |
|||
) |
|||
|
|||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
|||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data. |
|||
if exist %WRAPPER_JAR% ( |
|||
echo Found %WRAPPER_JAR% |
|||
) else ( |
|||
echo Couldn't find %WRAPPER_JAR%, downloading it ... |
|||
echo Downloading from: %DOWNLOAD_URL% |
|||
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" |
|||
echo Finished downloading %WRAPPER_JAR% |
|||
) |
|||
@REM End of extension |
|||
|
|||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* |
|||
if ERRORLEVEL 1 goto error |
|||
goto end |
|||
|
|||
:error |
|||
set ERROR_CODE=1 |
|||
|
|||
:end |
|||
@endlocal & set ERROR_CODE=%ERROR_CODE% |
|||
|
|||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost |
|||
@REM check for post script, once with legacy .bat ending and once with .cmd ending |
|||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" |
|||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" |
|||
:skipRcPost |
|||
|
|||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' |
|||
if "%MAVEN_BATCH_PAUSE%" == "on" pause |
|||
|
|||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% |
|||
|
|||
exit /B %ERROR_CODE% |
@ -0,0 +1,275 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<groupId>com.ccsens</groupId> |
|||
<artifactId>tall-message</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>tall-message</name> |
|||
<description>Tall Message MicroService</description> |
|||
|
|||
<parent> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-parent</artifactId> |
|||
<version>2.1.6.RELEASE</version> |
|||
<relativePath/> <!-- lookup parent from repository --> |
|||
</parent> |
|||
|
|||
<properties> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
|||
<java.version>1.8</java.version> |
|||
<spring-cloud.version>Greenwich.SR1</spring-cloud.version> |
|||
<docker.image.prefix>springboot</docker.image.prefix> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<!--<dependency>--> |
|||
<!--<groupId>org.springframework.cloud</groupId>--> |
|||
<!--<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>--> |
|||
<!--</dependency>--> |
|||
|
|||
<!--<dependency>--> |
|||
<!--<groupId>org.springframework.cloud</groupId>--> |
|||
<!--<artifactId>spring-cloud-starter-openfeign</artifactId>--> |
|||
<!--</dependency>--> |
|||
|
|||
<!--<dependency>--> |
|||
<!--<groupId>org.springframework.cloud</groupId>--> |
|||
<!--<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>--> |
|||
<!--</dependency>--> |
|||
|
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-devtools</artifactId> |
|||
<optional>true</optional> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-configuration-processor</artifactId> |
|||
<optional>true</optional> |
|||
</dependency> |
|||
|
|||
<!-- spring Redis--> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-data-redis</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- spring RabbitMQ--> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-amqp</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- spring MongoDB--> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-data-mongodb</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- spring MyBatis --> |
|||
<dependency> |
|||
<groupId>org.mybatis.spring.boot</groupId> |
|||
<artifactId>mybatis-spring-boot-starter</artifactId> |
|||
<version>1.1.1</version> |
|||
</dependency> |
|||
|
|||
<!--jfinal enjoy 模板引擎--> |
|||
<dependency> |
|||
<groupId>com.jfinal</groupId> |
|||
<artifactId>enjoy</artifactId> |
|||
<version>4.7</version> |
|||
</dependency> |
|||
|
|||
<!-- mybatis pagehelper包--> |
|||
<dependency> |
|||
<groupId>com.github.pagehelper</groupId> |
|||
<artifactId>pagehelper</artifactId> |
|||
<version>4.1.4</version> |
|||
</dependency> |
|||
|
|||
<!--mybatis generator包--> |
|||
<dependency> |
|||
<groupId>org.mybatis.generator</groupId> |
|||
<artifactId>mybatis-generator-core</artifactId> |
|||
<version>1.3.7</version> |
|||
</dependency> |
|||
|
|||
<!-- MySQL数据库 --> |
|||
<dependency> |
|||
<groupId>mysql</groupId> |
|||
<artifactId>mysql-connector-java</artifactId> |
|||
</dependency> |
|||
|
|||
<!--Durid数据源--> |
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>druid</artifactId> |
|||
<version>1.1.10</version> |
|||
</dependency> |
|||
|
|||
<!-- hutool-all --> |
|||
<dependency> |
|||
<groupId>cn.hutool</groupId> |
|||
<artifactId>hutool-all</artifactId> |
|||
<version>4.1.21</version> |
|||
</dependency> |
|||
|
|||
<!--jjwt--> |
|||
<dependency> |
|||
<groupId>io.jsonwebtoken</groupId> |
|||
<artifactId>jjwt</artifactId> |
|||
<version>0.7.0</version> |
|||
</dependency> |
|||
|
|||
<!--Jackson Xml--> |
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.dataformat</groupId> |
|||
<artifactId>jackson-dataformat-xml</artifactId> |
|||
<version>2.9.0</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.jayway.jsonpath</groupId> |
|||
<artifactId>json-path</artifactId> |
|||
<version>2.4.0</version> |
|||
</dependency> |
|||
|
|||
<!--Shiro --> |
|||
<dependency> |
|||
<groupId>org.apache.shiro</groupId> |
|||
<artifactId>shiro-spring</artifactId> |
|||
<version>1.4.0</version> |
|||
</dependency> |
|||
|
|||
<!--Swagger --> |
|||
<dependency> |
|||
<groupId>io.springfox</groupId> |
|||
<artifactId>springfox-swagger2</artifactId> |
|||
<version>2.9.2</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>io.springfox</groupId> |
|||
<artifactId>springfox-swagger-ui</artifactId> |
|||
<version>2.9.2</version> |
|||
</dependency> |
|||
|
|||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> |
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
<version>1.18.2</version> |
|||
<scope>provided</scope> |
|||
</dependency> |
|||
|
|||
<!-- https://mvnrepository.com/artifact/org.beanshell/bsh --> |
|||
<dependency> |
|||
<groupId>org.beanshell</groupId> |
|||
<artifactId>bsh</artifactId> |
|||
<version>2.0b5</version> |
|||
</dependency> |
|||
|
|||
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> |
|||
<dependency> |
|||
<groupId>org.apache.httpcomponents</groupId> |
|||
<artifactId>httpclient</artifactId> |
|||
<version>4.3.1</version> |
|||
</dependency> |
|||
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore --> |
|||
<dependency> |
|||
<groupId>org.apache.httpcomponents</groupId> |
|||
<artifactId>httpcore</artifactId> |
|||
<version>4.3.1</version> |
|||
</dependency> |
|||
<!-- https://mvnrepository.com/artifact/io.netty/netty-all --> |
|||
<dependency> |
|||
<groupId>io.netty</groupId> |
|||
<artifactId>netty-all</artifactId> |
|||
<version>4.1.32.Final</version> |
|||
</dependency> |
|||
|
|||
<!--Poi Excel --> |
|||
<dependency> |
|||
<groupId>org.apache.poi</groupId> |
|||
<artifactId>poi</artifactId> |
|||
<version>3.17</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.apache.poi</groupId> |
|||
<artifactId>poi-ooxml</artifactId> |
|||
<version>3.17</version> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
<dependencyManagement> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.springframework.cloud</groupId> |
|||
<artifactId>spring-cloud-dependencies</artifactId> |
|||
<version>${spring-cloud.version}</version> |
|||
<type>pom</type> |
|||
<scope>import</scope> |
|||
</dependency> |
|||
</dependencies> |
|||
</dependencyManagement> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.mybatis.generator</groupId> |
|||
<artifactId>mybatis-generator-maven-plugin</artifactId> |
|||
<version>1.3.7</version> |
|||
<configuration> |
|||
<configurationFile>${basedir}/src/main/resources/mbg.xml</configurationFile> |
|||
<overwrite>true</overwrite> |
|||
</configuration> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>mysql</groupId> |
|||
<artifactId>mysql-connector-java</artifactId> |
|||
<version>5.1.34</version> |
|||
</dependency> |
|||
</dependencies> |
|||
</plugin> |
|||
<!-- Docker maven plugin --> |
|||
<plugin> |
|||
<groupId>com.spotify</groupId> |
|||
<artifactId>docker-maven-plugin</artifactId> |
|||
<version>1.0.0</version> |
|||
<configuration> |
|||
<imageName>${docker.image.prefix}/${project.artifactId}</imageName> |
|||
<dockerDirectory>src/main/docker</dockerDirectory> |
|||
<resources> |
|||
<resource> |
|||
<targetPath>/</targetPath> |
|||
<directory>${project.build.directory}</directory> |
|||
<include>${project.build.finalName}.jar</include> |
|||
</resource> |
|||
</resources> |
|||
</configuration> |
|||
</plugin> |
|||
<!-- Docker maven plugin --> |
|||
</plugins> |
|||
</build> |
|||
</project> |
@ -0,0 +1,234 @@ |
|||
package com.ccsens.ccmq.lowlevel.client; |
|||
|
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.WrapperedChannel; |
|||
import com.ccsens.ccmq.lowlevel.message.MessageHandler; |
|||
import com.ccsens.ccmq.lowlevel.message.client.ClientAuthTimeOutMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.*; |
|||
import com.ccsens.ccmq.lowlevel.message.server.ServerAckMessage; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager; |
|||
import com.ccsens.ccmq.lowlevel.client.rabbitmq.QueueManager; |
|||
import com.ccsens.ccmq.lowlevel.client.restful.RestManager; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import io.netty.channel.Channel; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.scheduling.annotation.Scheduled; |
|||
import org.springframework.stereotype.Component; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
|
|||
import java.util.Iterator; |
|||
import java.util.Map; |
|||
|
|||
import static com.ccsens.ccmq.lowlevel.message.MessageHandler.handleMessage; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@Component |
|||
public class ClientManager { |
|||
private static Logger logger = LoggerFactory.getLogger(ClientManager.class); |
|||
/** |
|||
* 未认证连接存活时间(s) |
|||
*/ |
|||
private static final Long UnAuthedChannelMaxAliveTimeInSeconds = 30L; |
|||
|
|||
@Scheduled(cron = "0/30 * * * * *") |
|||
public void closeUnAuthedChannels() throws Exception{ |
|||
logger.debug("Invoke closeUnAuthedChannels({})",UnAuthedChannelMaxAliveTimeInSeconds); |
|||
|
|||
Map<Channel, WrapperedChannel> allChannels = ChannelManager.getCopyOfAllChannels(); |
|||
Iterator<Map.Entry<Channel,WrapperedChannel>> it = allChannels.entrySet().iterator(); |
|||
while(it.hasNext()){ |
|||
Map.Entry<Channel,WrapperedChannel> entry = it.next(); |
|||
WrapperedChannel wrapperedChannel = entry.getValue(); |
|||
if((!wrapperedChannel.isAuthed()) && (wrapperedChannel.getConnectedSeconds() > UnAuthedChannelMaxAliveTimeInSeconds)) { |
|||
//构造AuthTimeOut消息并处理
|
|||
try { |
|||
ChannelManager.setCurrentChannel(wrapperedChannel.getChannel()); |
|||
MessageHandler.handleMessage(InMessage.newToServerMessage(MessageConstant.DomainType.User, |
|||
new ClientAuthTimeOutMessage())); |
|||
//close会触发remove
|
|||
wrapperedChannel.getChannel().close(); |
|||
logger.debug("Remove a unAuthed Channel {}, which has connected {}s,maxOutTime is {}s", |
|||
wrapperedChannel.getId(), wrapperedChannel.getConnectedSeconds(), UnAuthedChannelMaxAliveTimeInSeconds |
|||
); |
|||
}catch(Exception e){ |
|||
e.printStackTrace(); |
|||
logger.error("handle ClientAuthTimeOutMessage error: {}",e.getMessage()); |
|||
}finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Scheduled(cron="*/30 * * * * ?") |
|||
public void showChannels(){ |
|||
logger.debug("----------------- AuthedChannels(" + System.currentTimeMillis()/1000 + ")--------------"); |
|||
ChannelManager.showAuthedChannels(); |
|||
logger.debug("----------------- AllChannels -----------------"); |
|||
ChannelManager.showAllChannels(); |
|||
logger.debug("------------------End -------------------------"); |
|||
} |
|||
|
|||
public static InMessage fillMessageInfo(InMessage inMessage, MessageConstant.DomainType fromDomain){ |
|||
//1. 填充from、toDomain、Rule
|
|||
switch (fromDomain){ |
|||
case User: { |
|||
//1.填充Message from信息
|
|||
inMessage.setFromDomain(MessageConstant.DomainType.User); |
|||
inMessage.setFrom(ChannelManager.getUserIdByChannel(ChannelManager.getCurrentChannel())); |
|||
|
|||
//2.填充Message to信息
|
|||
if (null == inMessage.getToDomain()) { |
|||
inMessage.setToDomain(MessageConstant.DomainType.Queue); |
|||
} |
|||
|
|||
//3.填充规则缺省值
|
|||
if (null == inMessage.getRule()) { |
|||
inMessage.setRule(MessageRule.defaultRule(inMessage.getFromDomain())); |
|||
} |
|||
break; |
|||
} |
|||
case Queue: { |
|||
//1.填充Message from信息
|
|||
inMessage.setFromDomain(MessageConstant.DomainType.Queue); |
|||
inMessage.setFrom(QueueManager.getInQueueName()); |
|||
|
|||
//2.填充Message to信息
|
|||
if(null == inMessage.getToDomain()){ |
|||
inMessage.setToDomain( MessageConstant.DomainType.User); |
|||
} |
|||
|
|||
//3.填充规则缺省值
|
|||
if (null == inMessage.getRule()) { |
|||
inMessage.setRule(MessageRule.defaultRule(inMessage.getFromDomain())); |
|||
} |
|||
break; |
|||
} |
|||
case Rest: { |
|||
//1.填充Message from信息
|
|||
inMessage.setFromDomain(MessageConstant.DomainType.Rest); |
|||
inMessage.setFrom(""); |
|||
|
|||
//2.填充Message to信息
|
|||
if (null == inMessage.getToDomain()) { |
|||
inMessage.setToDomain(MessageConstant.DomainType.User); |
|||
} |
|||
|
|||
//3.填充规则缺省值
|
|||
if (null == inMessage.getRule()) { |
|||
inMessage.setRule(MessageRule.defaultRule(inMessage.getFromDomain())); |
|||
} |
|||
break; |
|||
} |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
//2.填充tos值
|
|||
if (null == inMessage.getTos()) { |
|||
inMessage.setTos(CollectionUtil.newHashSet()); |
|||
} else { |
|||
inMessage.getTos().removeIf(to -> to == null || StrUtil.isEmpty(to)); |
|||
} |
|||
switch (inMessage.getToDomain()) { |
|||
case Queue: { |
|||
if (CollectionUtil.isEmpty(inMessage.getTos())) { |
|||
inMessage.getTos().add(QueueManager.getOutQueueName()); |
|||
} |
|||
break; |
|||
} |
|||
case Rest:{ |
|||
if (CollectionUtil.isEmpty(inMessage.getTos())) { |
|||
inMessage.getTos().add(RestManager.getOutRestName()); |
|||
} |
|||
break; |
|||
} |
|||
default: { |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return inMessage; |
|||
} |
|||
|
|||
public static void sendMessageToAuthedClient(MessageConstant.DomainType toDomain, String to, OutMessageSet outMessageSet) throws JsonProcessingException { |
|||
switch (toDomain){ |
|||
case User: |
|||
if(StrUtil.isNotEmpty(to)) { |
|||
ChannelManager.sendTo(to, outMessageSet); |
|||
} |
|||
break; |
|||
case Queue: |
|||
QueueManager.sendTo(outMessageSet); |
|||
break; |
|||
case Rest: |
|||
RestManager.sendTo(outMessageSet); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public static void sendServerMessage(MessageConstant.DomainType toDomain, OutMessageSet outMessageSet) throws JsonProcessingException { |
|||
switch (toDomain){ |
|||
case User: |
|||
ChannelManager.sendTo(ChannelManager.getCurrentChannel(), outMessageSet); |
|||
break; |
|||
case Queue: |
|||
QueueManager.sendTo(outMessageSet); |
|||
break; |
|||
case Rest: |
|||
RestManager.sendTo(outMessageSet); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
public static void sendServerAck(InMessage inMessage, MessageConstant.Error error) throws Exception { |
|||
switch (error) { |
|||
case Ok: |
|||
if (inMessage.getRule().getAckRule() != MessageRule.AckRule.ALWAYS) { |
|||
return; |
|||
} |
|||
break; |
|||
default: |
|||
if (inMessage.getRule().getAckRule() == MessageRule.AckRule.NONE) { |
|||
return; |
|||
} |
|||
break; |
|||
} |
|||
OutMessageSet outMessageSet = OutMessageSet.newInstance().ackId(null).add( |
|||
new OutMessage(MessageConstant.DomainType.Server, |
|||
JacksonUtil.beanToJson( |
|||
new ServerAckMessage(inMessage.getId(),inMessage.getUnikey(),error) |
|||
) |
|||
) |
|||
); |
|||
sendServerMessage(inMessage.getFromDomain(),outMessageSet); |
|||
} |
|||
|
|||
public static boolean isSenderAuthed(InMessage inMessage) { |
|||
if (inMessage.getFromDomain() == MessageConstant.DomainType.User) { |
|||
return ChannelManager.channelAuthed(ChannelManager.getCurrentChannel()); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
public static void closeCurrentSender(InMessage message) { |
|||
if(message.getFromDomain() == MessageConstant.DomainType.User){ |
|||
ChannelManager.getCurrentChannel().close(); |
|||
} |
|||
} |
|||
|
|||
public static boolean isUserOnline(MessageConstant.DomainType domain, String to) { |
|||
if (domain == MessageConstant.DomainType.User) { |
|||
return ChannelManager.isUserOnline(to); |
|||
} |
|||
return true; |
|||
} |
|||
} |
@ -0,0 +1,366 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty; |
|||
|
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import com.ccsens.ccmq.lowlevel.message.client.ClientAuthTimeOutMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import io.netty.channel.Channel; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import java.util.*; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
import static com.ccsens.ccmq.lowlevel.message.MessageHandler.handleMessage; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
public class ChannelManager { |
|||
private static Logger logger = LoggerFactory.getLogger(ChannelManager.class); |
|||
private static ThreadLocal<Channel> threadLocal = new ThreadLocal<>(); |
|||
|
|||
/** |
|||
* UserId,WrappedChannel authed channels; |
|||
*/ |
|||
private static Map<String,Set<WrapperedChannel>> authedChannels; |
|||
/** |
|||
* Channel,WrapperedChannel |
|||
*/ |
|||
private static Map<Channel,WrapperedChannel> rawChannels; |
|||
|
|||
static { |
|||
authedChannels = new ConcurrentHashMap<>(); |
|||
rawChannels = new ConcurrentHashMap<>(); |
|||
} |
|||
|
|||
/** |
|||
* 私有构造,不允许生成该类对象 |
|||
*/ |
|||
private ChannelManager(){} |
|||
|
|||
/** |
|||
* 设置当前正在错误的channel |
|||
* @param channel netty ws/tcp连接 |
|||
*/ |
|||
public static void setCurrentChannel(Channel channel){ |
|||
threadLocal.set(channel); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前线程的channel对象 |
|||
* @return channel |
|||
*/ |
|||
public static Channel getCurrentChannel(){ |
|||
return threadLocal.get(); |
|||
} |
|||
|
|||
/** |
|||
* 删除当前线程的channel对象 |
|||
*/ |
|||
public static void removeCurrentChannel(){ |
|||
threadLocal.remove(); |
|||
} |
|||
|
|||
/** |
|||
* 添加一个新的连接(Channel) |
|||
* @param channel 新的连接 |
|||
* @param serverType 服务器类型 |
|||
*/ |
|||
public static synchronized void addChannel(Channel channel,String serverType){ |
|||
logger.debug("Invoke addChannel({},{})",channel,serverType); |
|||
if(null != channel) { |
|||
rawChannels.put(channel, new WrapperedChannel(channel, serverType)); |
|||
logger.debug("Add a new channel: {},{}",channel.id().asLongText(),serverType); |
|||
}else{ |
|||
logger.error("channel is null"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 用户认证 |
|||
* @param channel 连接 |
|||
* @param userId 用户ID |
|||
* @param major 客户端主版本号 |
|||
* @param minor 客户端次版本号 |
|||
*/ |
|||
public static synchronized void authChannel(Channel channel,String userId,Integer major,Integer minor){ |
|||
logger.debug("Invoke authedChannels({},{},{},{})",channel.id().asLongText(),userId,major,minor); |
|||
major = major != null ? major : 0; |
|||
minor = minor != null ? minor : 0; |
|||
|
|||
WrapperedChannel wrapperedChannel = rawChannels.get(channel); |
|||
if(wrapperedChannel != null){ |
|||
wrapperedChannel.whenAuthed(userId,major,minor); |
|||
Set<WrapperedChannel> authedWchannelSet = authedChannels.computeIfAbsent(userId, k -> new HashSet<>()); |
|||
authedWchannelSet.add(wrapperedChannel); |
|||
logger.debug("Authed channel {} with user {}", channel.id().asLongText(), userId); |
|||
}else{ |
|||
logger.error("Authed channel,but wrappedChannel is null."); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 移除一个连接(Channel) |
|||
* @param channel 要移除的连接 |
|||
*/ |
|||
public static synchronized void removeChannel(Channel channel){ |
|||
logger.debug("Invoke removeChannel({})",channel.id().asLongText()); |
|||
WrapperedChannel wrapperedChannel = rawChannels.get(channel); |
|||
if(wrapperedChannel != null){ |
|||
//从rawChannels集合中删除
|
|||
rawChannels.remove(channel); |
|||
logger.debug("Remove a channel from rawChannels: {}",channel.id().asLongText()); |
|||
if(wrapperedChannel.isAuthed()){ |
|||
Set<WrapperedChannel> authedChannelSet = authedChannels.get(wrapperedChannel.getUserId()); |
|||
//从authedChannel集合的value(set)中删除
|
|||
if(CollectionUtil.isNotEmpty(authedChannelSet)){ |
|||
authedChannelSet.remove(wrapperedChannel); |
|||
logger.debug("Remove a channel from authedChannelSet: {}, {}",wrapperedChannel.getUserId(),channel.id().asLongText()); |
|||
} |
|||
//从authedChannel中删除,此处不用else,因为if中语句执行完毕之后,authedChannelSet也可能变成空集合
|
|||
if(CollectionUtil.isEmpty(authedChannelSet)){ |
|||
authedChannels.remove(wrapperedChannel.getUserId()); |
|||
logger.debug("Remove a user from authedChannels: {}",wrapperedChannel.getUserId()); |
|||
} |
|||
} |
|||
}else{ |
|||
logger.error("Remove channel,but wrappedChannel is null."); |
|||
} |
|||
|
|||
if(channel.isOpen() || channel.isActive()){ |
|||
channel.close(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 移除一个用户 |
|||
* @param userId 要移除的用户 |
|||
*/ |
|||
public static synchronized void removeUser(String userId){ |
|||
logger.debug("Invoke remove user : {}",userId); |
|||
Set<WrapperedChannel> wChannelSet = authedChannels.get(userId); |
|||
if(CollectionUtil.isNotEmpty(wChannelSet)){ |
|||
for(WrapperedChannel wChannel : wChannelSet){ |
|||
//从rawChannel中依次删除
|
|||
rawChannels.remove(wChannel.getChannel()); |
|||
logger.debug("Remove a channel from rawChannels: {}",wChannel.getChannel().id().asLongText()); |
|||
} |
|||
} |
|||
//从authedChannel中删除
|
|||
authedChannels.remove(userId); |
|||
logger.debug("Remove a user from authedChannels: {}",userId); |
|||
} |
|||
|
|||
/** |
|||
* 添加版本号 |
|||
* 只能给已认证的请求添加版本号 |
|||
* @param channel 连接 |
|||
* @param major 主版本号 |
|||
* @param minor 次版本号 |
|||
*/ |
|||
public static synchronized void versionChannel(Channel channel,int major,int minor){ |
|||
logger.debug("Invoke Version channel({},{},{}))",channel.id().asLongText(),major,minor); |
|||
WrapperedChannel wChannel = rawChannels.get(channel); |
|||
if(wChannel != null){ |
|||
wChannel.setVersion(major,minor); |
|||
logger.debug("Version Channel: {},{},{}",channel.id().asLongText(),major,minor); |
|||
}else{ |
|||
logger.error("Remove channel,but wrappedChannel is null."); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 发送广播消息给所有已认证Channel |
|||
* @param message 消息 |
|||
*/ |
|||
public static synchronized void broadCastAuthed(Object message) { |
|||
logger.debug("Invoke broadCastAuthed({})",message); |
|||
for (Map.Entry<Channel,WrapperedChannel> entry : rawChannels.entrySet()) { |
|||
WrapperedChannel wChannel = entry.getValue(); |
|||
if(wChannel.isAuthed()) { |
|||
wChannel.writeAndFlush(message); |
|||
logger.debug("Send Message {} to {},{}",message,wChannel.getUserId(),wChannel.getId()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 发送广播消息 |
|||
* @param message 消息 |
|||
*/ |
|||
public static synchronized void broadCast(Object message) { |
|||
logger.debug("Invoke broadCast({})",message); |
|||
for (Map.Entry<Channel,WrapperedChannel> entry : rawChannels.entrySet()) { |
|||
entry.getValue().writeAndFlush(message); |
|||
logger.debug("Send Message {} to {}",message,entry.getValue().getId()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 发送消息给某个连接 |
|||
* @param channel 连接 |
|||
* @param message 消息 |
|||
*/ |
|||
public static synchronized void sendTo(Channel channel,Object message){ |
|||
logger.debug("Invoke sendTo({},{})",channel.id().asLongText(),message); |
|||
WrapperedChannel wrapperedChannel = rawChannels.get(channel); |
|||
if(wrapperedChannel != null) { |
|||
wrapperedChannel.writeAndFlush(message); |
|||
logger.debug("Write message {} to Channel {}",message,channel); |
|||
}else{ |
|||
logger.error("can't find channel from rawChannels"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 发送消息给某个用户 |
|||
* @param userId 用户ID |
|||
* @param message 消息 |
|||
*/ |
|||
public static synchronized boolean sendTo(String userId,Object message){ |
|||
logger.debug("Invoke sendTo({},{})",userId,message); |
|||
Set<WrapperedChannel> wChannelSet = authedChannels.get(userId); |
|||
if(CollectionUtil.isNotEmpty(wChannelSet)){ |
|||
for(WrapperedChannel wChannel:wChannelSet){ |
|||
wChannel.writeAndFlush(message); |
|||
logger.debug("Send message {} to channel {}",message,userId); |
|||
} |
|||
return true; |
|||
}else{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 刷新最后一次接收数据时间 每次接收数据需要调用该函数 |
|||
* @param channel 接收数据的连接 |
|||
*/ |
|||
public static synchronized void flushReceiveTimestamp(Channel channel){ |
|||
logger.debug("Invoke flushReceiveTimestamp({})",channel.id().asLongText()); |
|||
WrapperedChannel wrapperedChannel = rawChannels.get(channel); |
|||
if(wrapperedChannel != null){ |
|||
wrapperedChannel.whenReceivedData(); |
|||
}else{ |
|||
logger.error("can find channel from rawChannels"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 关闭所有未认证的连接 |
|||
* @param unAuthedChannelsMaxAliveTimeInSeconds 未认证的最大时长(s) |
|||
* @throws Exception |
|||
* @deprecated 已过期,该方法在多线程并发下可能出现问题,建议使用messageService中的同名方法 |
|||
*/ |
|||
@Deprecated |
|||
public static synchronized void closeUnAuthedChannels(Long unAuthedChannelsMaxAliveTimeInSeconds) throws Exception { |
|||
logger.debug("Inovke closeUnAuthedChannels({})",unAuthedChannelsMaxAliveTimeInSeconds); |
|||
Iterator<Map.Entry<Channel,WrapperedChannel>> it = rawChannels.entrySet().iterator(); |
|||
while(it.hasNext()){ |
|||
Map.Entry<Channel,WrapperedChannel> entry = it.next(); |
|||
WrapperedChannel wrapperedChannel = entry.getValue(); |
|||
if((!wrapperedChannel.isAuthed()) && (wrapperedChannel.getConnectedSeconds() > unAuthedChannelsMaxAliveTimeInSeconds)) { |
|||
it.remove(); |
|||
//关闭连接
|
|||
wrapperedChannel.getChannel().close(); |
|||
logger.debug("Remove a unAuthed Channel {}, which has connected {}s,maxOutTime is {}s", wrapperedChannel.getId(),wrapperedChannel.getConnectedSeconds() , |
|||
unAuthedChannelsMaxAliveTimeInSeconds ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 判断Channel是否存在 |
|||
* @param channel 连接 |
|||
* @return 是否存在 |
|||
*/ |
|||
public static synchronized boolean channelExist(Channel channel){ |
|||
return rawChannels.containsKey(channel); |
|||
} |
|||
|
|||
/** |
|||
* 判断Channel是否已经认证 |
|||
* @param channel 连接 |
|||
* @return 是否认证 |
|||
*/ |
|||
public static synchronized boolean channelAuthed(Channel channel){ |
|||
return rawChannels.containsKey(channel) && rawChannels.get(channel).isAuthed(); |
|||
} |
|||
|
|||
/** |
|||
* 判断用户是否在线 |
|||
* @param userId 用户ID |
|||
* @return 是否认证 |
|||
*/ |
|||
public static synchronized boolean isUserOnline(String userId){ |
|||
return authedChannels.containsKey(userId); |
|||
} |
|||
|
|||
/** |
|||
* 根据Channel获取对应的用户ID |
|||
* @param channel 连接 |
|||
* @return 用户ID |
|||
*/ |
|||
public static synchronized String getUserIdByChannel(Channel channel) { |
|||
return rawChannels.containsKey(channel) ? rawChannels.get(channel).getUserId() : null; |
|||
} |
|||
|
|||
/** |
|||
* 获取所有在线用户 |
|||
* @return 所有用户的集合列表 |
|||
*/ |
|||
public static synchronized Set<String> getOnlineUsers(){ |
|||
return authedChannels.keySet(); |
|||
} |
|||
|
|||
/** |
|||
* 获取某种类型的所有在线用户的连接 |
|||
* @param type 客户端类型(ws/tcp modebus/tcp text) |
|||
* @return 所有在西安channel的集合列表 |
|||
*/ |
|||
public static synchronized Set<Channel> getOnlineChannels(String type){ |
|||
Set<Channel> onLineChannels = CollectionUtil.newHashSet(); |
|||
for(Map.Entry<Channel,WrapperedChannel> entry: rawChannels.entrySet()){ |
|||
WrapperedChannel wrapperedChannel = entry.getValue(); |
|||
if(wrapperedChannel.isAuthed() && wrapperedChannel.getType().equals(type)){ |
|||
onLineChannels.add(entry.getKey()); |
|||
} |
|||
} |
|||
return onLineChannels; |
|||
} |
|||
|
|||
/** |
|||
* 获取一个rawChannel的副本 |
|||
* @return 副本 |
|||
*/ |
|||
public static Map<Channel, WrapperedChannel> getCopyOfAllChannels() { |
|||
Map<Channel,WrapperedChannel> copyMap = new HashMap<>(rawChannels.size()); |
|||
copyMap.putAll(rawChannels); |
|||
return copyMap; |
|||
} |
|||
|
|||
/** |
|||
* 根据Channel获取WrapperedChannel |
|||
* @param channel channel |
|||
* @return 对应的wrapperedChannel |
|||
*/ |
|||
public static WrapperedChannel getWrapperedChannelByChannel(Channel channel) { |
|||
return rawChannels.get(channel); |
|||
} |
|||
|
|||
public static synchronized void showAuthedChannels(){ |
|||
for(Map.Entry<String,Set<WrapperedChannel>> entry : authedChannels.entrySet()){ |
|||
for (WrapperedChannel channel:entry.getValue()){ |
|||
logger.debug("{}-->{}",entry.getKey(),channel.toString()); |
|||
} |
|||
} |
|||
} |
|||
public static synchronized void showAllChannels(){ |
|||
for(Map.Entry<Channel,WrapperedChannel> entry : rawChannels.entrySet()){ |
|||
logger.debug(entry.getValue().toString()); |
|||
} |
|||
} |
|||
|
|||
public static Set<String> getAllOnlineUsers() { |
|||
return authedChannels.keySet(); |
|||
} |
|||
} |
@ -0,0 +1,200 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty; |
|||
|
|||
import cn.hutool.core.util.ObjectUtil; |
|||
import wiki.tall.ccmq.common.util.DateUtil; |
|||
import io.netty.channel.Channel; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
import java.net.InetSocketAddress; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Getter |
|||
@Setter |
|||
public class WrapperedChannel { |
|||
@Getter |
|||
@Setter |
|||
private static class ClientVersion{ |
|||
int major; |
|||
int minor; |
|||
public ClientVersion(int major,int minor){ |
|||
this.major = major; |
|||
this.minor = minor; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Netty's Channel |
|||
*/ |
|||
private Channel channel; |
|||
/** |
|||
* userId |
|||
*/ |
|||
private String userId; |
|||
/** |
|||
* 客户端版本号 |
|||
*/ |
|||
private ClientVersion version; |
|||
|
|||
/** |
|||
* 客户端类型(哪种服务器) |
|||
*/ |
|||
private String type; |
|||
/** |
|||
* 是否认证 |
|||
*/ |
|||
private boolean authed; |
|||
/** |
|||
* 连接建立时间 |
|||
*/ |
|||
private Long createdAtInSeconds; |
|||
/** |
|||
* 认证时间(s) |
|||
*/ |
|||
private Long authedAtInSeconds; |
|||
/** |
|||
* 最后一次接收有效数据时间(包括心跳包) |
|||
*/ |
|||
private Long lastDataReceivedInSeconds; |
|||
/** |
|||
* 最后一次发送有效数据时间(包括心跳包) |
|||
*/ |
|||
private Long lastDataSendInSeconds; |
|||
/** |
|||
* 接收到数据条数(每接收到一条协议加1,包括心跳) |
|||
*/ |
|||
private long dataReceivedCount; |
|||
/** |
|||
* 发送数据条数(每发送到一条协议加1,包括心跳) |
|||
*/ |
|||
private long dataSendCount; |
|||
|
|||
public WrapperedChannel(Channel channel){ |
|||
this.channel = channel; |
|||
this.createdAtInSeconds = DateUtil.currentSeconds(); |
|||
} |
|||
|
|||
public WrapperedChannel(Channel channel,String type){ |
|||
this(channel); |
|||
this.type = type; |
|||
} |
|||
|
|||
public WrapperedChannel(Channel channel,String type,int major,int minor){ |
|||
this(channel,type); |
|||
this.version = new ClientVersion(major,minor); |
|||
} |
|||
|
|||
public String getVersion(){ |
|||
if(ObjectUtil.isNotNull(version)){ |
|||
return "v" + version.getMajor() + "." + version.getMinor(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public void setVersion(int major,int minor){ |
|||
if (ObjectUtil.isNull(version)) { |
|||
version = new ClientVersion(major, minor); |
|||
} else { |
|||
version.setMajor(major); |
|||
version.setMinor(minor); |
|||
} |
|||
} |
|||
|
|||
public String getRemoteAddr(){ |
|||
if(ObjectUtil.isNotNull(channel)){ |
|||
InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress(); |
|||
if(ObjectUtil.isNotNull(insocket)) { |
|||
return insocket.getAddress().getHostAddress(); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
public Integer getRemotePort(){ |
|||
if(ObjectUtil.isNotNull(channel)){ |
|||
InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress(); |
|||
if(ObjectUtil.isNotNull(insocket)) { |
|||
return insocket.getPort(); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public String getId(){ |
|||
if(ObjectUtil.isNotNull(channel)){ |
|||
return channel.id().asLongText(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public void whenReceivedData(){ |
|||
lastDataReceivedInSeconds = DateUtil.currentSeconds(); |
|||
dataReceivedCount++; |
|||
} |
|||
|
|||
public void whenSendData(){ |
|||
lastDataSendInSeconds = DateUtil.currentSeconds(); |
|||
dataSendCount++; |
|||
} |
|||
|
|||
public void whenAuthed(){ |
|||
authed = true; |
|||
authedAtInSeconds = DateUtil.currentSeconds(); |
|||
} |
|||
|
|||
public void whenAuthed(String userId,int major,int minor){ |
|||
whenAuthed(); |
|||
setVersion(major,minor); |
|||
this.userId = userId; |
|||
} |
|||
|
|||
public void writeAndFlush(Object message){ |
|||
if(channel != null && channel.isActive()){ |
|||
channel.writeAndFlush(message); |
|||
whenSendData(); |
|||
} |
|||
} |
|||
|
|||
public Long getConnectedSeconds(){ |
|||
return createdAtInSeconds == null ? null : DateUtil.currentSeconds() - createdAtInSeconds; |
|||
} |
|||
|
|||
public Long getOnlineSeconds(){ |
|||
return authedAtInSeconds == null ? null : DateUtil.currentSeconds() - authedAtInSeconds ; |
|||
} |
|||
|
|||
@Override |
|||
public boolean equals(Object obj) { |
|||
if(ObjectUtil.isNull(obj)) { |
|||
return false; |
|||
} |
|||
if(this == obj) { |
|||
return true; |
|||
} |
|||
if(ObjectUtil.isNull(channel)) { |
|||
return false; |
|||
} |
|||
if(obj.getClass() == this.getClass()){ |
|||
WrapperedChannel other = (WrapperedChannel)obj; |
|||
return channel.equals(other.channel); |
|||
}else if(obj.getClass() == channel.getClass()){ |
|||
Channel other = (Channel)obj; |
|||
return channel.equals(other); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public int hashCode() { |
|||
if(ObjectUtil.isNotNull(channel)){ |
|||
return channel.hashCode(); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return String.format("id: %s, type: %s, authed: %b, userId: %s, version: %s",getId(),type,authed,userId,getVersion()); |
|||
} |
|||
} |
@ -0,0 +1,115 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver; |
|||
|
|||
public class ModbusConverter { |
|||
|
|||
// public static BaseMessageDto convertCCModbusToMessage(CCModBusEntity ccModBusEntity) {
|
|||
// BaseMessageDto message = null;
|
|||
// int addr = ccModBusEntity.getAddr() & 0xFF;
|
|||
// int oper = ccModBusEntity.getOper() & 0xFF;
|
|||
// WebConstant.Message_Type type = WebConstant.Message_Type.valueOf(addr);
|
|||
// if(type != null) {
|
|||
// switch (type) {
|
|||
// case Heart: {
|
|||
// message = toHeartMessage(addr,oper,ccModBusEntity.getOriginData());
|
|||
// break;
|
|||
// }
|
|||
// case Auth:{
|
|||
// message = toAuthMessage(addr,oper,ccModBusEntity.getOriginData());
|
|||
// }
|
|||
// }
|
|||
// }
|
|||
// return message;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * OriginaData ==> 0x00
|
|||
// * @param oper
|
|||
// * @return
|
|||
// */
|
|||
// private static BaseMessageDto toHeartMessage(int addr, int oper, byte[] originData){
|
|||
// HeartMessageDto message = new HeartMessageDto();
|
|||
// return message;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * OriginaData ==> 0x00 0x00 0x00 0x64
|
|||
// * @param oper
|
|||
// * @return
|
|||
// */
|
|||
// private static BaseMessageDto toAuthMessage(int addr, int oper, byte[] originData){
|
|||
// Long userId = 0L;
|
|||
// for(int i=0;i<originData.length;i++){
|
|||
// userId <<= 8;
|
|||
// userId |= originData[i] & 0xFF;
|
|||
// }
|
|||
// AuthMessageDto message = new AuthMessageDto(userId,null);
|
|||
// return message;
|
|||
// }
|
|||
//
|
|||
// public static CCModBusEntity convertCommonProtocolToCCModbus(BaseMessageDto message) {
|
|||
// CCModBusEntity ccModBusEntity = null;
|
|||
// WebConstant.Message_Type type = WebConstant.Message_Type.phaseOf(message.getType());
|
|||
// switch (type){
|
|||
// case Heart:{
|
|||
// ccModBusEntity = fromHeartMessage((HeartMessageDto)message);
|
|||
// break;
|
|||
// }
|
|||
// case Timer:{
|
|||
// WebConstant.Message_Timer_Event event = WebConstant.Message_Timer_Event.phaseOf(message.getEvent());
|
|||
// switch (event){
|
|||
// case CountDown:
|
|||
// ccModBusEntity = fromTimerMessageWithCountDown((TimerMessageWithCountDownDto)message);
|
|||
// break;
|
|||
// case Clock:
|
|||
// ccModBusEntity = fromTimerMessageWithClock((TimerMessageWithClockDto)message);
|
|||
// break;
|
|||
// }
|
|||
// break;
|
|||
// }
|
|||
// }
|
|||
// return ccModBusEntity;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * OriginalData: 0x00
|
|||
// * @param message
|
|||
// * @return
|
|||
// */
|
|||
// private static CCModBusEntity fromHeartMessage(HeartMessageDto message){
|
|||
// byte []originData = new byte[1];
|
|||
// originData[0] = 0x00;
|
|||
// byte addr = (byte)(WebConstant.Message_Type.phaseOf(message.getType()).value & 0xFF);
|
|||
// byte oper = (byte)(WebConstant.Message_Type.phaseOf(message.getEvent()).value & 0xFF);
|
|||
// CCModBusEntity ccModBusEntity = new CCModBusEntity(addr,oper,originData);
|
|||
// return ccModBusEntity;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * originaData: a1 xx xx xx crc
|
|||
// * @param message
|
|||
// * @return
|
|||
// */
|
|||
// private static CCModBusEntity fromTimerMessageWithCountDown(TimerMessageWithCountDownDto message){
|
|||
// Long second = message.getData().getSecond();
|
|||
// byte []originData = new byte[5];
|
|||
// originData[0] = (byte)(0xa1 & 0xFF);
|
|||
// originData[1] = (0x48 & 0xFF);
|
|||
// originData[2] = (byte)((second >> 8) & 0xFF);
|
|||
// originData[3] = (byte)(second & 0xFF);
|
|||
// originData[4] = (byte)((originData[0] + originData[1] + originData[2] + originData[3]) & 0xFF);
|
|||
//
|
|||
// byte addr = (byte)(WebConstant.Message_Type.phaseOf(message.getType()).value & 0xFF);
|
|||
// byte oper = (byte)(WebConstant.Message_Timer_Event.phaseOf(message.getEvent()).value & 0xFF);
|
|||
// CCModBusEntity ccModBusEntity = new CCModBusEntity(addr,oper,originData);
|
|||
// return ccModBusEntity;
|
|||
// }
|
|||
//
|
|||
// /**
|
|||
// * originaData: xxx
|
|||
// * @param message
|
|||
// * @return
|
|||
// */
|
|||
// private static CCModBusEntity fromTimerMessageWithClock(TimerMessageWithClockDto message){
|
|||
// return null;
|
|||
// }
|
|||
} |
@ -0,0 +1,45 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver; |
|||
|
|||
import io.netty.buffer.ByteBuf; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.handler.codec.ByteToMessageDecoder; |
|||
|
|||
import java.util.List; |
|||
|
|||
public class ModbusDecoder extends ByteToMessageDecoder { |
|||
|
|||
private void discardNBytes(ByteBuf in, int length) { |
|||
for (int i = 0; i < length; i++) { |
|||
in.readByte(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception { |
|||
// if (in.readableBytes() < CCModBusEntity.SIZE_MIN) {
|
|||
// //长度小于协议的最小长度,继续读下一次
|
|||
// return;
|
|||
// }
|
|||
//
|
|||
// CCModBusEntity ccModBusEntity = new CCModBusEntity(in);
|
|||
// CCModBusEntity.Error error = ccModBusEntity.valid();
|
|||
// switch (error) {
|
|||
// case ERROR_FILTER_NOT_MATCH:
|
|||
// case ERROR_LEN_EXCLUEE_MAX:
|
|||
// case ERROR_CRC_INVALID: //丢弃一个字节,继续读取
|
|||
// discardNBytes(in, 1);
|
|||
// return;
|
|||
// case ERROR_NEED_MORE_DATA: //继续读取
|
|||
// return;
|
|||
// case ERROR_NONE:
|
|||
// break;
|
|||
// }
|
|||
//
|
|||
// //交给下个handler处理
|
|||
// discardNBytes(in, ccModBusEntity.getModbusLength());
|
|||
// BaseMessageDto message = ModbusConverter.convertCCModbusToMessage(ccModBusEntity);
|
|||
// if(ObjectUtil.isNotNull(message)) {
|
|||
// out.add(message);
|
|||
// }
|
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet; |
|||
import io.netty.buffer.ByteBuf; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.handler.codec.MessageToByteEncoder; |
|||
|
|||
public class ModbusEncoder extends MessageToByteEncoder<OutMessageSet> { |
|||
@Override |
|||
protected void encode(ChannelHandlerContext channelHandlerContext, OutMessageSet message, ByteBuf out) throws Exception { |
|||
//
|
|||
// //1.处理Start->Timer[Countdown]
|
|||
// if(message != null && message instanceof SyncMessageWithStartDto){
|
|||
// SyncMessageWithStartDto startMessage = (SyncMessageWithStartDto)message;
|
|||
// if(ObjectUtil.isNotNull(startMessage.getData().getBeginTaskId())){
|
|||
// Long duration = startMessage.getData().getDuration();
|
|||
// message = new TimerMessageWithCountDownDto(duration/1000,null,null);
|
|||
// }
|
|||
// }
|
|||
//
|
|||
// if(message != null){
|
|||
// CCModBusEntity ccModBusEntity = ModbusConverter.convertCommonProtocolToCCModbus(message);
|
|||
// if(ccModBusEntity != null){
|
|||
// byte[] modbusData = ccModBusEntity.getModbusData();
|
|||
// for(int i=0;i<modbusData.length;i++){
|
|||
// System.out.printf("%02x ",modbusData[i]);
|
|||
// }
|
|||
// System.out.println();
|
|||
// out.writeBytes(modbusData);
|
|||
// }
|
|||
// }
|
|||
} |
|||
} |
@ -0,0 +1,109 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.client.ClientIdleClosedMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.server.UnExceptedErrorMessage; |
|||
import com.ccsens.ccmq.lowlevel.client.ClientManager; |
|||
import com.ccsens.ccmq.lowlevel.message.MessageHandler; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import wiki.tall.ccmq.common.util.WebConstant; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager; |
|||
import io.netty.channel.ChannelHandler; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.channel.SimpleChannelInboundHandler; |
|||
import io.netty.handler.timeout.IdleState; |
|||
import io.netty.handler.timeout.IdleStateEvent; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@ChannelHandler.Sharable |
|||
@Component |
|||
public class ModbusHandler extends SimpleChannelInboundHandler<InMessage> { |
|||
private Logger logger = LoggerFactory.getLogger(ModbusHandler.class); |
|||
|
|||
private static final String TYPE = WebConstant.NETTY_SERVER_TYPE.TCP_MB.phase; |
|||
|
|||
@Override |
|||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { |
|||
} |
|||
|
|||
@Override |
|||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { |
|||
} |
|||
|
|||
@Override |
|||
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
|||
ChannelManager.addChannel(ctx.channel(),TYPE); |
|||
} |
|||
|
|||
@Override |
|||
public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
|||
ChannelManager.removeChannel(ctx.channel()); |
|||
} |
|||
|
|||
@Override |
|||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
|||
try { |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
MessageHandler.handleMessage( |
|||
InMessage.newToServerMessage(MessageConstant.DomainType.User,new UnExceptedErrorMessage(cause.getMessage()))); |
|||
}catch(Exception e){ |
|||
e.printStackTrace(); |
|||
logger.error("TcpHex exceptionCaught handler error: {}",e.getMessage()); |
|||
}finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
cause.printStackTrace(); |
|||
ctx.close(); |
|||
logger.error("Channel closed. TcpHex get a exception: {}", cause.getMessage()); |
|||
} |
|||
|
|||
@SuppressWarnings("all") |
|||
@Override |
|||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { |
|||
if (evt instanceof IdleStateEvent) { |
|||
IdleStateEvent idleStateEvent = (IdleStateEvent) evt; |
|||
if (idleStateEvent.state() == IdleState.READER_IDLE) { |
|||
try { |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
MessageHandler.handleMessage( |
|||
InMessage.newToServerMessage(MessageConstant.DomainType.User,new ClientIdleClosedMessage())); |
|||
}catch(Exception e){ |
|||
e.printStackTrace(); |
|||
logger.error("TcpHex exceptionCaught handler error: {}",e.getMessage()); |
|||
}finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
ctx.channel().close(); |
|||
logger.error("TcpHex channel idle,closed."); |
|||
} |
|||
} else { |
|||
super.userEventTriggered(ctx, evt); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void channelRead0(ChannelHandlerContext ctx, InMessage inMessage) throws Exception { |
|||
try { |
|||
//0.设置最后一次收数据的时间 和 当前线程channel
|
|||
ChannelManager.flushReceiveTimestamp(ctx.channel()); |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
|
|||
//1.填充缺省信息
|
|||
ClientManager.fillMessageInfo(inMessage, MessageConstant.DomainType.User); |
|||
|
|||
//2.处理消息
|
|||
MessageHandler.handleMessage(inMessage); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
logger.error("TcpHex Process Message Failed: {},{}", e.getMessage(), inMessage); |
|||
throw e; |
|||
} finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver; |
|||
|
|||
import io.netty.bootstrap.ServerBootstrap; |
|||
import io.netty.channel.*; |
|||
import io.netty.channel.nio.NioEventLoopGroup; |
|||
import io.netty.channel.socket.SocketChannel; |
|||
import io.netty.channel.socket.nio.NioServerSocketChannel; |
|||
import io.netty.handler.logging.LogLevel; |
|||
import io.netty.handler.logging.LoggingHandler; |
|||
import io.netty.handler.timeout.IdleStateHandler; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.scheduling.annotation.Async; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@Component |
|||
public class NettyMBServer { |
|||
@Autowired |
|||
private ModbusHandler modbusHandler; |
|||
private static final short SERVER_PORT = 8195; |
|||
|
|||
@Async |
|||
public void start() { |
|||
// Configure the server.
|
|||
EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
|||
EventLoopGroup workerGroup = new NioEventLoopGroup(); |
|||
try { |
|||
ServerBootstrap b = new ServerBootstrap(); |
|||
b.group(bossGroup, workerGroup) |
|||
.channel(NioServerSocketChannel.class) |
|||
.option(ChannelOption.SO_BACKLOG, 100) |
|||
.handler(new LoggingHandler(LogLevel.INFO)) |
|||
.childHandler(new ChannelInitializer<SocketChannel>() { |
|||
@Override |
|||
public void initChannel(SocketChannel ch) throws Exception { |
|||
ChannelPipeline p = ch.pipeline(); |
|||
p.addLast(new IdleStateHandler(40, 0, 0, TimeUnit.SECONDS)); |
|||
p.addLast(new ModbusDecoder()); |
|||
p.addLast(new ModbusEncoder()); |
|||
p.addLast(modbusHandler); |
|||
} |
|||
}); |
|||
// Start the server.
|
|||
ChannelFuture f = b.bind(SERVER_PORT).sync(); |
|||
// Wait until the server socket is closed.
|
|||
f.channel().closeFuture().sync(); |
|||
} catch(Exception e){ |
|||
e.printStackTrace(); |
|||
}finally { |
|||
// Shut down all event loops to terminate all threads.
|
|||
bossGroup.shutdownGracefully(); |
|||
workerGroup.shutdownGracefully(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,61 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver; |
|||
|
|||
import io.netty.bootstrap.ServerBootstrap; |
|||
import io.netty.buffer.Unpooled; |
|||
import io.netty.channel.*; |
|||
import io.netty.channel.nio.NioEventLoopGroup; |
|||
import io.netty.channel.socket.SocketChannel; |
|||
import io.netty.channel.socket.nio.NioServerSocketChannel; |
|||
import io.netty.handler.codec.DelimiterBasedFrameDecoder; |
|||
import io.netty.handler.logging.LogLevel; |
|||
import io.netty.handler.logging.LoggingHandler; |
|||
import io.netty.handler.timeout.IdleStateHandler; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.scheduling.annotation.Async; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Component |
|||
public class NettyTextServer { |
|||
@Autowired |
|||
private TextHandler textHandler; |
|||
@Autowired |
|||
private static final String DELIMITER = "_$$_"; |
|||
private static final short SERVER_PORT = 8199; |
|||
|
|||
@Async |
|||
public void start() { |
|||
// Configure the server.
|
|||
EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
|||
EventLoopGroup workerGroup = new NioEventLoopGroup(); |
|||
try { |
|||
ServerBootstrap b = new ServerBootstrap(); |
|||
b.group(bossGroup, workerGroup) |
|||
.channel(NioServerSocketChannel.class) |
|||
.option(ChannelOption.SO_BACKLOG, 100) |
|||
.handler(new LoggingHandler(LogLevel.INFO)) |
|||
.childHandler(new ChannelInitializer<SocketChannel>() { |
|||
@Override |
|||
public void initChannel(SocketChannel ch) throws Exception { |
|||
ChannelPipeline p = ch.pipeline(); |
|||
p.addLast(new IdleStateHandler(40, 0, 0, TimeUnit.SECONDS)); |
|||
p.addLast(new DelimiterBasedFrameDecoder(2048, Unpooled.copiedBuffer(DELIMITER.getBytes()))); |
|||
p.addLast(new TextDecoder()); |
|||
p.addLast(new TextEncoder(2048,Unpooled.copiedBuffer(DELIMITER.getBytes()))); |
|||
p.addLast(textHandler); |
|||
} |
|||
}); |
|||
// Start the server.
|
|||
ChannelFuture f = b.bind(SERVER_PORT).sync(); |
|||
// Wait until the server socket is closed.
|
|||
f.channel().closeFuture().sync(); |
|||
} catch(Exception e){ |
|||
e.printStackTrace(); |
|||
}finally { |
|||
// Shut down all event loops to terminate all threads.
|
|||
bossGroup.shutdownGracefully(); |
|||
workerGroup.shutdownGracefully(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
import io.netty.buffer.ByteBuf; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.handler.codec.MessageToMessageDecoder; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import java.io.IOException; |
|||
import java.nio.charset.Charset; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
public class TextDecoder extends MessageToMessageDecoder<ByteBuf> { |
|||
private static Logger logger = LoggerFactory.getLogger(TextDecoder.class); |
|||
|
|||
@Override |
|||
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List<Object> out) throws Exception { |
|||
String msgString = msg.toString(Charset.defaultCharset()); |
|||
logger.info("TcpText received: {}" + msgString); |
|||
|
|||
try { |
|||
out.add(JacksonUtil.jsonToBean(msgString, InMessage.class)); |
|||
}catch (IOException e){ |
|||
e.printStackTrace(); |
|||
logger.error("TcpText read error: {}",msgString); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
import io.netty.buffer.ByteBuf; |
|||
import io.netty.buffer.ByteBufUtil; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.handler.codec.MessageToMessageEncoder; |
|||
import java.nio.CharBuffer; |
|||
import java.nio.charset.Charset; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
public class TextEncoder extends MessageToMessageEncoder<OutMessageSet> { |
|||
private final Charset charset; |
|||
private int maxLength; |
|||
private ByteBuf delimiter; |
|||
|
|||
public TextEncoder() { |
|||
this(Charset.defaultCharset()); |
|||
} |
|||
|
|||
public TextEncoder(Charset charset) { |
|||
if (charset == null) { |
|||
throw new NullPointerException("charset"); |
|||
} else { |
|||
this.charset = charset; |
|||
} |
|||
} |
|||
|
|||
public TextEncoder(int maxLength, ByteBuf delimiter) { |
|||
this(); |
|||
this.maxLength = maxLength; |
|||
this.delimiter = delimiter; |
|||
} |
|||
|
|||
@Override |
|||
protected void encode(ChannelHandlerContext ctx, OutMessageSet outMessageSet, List<Object> out) throws Exception { |
|||
String delimiterString = delimiter.toString(Charset.defaultCharset()); |
|||
String msgString = JacksonUtil.beanToJson(outMessageSet) + delimiterString; |
|||
|
|||
if (msgString.length() != 0) { |
|||
out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msgString), this.charset)); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,110 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.client.ClientIdleClosedMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.server.UnExceptedErrorMessage; |
|||
import com.ccsens.ccmq.lowlevel.client.ClientManager; |
|||
import com.ccsens.ccmq.lowlevel.message.MessageHandler; |
|||
import wiki.tall.ccmq.common.config.SettingProps; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager; |
|||
import io.netty.channel.ChannelHandler; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.channel.SimpleChannelInboundHandler; |
|||
import io.netty.handler.timeout.IdleState; |
|||
import io.netty.handler.timeout.IdleStateEvent; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@ChannelHandler.Sharable |
|||
@Component |
|||
public class TextHandler extends SimpleChannelInboundHandler<InMessage> { |
|||
private static Logger logger = LoggerFactory.getLogger(TextHandler.class); |
|||
@Autowired |
|||
private SettingProps settingProps; |
|||
|
|||
@Override |
|||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { |
|||
} |
|||
|
|||
@Override |
|||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { |
|||
} |
|||
|
|||
@Override |
|||
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
|||
ChannelManager.addChannel(ctx.channel(),settingProps.getNettyTcpTextType()); |
|||
} |
|||
|
|||
@Override |
|||
public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
|||
ChannelManager.removeChannel(ctx.channel()); |
|||
} |
|||
|
|||
@Override |
|||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
|||
try { |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
MessageHandler.handleMessage( |
|||
InMessage.newToServerMessage(MessageConstant.DomainType.User,new UnExceptedErrorMessage(cause.getMessage()))); |
|||
}catch(Exception e){ |
|||
e.printStackTrace(); |
|||
logger.error("TcpText exceptionCaught handler error: {}",e.getMessage()); |
|||
}finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
cause.printStackTrace(); |
|||
ctx.close(); |
|||
logger.error("Channel closed. TcpText get a exception: {}",cause.getMessage()); |
|||
} |
|||
|
|||
@SuppressWarnings("all") |
|||
@Override |
|||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { |
|||
if (evt instanceof IdleStateEvent) { |
|||
IdleStateEvent idleStateEvent = (IdleStateEvent) evt; |
|||
if (idleStateEvent.state() == IdleState.READER_IDLE) { |
|||
try { |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
MessageHandler.handleMessage( |
|||
InMessage.newToServerMessage(MessageConstant.DomainType.User,new ClientIdleClosedMessage())); |
|||
}catch(Exception e){ |
|||
e.printStackTrace(); |
|||
logger.error("TcpText exceptionCaught handler error: {}",e.getMessage()); |
|||
}finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
ctx.channel().close(); |
|||
logger.error("TcpText channel idle,closed."); |
|||
} |
|||
} else { |
|||
super.userEventTriggered(ctx, evt); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void channelRead0(ChannelHandlerContext ctx, InMessage inMessage) throws Exception { |
|||
try { |
|||
//0.设置最后一次收数据的时间 和 当前线程channel
|
|||
ChannelManager.flushReceiveTimestamp(ctx.channel()); |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
|
|||
//1.填充缺省信息
|
|||
ClientManager.fillMessageInfo(inMessage, MessageConstant.DomainType.User); |
|||
|
|||
//2.处理消息
|
|||
MessageHandler.handleMessage(inMessage); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
logger.error("TcpText Process Message Failed: {},{}", e.getMessage(), inMessage); |
|||
throw e; |
|||
} finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,72 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.wsserver; |
|||
|
|||
import wiki.tall.ccmq.common.config.SettingProps; |
|||
import io.netty.bootstrap.ServerBootstrap; |
|||
import io.netty.channel.*; |
|||
import io.netty.channel.nio.NioEventLoopGroup; |
|||
import io.netty.channel.socket.SocketChannel; |
|||
import io.netty.channel.socket.nio.NioServerSocketChannel; |
|||
import io.netty.handler.codec.http.HttpObjectAggregator; |
|||
import io.netty.handler.codec.http.HttpServerCodec; |
|||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; |
|||
import io.netty.handler.logging.LogLevel; |
|||
import io.netty.handler.logging.LoggingHandler; |
|||
import io.netty.handler.stream.ChunkedWriteHandler; |
|||
import io.netty.handler.timeout.IdleStateHandler; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.scheduling.annotation.Async; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Component |
|||
public class NettyWsServer { |
|||
@Autowired |
|||
private SettingProps settingProps; |
|||
@Autowired |
|||
private WebSocketHandler webSocketHandler; |
|||
|
|||
@Async |
|||
public void start() { |
|||
// Configure the server.
|
|||
EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
|||
EventLoopGroup workerGroup = new NioEventLoopGroup(); |
|||
try { |
|||
ServerBootstrap b = new ServerBootstrap(); |
|||
b.group(bossGroup, workerGroup) |
|||
.channel(NioServerSocketChannel.class) |
|||
.option(ChannelOption.SO_BACKLOG, 100) |
|||
.handler(new LoggingHandler(LogLevel.INFO)) |
|||
.childHandler(new ChannelInitializer<SocketChannel>() { |
|||
@Override |
|||
public void initChannel(SocketChannel ch) throws Exception { |
|||
ChannelPipeline p = ch.pipeline(); |
|||
if(settingProps.getKeepAlive().isEnable()) { |
|||
p.addLast(new IdleStateHandler(settingProps.getKeepAlive().getMaxIdleSeconds(), |
|||
0, 0, TimeUnit.SECONDS)); |
|||
} |
|||
p.addLast(new HttpServerCodec()); |
|||
p.addLast(new HttpObjectAggregator(64 * 1024)); |
|||
p.addLast(new ChunkedWriteHandler()); |
|||
p.addLast(new WebSocketServerProtocolHandler(settingProps.getNettyWsUri())); |
|||
p.addLast(new WebSocketDecoder()); |
|||
p.addLast(new WebSocketEncoder()); |
|||
p.addLast(webSocketHandler); |
|||
} |
|||
}); |
|||
// Start the server.
|
|||
ChannelFuture f = b.bind(settingProps.getNettyWsPort()).sync(); |
|||
// Wait until the server socket is closed.
|
|||
f.channel().closeFuture().sync(); |
|||
} catch(Exception e){ |
|||
e.printStackTrace(); |
|||
}finally { |
|||
// Shut down all event loops to terminate all threads.
|
|||
bossGroup.shutdownGracefully(); |
|||
workerGroup.shutdownGracefully(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,33 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.wsserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.handler.codec.MessageToMessageDecoder; |
|||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
public class WebSocketDecoder extends MessageToMessageDecoder<TextWebSocketFrame> { |
|||
private static Logger logger = LoggerFactory.getLogger(WebSocketDecoder.class); |
|||
|
|||
@Override |
|||
protected void decode(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame msg, List<Object> out) throws Exception { |
|||
String text = msg.text(); |
|||
logger.info("Websocket received: {}",text); |
|||
|
|||
try { |
|||
out.add(JacksonUtil.jsonToBean(text, InMessage.class)); |
|||
}catch(IOException e){ |
|||
e.printStackTrace(); |
|||
logger.error("Websocket Read Error: {}",text); |
|||
throw e; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.wsserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
import io.netty.buffer.ByteBuf; |
|||
import io.netty.channel.ChannelHandlerContext; |
|||
import io.netty.handler.codec.MessageToByteEncoder; |
|||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
public class WebSocketEncoder extends MessageToByteEncoder<OutMessageSet> { |
|||
private static Logger logger = LoggerFactory.getLogger(WebSocketEncoder.class); |
|||
|
|||
@Override |
|||
protected void encode(ChannelHandlerContext ctx, OutMessageSet outMessageSet, ByteBuf out) throws Exception { |
|||
String msg = JacksonUtil.beanToJson(outMessageSet); |
|||
ctx.writeAndFlush(new TextWebSocketFrame(msg)); |
|||
|
|||
logger.info("Websocket send: {}",msg); |
|||
} |
|||
} |
@ -0,0 +1,107 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.netty.wsserver; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.client.ClientIdleClosedMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.server.UnExceptedErrorMessage; |
|||
import com.ccsens.ccmq.lowlevel.client.ClientManager; |
|||
import com.ccsens.ccmq.lowlevel.message.MessageHandler; |
|||
import wiki.tall.ccmq.common.config.SettingProps; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager; |
|||
import io.netty.channel.*; |
|||
import io.netty.handler.timeout.IdleState; |
|||
import io.netty.handler.timeout.IdleStateEvent; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@ChannelHandler.Sharable |
|||
@Component |
|||
public class WebSocketHandler extends SimpleChannelInboundHandler<InMessage> { |
|||
private static Logger logger = LoggerFactory.getLogger(WebSocketHandler.class); |
|||
@Autowired |
|||
private SettingProps settingProps; |
|||
|
|||
@Override |
|||
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { |
|||
} |
|||
|
|||
@Override |
|||
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { |
|||
} |
|||
|
|||
@Override |
|||
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
|||
ChannelManager.addChannel(ctx.channel(),settingProps.getNettyWsType()); |
|||
} |
|||
|
|||
@Override |
|||
public void channelInactive(ChannelHandlerContext ctx) throws Exception { |
|||
ChannelManager.removeChannel(ctx.channel()); |
|||
} |
|||
|
|||
@Override |
|||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
|||
try { |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
MessageHandler.handleMessage( |
|||
InMessage.newToServerMessage(MessageConstant.DomainType.User,new UnExceptedErrorMessage(cause.getMessage()))); |
|||
}catch(Exception e){ |
|||
e.printStackTrace(); |
|||
logger.error("Ws exceptionCaught handler error: {}",e.getMessage()); |
|||
}finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
cause.printStackTrace(); |
|||
ctx.close(); |
|||
logger.error("Channel closed. Ws get a exception: {}", cause.getMessage()); |
|||
} |
|||
|
|||
@Override |
|||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { |
|||
if (evt instanceof IdleStateEvent) { |
|||
IdleStateEvent idleStateEvent = (IdleStateEvent) evt; |
|||
if (idleStateEvent.state() == IdleState.READER_IDLE) { |
|||
try { |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
MessageHandler.handleMessage( |
|||
InMessage.newToServerMessage(MessageConstant.DomainType.User,new ClientIdleClosedMessage())); |
|||
}catch(Exception e){ |
|||
e.printStackTrace(); |
|||
logger.error("Ws exceptionCaught handler error: {}",e.getMessage()); |
|||
}finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
ctx.channel().close(); |
|||
logger.error("Ws channel idle,closed."); |
|||
} |
|||
} else { |
|||
super.userEventTriggered(ctx, evt); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void channelRead0(ChannelHandlerContext ctx, InMessage inMessage) throws Exception { |
|||
try { |
|||
//0.设置最后一次收数据的时间 和 当前线程channel
|
|||
ChannelManager.flushReceiveTimestamp(ctx.channel()); |
|||
ChannelManager.setCurrentChannel(ctx.channel()); |
|||
|
|||
//1.填充缺省信息
|
|||
ClientManager.fillMessageInfo(inMessage,MessageConstant.DomainType.User); |
|||
|
|||
//2.处理消息
|
|||
MessageHandler.handleMessage(inMessage); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
logger.error("Websocket Process Message Failed: {},{}", e.getMessage(), inMessage); |
|||
throw e; |
|||
} finally { |
|||
ChannelManager.removeCurrentChannel(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.rabbitmq; |
|||
|
|||
import wiki.tall.ccmq.common.config.SettingProps; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
import wiki.tall.ccmq.common.util.SpringContextUtils; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.amqp.rabbit.core.RabbitTemplate; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
public class QueueManager { |
|||
private static Logger logger = LoggerFactory.getLogger(QueueManager.class); |
|||
|
|||
public static String getInQueueName(){ |
|||
return SpringContextUtils.getBean(SettingProps.class).getMq().getInName(); |
|||
} |
|||
|
|||
public static String getOutQueueName(){ |
|||
return SpringContextUtils.getBean(SettingProps.class).getMq().getOutName(); |
|||
} |
|||
|
|||
public static void sendTo(String queueName,Object obj) throws JsonProcessingException { |
|||
String text = JacksonUtil.beanToJson(obj); |
|||
logger.info("QueueManager SendTo: {}",text); |
|||
SpringContextUtils.getBean(RabbitTemplate.class).convertAndSend(queueName,text); |
|||
} |
|||
|
|||
public static void sendTo(Object obj) throws JsonProcessingException { |
|||
String text = JacksonUtil.beanToJson(obj); |
|||
logger.info("QueueManager SendTo: {}",text); |
|||
SpringContextUtils.getBean(RabbitTemplate.class).convertAndSend(getOutQueueName(),text); |
|||
} |
|||
} |
@ -0,0 +1,48 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.rabbitmq; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.server.UnExceptedErrorMessage; |
|||
import com.ccsens.ccmq.lowlevel.client.ClientManager; |
|||
import com.ccsens.ccmq.lowlevel.message.MessageHandler; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.amqp.rabbit.annotation.RabbitHandler; |
|||
import org.springframework.amqp.rabbit.annotation.RabbitListener; |
|||
import org.springframework.context.annotation.PropertySource; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Component |
|||
@PropertySource(value = {"classpath:setting-${spring.profiles.active}.properties"}) |
|||
@RabbitListener(queues = "${setting.mq.inName}") |
|||
public class RabbitMqListener { |
|||
private Logger logger = LoggerFactory.getLogger(RabbitMqListener.class); |
|||
|
|||
@RabbitHandler |
|||
public void process(String messageJson) { |
|||
logger.info("Rabbit Received: {}",messageJson); |
|||
|
|||
try { |
|||
InMessage inMessage = JacksonUtil.jsonToBean(messageJson, InMessage.class); |
|||
|
|||
//1.填充缺省字段
|
|||
ClientManager.fillMessageInfo(inMessage,MessageConstant.DomainType.Queue); |
|||
|
|||
//2.处理消息
|
|||
MessageHandler.handleMessage(inMessage); |
|||
}catch (Exception e){ |
|||
e.printStackTrace(); |
|||
try { |
|||
MessageHandler.handleMessage( |
|||
InMessage.newToServerMessage(MessageConstant.DomainType.Queue,new UnExceptedErrorMessage(e.getMessage()))); |
|||
} catch (Exception ex) { |
|||
ex.printStackTrace(); |
|||
} |
|||
logger.error("Rabbit Process Message Failed: {},{}",e.getMessage(),messageJson); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.restful; |
|||
|
|||
import org.springframework.stereotype.Controller; |
|||
import org.springframework.ui.Model; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
import wiki.tall.ccmq.common.util.JsonResponse; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
|
|||
@Controller |
|||
public class MessageApi { |
|||
/** |
|||
* Restful Json 请求 |
|||
* @param request |
|||
* @param response |
|||
* @param model |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@RequestMapping(value = "/json",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"}) |
|||
@ResponseBody |
|||
public JsonResponse api(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { |
|||
return JsonResponse.newInstance().ok(); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.ccsens.ccmq.lowlevel.client.restful; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
public class RestManager { |
|||
public static void sendTo(OutMessageSet outMessageSet){ |
|||
|
|||
} |
|||
|
|||
public static String getOutRestName(){ |
|||
return ""; |
|||
} |
|||
} |
@ -0,0 +1,478 @@ |
|||
package com.ccsens.ccmq.lowlevel.message; |
|||
|
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import cn.hutool.core.lang.Console; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.ccsens.ccmq.lowlevel.client.ClientManager; |
|||
import com.ccsens.ccmq.lowlevel.client.rabbitmq.QueueManager; |
|||
import com.ccsens.ccmq.lowlevel.message.server.*; |
|||
import com.ccsens.ccmq.lowlevel.persist.IMessageDao; |
|||
import com.ccsens.ccmq.lowlevel.message.client.*; |
|||
import com.ccsens.ccmq.lowlevel.message.common.*; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.WrapperedChannel; |
|||
import com.ccsens.ccmq.lowlevel.service.IUserService; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.scheduling.annotation.Async; |
|||
import org.springframework.scheduling.annotation.Scheduled; |
|||
import org.springframework.stereotype.Component; |
|||
import wiki.tall.ccmq.common.config.SettingProps; |
|||
import wiki.tall.ccmq.common.util.*; |
|||
|
|||
import java.nio.channels.Channel; |
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@Component |
|||
public class MessageHandler { |
|||
private Logger logger = LoggerFactory.getLogger(MessageHandler.class); |
|||
/** |
|||
* 发送消息最大等待ack时间 |
|||
*/ |
|||
private static final Integer REDIS_ACK_EXPIRED_SECONDS = 10; |
|||
/** |
|||
* 每次最多查询消息条数 |
|||
*/ |
|||
private static final Integer MAX_MESSAGE_NUM = 50; |
|||
|
|||
private static IMessageDao getMessageDao(){ |
|||
return SpringContextUtils.getBean(IMessageDao.class); |
|||
} |
|||
|
|||
private static SettingProps getSettingProps(){ |
|||
return SpringContextUtils.getBean(SettingProps.class); |
|||
} |
|||
|
|||
private static IUserService getUserService(){ |
|||
return SpringContextUtils.getBean(IUserService.class); |
|||
} |
|||
|
|||
/** |
|||
* 每个20s同步一次有未决消息的用户到pendingClientSet中 |
|||
* @throws Exception |
|||
*/ |
|||
@Scheduled(cron = "0/20 * * * * *") |
|||
public void detectPendingClients() throws Exception{ |
|||
logger.info("schedule detectPendingClients: {}", DateUtil.currentSeconds()); |
|||
//1.获取所有的在线客户端
|
|||
Set<String> onLineUsers = ChannelManager.getAllOnlineUsers(); |
|||
Set<String> onLineClients = new HashSet<>(onLineUsers.size()+1); |
|||
if(CollectionUtil.isNotEmpty(onLineUsers)) { |
|||
for (String onlineUser : onLineUsers) { |
|||
onLineClients.add(CcMessageUtil.getDomainTypeAndUserIdString(MessageConstant.DomainType.User, onlineUser)); |
|||
} |
|||
} |
|||
onLineClients.add(CcMessageUtil.getDomainTypeAndUserIdString(MessageConstant.DomainType.Queue, |
|||
QueueManager.getOutQueueName())); |
|||
|
|||
//2.如果有未ack消息并且不在当前待处理列表中,则添加
|
|||
for(String domainTypeAndUserId: onLineClients){ |
|||
addClientToRedisUnPendingSet(domainTypeAndUserId); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 每隔5分钟将所有的tempSucceed的消息,状态修改为pending |
|||
* @throws Exception |
|||
*/ |
|||
@Scheduled(cron = "0 */5 * * * *") |
|||
public void updateMessageTempStatusToPending() throws Exception{ |
|||
logger.info("schedule updateMessageTempStatusToPending: {}", DateUtil.currentSeconds()); |
|||
getMessageDao().updateMessageStatusToPending(); |
|||
} |
|||
|
|||
/** |
|||
* 每隔5分钟扫描消息过期或发送次数达到上限消息 |
|||
* @throws Exception |
|||
*/ |
|||
@Scheduled(cron = "0 */5 * * * *") |
|||
public void updateMessageExpiredStatus() throws Exception{ |
|||
logger.info("schedule updateMessageExpiredStatus: {}", DateUtil.currentSeconds()); |
|||
getMessageDao().updateMessageToExpired(); |
|||
} |
|||
|
|||
/** |
|||
* 每隔5分钟扫描消息过期或发送次数达到上限消息 |
|||
* @throws Exception |
|||
*/ |
|||
@Scheduled(cron = "0 */5 * * * *") |
|||
public void updateMessageSendTimesUpLimitStatus() throws Exception{ |
|||
logger.info("schedule updateMessageSendTimesUpLimitStatus: {}", DateUtil.currentSeconds()); |
|||
getMessageDao().updateMessageToSendTimesUpLimit(); |
|||
} |
|||
|
|||
@Async("cc-msg-executor") |
|||
public void loopSendMessage() throws Exception { |
|||
String typeAndUserId = null,ackId = null; |
|||
List<Message> messageList = null; |
|||
List<String> sendTimesUpLimitMessageList = null,expiredMessageList = null; |
|||
|
|||
while(true){ |
|||
//从redis中或取第一个待处理用户
|
|||
Object o = RedisUtil.sPop(RedisKeyManager.getPendingClientSetKey()); |
|||
if(o != null && StrUtil.isNotEmpty(typeAndUserId = ackId = (String)o)){ |
|||
Console.log("RedisUtil.sPop: {}",o); |
|||
String []stringArray = CcMessageUtil.splitTypeAndUserId(typeAndUserId); |
|||
MessageConstant.DomainType toDomain = MessageConstant.DomainType.valueOf(stringArray[0]); |
|||
String to = stringArray[1]; |
|||
|
|||
//针对同一用户,在上一次ack还未收到/超时之前,不进行处理
|
|||
Object lockObj = ResourceLock.getLockObj(ackId); |
|||
synchronized (lockObj) { |
|||
//查找该用户是否有正在处理的消息
|
|||
if (RedisUtil.hasKey(RedisKeyManager.getAckSetKey(ackId))) { |
|||
//将当前用户重新放回到待处理列表的最后
|
|||
RedisUtil.sSet(RedisKeyManager.getPendingClientSetKey(), o); |
|||
continue; |
|||
} |
|||
|
|||
//查找所有没有ack的消息
|
|||
messageList = getMessageDao().getClientPendingMessage(toDomain, to, MAX_MESSAGE_NUM); |
|||
if (CollectionUtil.isEmpty(messageList)) { |
|||
continue; |
|||
} |
|||
|
|||
//判断用户是否在线
|
|||
boolean clientOnLine = ClientManager.isUserOnline(toDomain,to); |
|||
|
|||
//发送和处理消息
|
|||
if(clientOnLine){ |
|||
//发送
|
|||
//1.手机所有待发送的messageId,以ackId为key放入redis
|
|||
Set<String> messageIdSet = new HashSet<>(messageList.size()); |
|||
OutMessageSet outMessageSet = OutMessageSet.newInstance(); |
|||
for(Message message : messageList) { |
|||
messageIdSet.add(message.getId()); |
|||
outMessageSet.add(new OutMessage(message)); |
|||
getMessageDao().incrementSendTimes(message.getId(), DateUtil.currentSeconds()); |
|||
} |
|||
RedisUtil.sSetAndTime(RedisKeyManager.getAckSetKey(ackId),REDIS_ACK_EXPIRED_SECONDS,messageIdSet.toArray()); |
|||
|
|||
//2.构造outMessage并且发送
|
|||
outMessageSet.ackId(ackId); |
|||
|
|||
//发送给对应的接收者
|
|||
ClientManager.sendMessageToAuthedClient(toDomain,to,outMessageSet); |
|||
}else{ |
|||
//不发送,根据规则检查所有“offLineDiscard”的消息设置为failed状态
|
|||
for(Message message : messageList){ |
|||
if(message.getRule().getOfflineDiscard() == 1){ |
|||
getMessageDao().updateMessageStatus(message.getId(),MessageConstant.Status.Failed); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
ResourceLock.freeLockObj(ackId); |
|||
} |
|||
try { |
|||
Thread.sleep(100); |
|||
} catch (InterruptedException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static void handleMessage(InMessage inMessage) throws Exception{ |
|||
//1. 处理发送给Server的消息
|
|||
if(inMessage.getToDomain() == MessageConstant.DomainType.Server){ |
|||
String type = null; |
|||
JsonNode typeNode = JacksonUtil.getJsonProperty(inMessage.getData(), "type"); |
|||
//校验错误
|
|||
if (typeNode == null || StrUtil.isEmpty(type = typeNode.textValue())) { |
|||
ClientManager.sendServerAck(inMessage, MessageConstant.Error.MessageNoTypeError); |
|||
} |
|||
//处理消息
|
|||
handlerServerMessage(type,inMessage); |
|||
return; |
|||
} |
|||
|
|||
//2. 处理发送给Client的消息
|
|||
//校验错误
|
|||
if (CollectionUtil.isEmpty(inMessage.getTos())) { |
|||
ClientManager.sendServerAck(inMessage, MessageConstant.Error.MessageNoReceiversError); |
|||
return; |
|||
} |
|||
if(!ClientManager.isSenderAuthed(inMessage)){ |
|||
ClientManager.sendServerAck(inMessage,MessageConstant.Error.UnAuthed); |
|||
ClientManager.closeCurrentSender(inMessage); |
|||
return; |
|||
} |
|||
|
|||
//存储消息(原始消息+按照tos拆分之后的每条消息)
|
|||
saveMessage(inMessage); |
|||
|
|||
//3.发送ack
|
|||
ClientManager.sendServerAck(inMessage, MessageConstant.Error.Ok); |
|||
|
|||
//4.将用户添加到Redis待处理用户集合中
|
|||
RedisUtil.sSet(RedisKeyManager.getPendingClientSetKey(), |
|||
CcMessageUtil.getNameManglingToList(inMessage).toArray() |
|||
); |
|||
} |
|||
|
|||
private static void saveMessage(InMessage inMessage) throws Exception{ |
|||
//1.存储原始消息
|
|||
getMessageDao().saveOrUpdateMessage(inMessage); |
|||
|
|||
//2.按照tos拆分成不同消息
|
|||
for(String to : inMessage.getTos()){ |
|||
Message message = new Message(inMessage,to); |
|||
getMessageDao().saveOrUpdateMessage(message); |
|||
} |
|||
} |
|||
|
|||
private static void handlerServerMessage(String type, InMessage inMessage) throws Exception { |
|||
MessageConstant.ClientMessageType clientMessageType = MessageConstant.ClientMessageType.valueOf(type); |
|||
String data = inMessage.getData(); |
|||
OutMessage outMessage = null; |
|||
|
|||
//1. 处理消息
|
|||
switch(clientMessageType){ |
|||
case Ping: { |
|||
PingMessage inSysData = JacksonUtil.jsonToBean(data, PingMessage.class); |
|||
if (null != inSysData.getData()) { |
|||
ChannelManager.versionChannel(ChannelManager.getCurrentChannel(), inSysData.getData().getMajor(), inSysData.getData().getMinor()); |
|||
} |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson(new PongMessage())); |
|||
break; |
|||
} |
|||
case Auth: { |
|||
boolean authSuccess = false; |
|||
AuthMessage inSysData = JacksonUtil.jsonToBean(data, AuthMessage.class); |
|||
if(null != inSysData.getData()){ |
|||
if(StrUtil.isNotEmpty(inSysData.getData().getToken())) { |
|||
String userId = getUserService().getUserIdByToken(inSysData.getData().getToken()); |
|||
if(StrUtil.isNotEmpty(userId)){ |
|||
ChannelManager.authChannel(ChannelManager.getCurrentChannel(),userId,inSysData.getData().getMajor(),inSysData.getData().getMinor()); |
|||
onClientOnLine(MessageConstant.DomainType.User,userId); |
|||
authSuccess = true; |
|||
} |
|||
} |
|||
} |
|||
if(!authSuccess){ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ChannelStatusMessage(false,0L,MessageConstant.Error.AuthFailed)) |
|||
); |
|||
}else{ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ChannelStatusMessage(true,0L,MessageConstant.Error.Ok)) |
|||
); |
|||
} |
|||
break; |
|||
} |
|||
case GetChannelStatus: { |
|||
WrapperedChannel wrapperedChannel = ChannelManager.getWrapperedChannelByChannel(ChannelManager.getCurrentChannel()); |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ChannelStatusMessage(wrapperedChannel.isAuthed(), |
|||
wrapperedChannel.getOnlineSeconds(), |
|||
MessageConstant.Error.Ok)) |
|||
); |
|||
break; |
|||
} |
|||
case Ack: { |
|||
//根据id找到
|
|||
boolean ackSuccess = false; |
|||
AckMessage inSysData = JacksonUtil.jsonToBean(data, AckMessage.class); |
|||
if(null != inSysData.getData()){ |
|||
updateMessageAckStatus(inSysData.getData().getAckId()); |
|||
ackSuccess = true; |
|||
} |
|||
if(!ackSuccess){ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.AckParameterError)) |
|||
); |
|||
} |
|||
//没有错误不回复消息
|
|||
break; |
|||
} |
|||
case HasRead: { |
|||
boolean hasReadSuccess = false; |
|||
HasReadMessage inSysData = JacksonUtil.jsonToBean(data, HasReadMessage.class); |
|||
if(null != inSysData.getData()){ |
|||
updateMessageHasReadStatus( |
|||
inSysData.getData().getFromDomain(),inSysData.getData().getFromUserId(), |
|||
inMessage.getFromDomain().name(),inMessage.getFrom(), inSysData.getData().getTime(),(byte)1 |
|||
); |
|||
hasReadSuccess = true; |
|||
} |
|||
if(!hasReadSuccess){ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.AckParameterError)) |
|||
); |
|||
} |
|||
//没有错误不回复消息
|
|||
break; |
|||
} |
|||
case SetMsgSuccess: { |
|||
boolean setStatusSuccess = false; |
|||
SetSuccessStatusMessage inSysData = JacksonUtil.jsonToBean(data, SetSuccessStatusMessage.class); |
|||
if(null != inSysData.getData() && StrUtil.isNotEmpty(inSysData.getData().getMsgId())){ |
|||
updateMessageStatus(inSysData.getData().getMsgId(), MessageConstant.Status.Succeed); |
|||
setStatusSuccess = true; |
|||
} |
|||
if(!setStatusSuccess){ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.SetStatusParameterError)) |
|||
); |
|||
}else{ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.Ok)) |
|||
); |
|||
} |
|||
break; |
|||
} |
|||
case SetMsgReverted: { |
|||
boolean setStatusSuccess = false; |
|||
SetRevertedStatusMessage inSysData = JacksonUtil.jsonToBean(data, SetRevertedStatusMessage.class); |
|||
if(null != inSysData.getData() && StrUtil.isNotEmpty(inSysData.getData().getMsgId())){ |
|||
updateMessageStatus(inSysData.getData().getMsgId(), MessageConstant.Status.Reverted); |
|||
setStatusSuccess = true; |
|||
} |
|||
if(!setStatusSuccess){ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.SetStatusParameterError)) |
|||
); |
|||
}else{ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.Ok)) |
|||
); |
|||
} |
|||
break; |
|||
} |
|||
case SetMsgDeleted: { |
|||
boolean setStatusSuccess = false; |
|||
SetDeletedStatusMessage inSysData = JacksonUtil.jsonToBean(data, SetDeletedStatusMessage.class); |
|||
if(null != inSysData.getData() && StrUtil.isNotEmpty(inSysData.getData().getMsgId())){ |
|||
updateMessageStatus(inSysData.getData().getMsgId(), MessageConstant.Status.Deleted); |
|||
setStatusSuccess = true; |
|||
} |
|||
if(!setStatusSuccess){ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.SetStatusParameterError)) |
|||
); |
|||
}else{ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.Ok)) |
|||
); |
|||
} |
|||
break; |
|||
} |
|||
case ClientIdleClosed: { |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage( |
|||
MessageConstant.Error.ChannelIdle.joinExtra(String.valueOf(getSettingProps().getKeepAlive().getMaxIdleSeconds())) |
|||
) |
|||
) |
|||
); |
|||
break; |
|||
} |
|||
case UnExceptedError: { |
|||
UnExceptedErrorMessage inSysData = JacksonUtil.jsonToBean(data, UnExceptedErrorMessage.class); |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage( |
|||
MessageConstant.Error.UnExpectedError.joinExtra(inSysData.getData().getErrorString()) |
|||
) |
|||
) |
|||
); |
|||
break; |
|||
} |
|||
case ClientAuthTimeOut:{ |
|||
outMessage = new OutMessage(JacksonUtil.beanToJson( |
|||
new ServerAckMessage(MessageConstant.Error.ChannelAuthTimeOut) |
|||
) |
|||
); |
|||
break; |
|||
} |
|||
default: break; |
|||
} |
|||
|
|||
//2.结果应答
|
|||
if(null != outMessage) { |
|||
ClientManager.sendServerMessage(inMessage.getFromDomain(), |
|||
OutMessageSet.newInstance().ackId(null).add(outMessage) |
|||
); |
|||
} |
|||
} |
|||
|
|||
private static void onClientOnLine(MessageConstant.DomainType domain,String userId){ |
|||
getMessageDao().updateMessageStatusToPending(domain,userId); |
|||
addClientToRedisUnPendingSet(CcMessageUtil.getDomainTypeAndUserIdString(domain,userId)); |
|||
} |
|||
|
|||
private static void updateMessageAckStatus(String ackId) throws Exception { |
|||
String ackKey = RedisKeyManager.getAckSetKey(ackId); |
|||
|
|||
//1.从redis中获取ackId对应的set列表
|
|||
if(!RedisUtil.hasKey(ackKey)) { |
|||
return; |
|||
} |
|||
|
|||
//2.从redis中获取set集合,并删除ackId对应的set列表
|
|||
Set<Object> ackMessageSet = RedisUtil.sGet(ackKey); |
|||
RedisUtil.del(ackKey); |
|||
|
|||
//2.更新msg的ack标志
|
|||
if (CollectionUtil.isNotEmpty(ackMessageSet)) { |
|||
for (Object msgId : ackMessageSet) { |
|||
Message message = getMessageDao().getMessageById((String) msgId); |
|||
if(message.getRule().getAckIsSuccess() == 0) { |
|||
getMessageDao().updateMessageAckAndStatus((String) msgId, (byte)1,MessageConstant.Status.TempSucceed); |
|||
}else{ |
|||
getMessageDao().updateMessageAckAndStatus((String) msgId, (byte)1,MessageConstant.Status.Succeed); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//3.查找该用户是否仍然有未ack消息,
|
|||
addClientToRedisUnPendingSet(ackId); |
|||
} |
|||
|
|||
private static void updateMessageHasReadStatus(String fromDomain, String fromUserId,String toDomain, String toUserId, |
|||
Long time, byte hasRead) throws Exception{ |
|||
getMessageDao().updateMessageReadStatus(fromDomain,fromUserId,toDomain,toUserId,time,hasRead); |
|||
} |
|||
|
|||
private static void updateMessageStatus(String rawId, MessageConstant.Status status) throws Exception { |
|||
getMessageDao().updateMessageStatusByRawId(rawId,status); |
|||
if(status == MessageConstant.Status.Reverted){ |
|||
List<Message> messageList = getMessageDao().getMessageByRawId(rawId); |
|||
if(CollectionUtil.isNotEmpty(messageList)){ |
|||
//ClientManager.sendServerMessage();
|
|||
//为了保证投递到目标客户端,将该消息添加到消息队列中,进行投递。
|
|||
for(Message message : messageList) { |
|||
Message invertedMessage = new Message( |
|||
MessageConstant.DomainType.Server, "", message.getToDomain(),message.getTo(), |
|||
JacksonUtil.beanToJson(new InvertedMessage(message.getId())),MessageRule.defaultRule(MessageConstant.DomainType.Server) |
|||
); |
|||
getMessageDao().saveOrUpdateMessage(invertedMessage); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static void addClientToRedisUnPendingSet(String domainTypeAndUserId){ |
|||
//如果有未ack消息并且不在当前待处理列表中,则添加
|
|||
String redisUnPendingClientSetKey = RedisKeyManager.getPendingClientSetKey(); |
|||
String redisWaitAckSetKey = RedisKeyManager.getAckSetKey(domainTypeAndUserId); |
|||
String []stringArray = CcMessageUtil.splitTypeAndUserId(domainTypeAndUserId); |
|||
MessageConstant.DomainType toDomain = MessageConstant.DomainType.valueOf(stringArray[0]); |
|||
String to = stringArray[1]; |
|||
//1.1 当前用户是否在pendingClients列表中
|
|||
if (!RedisUtil.sHas(redisUnPendingClientSetKey,domainTypeAndUserId)) { |
|||
//1.2 当前用户是否在redis的等待ack列表中
|
|||
if(!RedisUtil.hasKey(redisWaitAckSetKey)){ |
|||
//1.3 当前用户是否有pending消息
|
|||
if(getMessageDao().countClientPendingMessage(toDomain,to) > 0){ |
|||
//1.4 将当前用户添加到redisUnPendingClientSet列表中排队处理
|
|||
RedisUtil.sSet(redisUnPendingClientSetKey,domainTypeAndUserId); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class AckMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data { |
|||
String ackId; |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public AckMessage(){ |
|||
setType(MessageConstant.ClientMessageType.Ack.name()); |
|||
} |
|||
|
|||
public AckMessage(String ackId){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setAckId(ackId); |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
* 客户端认证 (user) |
|||
*/ |
|||
@Data |
|||
public class AuthMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data{ |
|||
private String token; |
|||
private int major; |
|||
private int minor; |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public AuthMessage(){ |
|||
setType(MessageConstant.ClientMessageType.Auth.name()); |
|||
} |
|||
|
|||
public AuthMessage(String token,int major,int minor){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setToken(token); |
|||
data.setMajor(major); |
|||
data.setMinor(minor); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author wei |
|||
* 客户端向服务器请求当前连接状态 |
|||
*/ |
|||
@Data |
|||
public class ClientAuthTimeOutMessage extends ServerMessage { |
|||
public ClientAuthTimeOutMessage(){ |
|||
setType(MessageConstant.ClientMessageType.ClientAuthTimeOut.name()); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author wei |
|||
* 客户端向服务器请求当前连接状态 |
|||
*/ |
|||
@Data |
|||
public class ClientIdleClosedMessage extends ServerMessage { |
|||
public ClientIdleClosedMessage(){ |
|||
setType(MessageConstant.ClientMessageType.ClientIdleClosed.name()); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author wei |
|||
* 客户端向服务器请求当前连接状态 |
|||
*/ |
|||
@Data |
|||
public class GetStatusMessage extends ServerMessage { |
|||
public GetStatusMessage(){ |
|||
setType(MessageConstant.ClientMessageType.GetChannelStatus.name()); |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class HasReadMessage extends ServerMessage { |
|||
/** |
|||
* fromDomain域的fromUserId用户 给我发送的所有 时间<lastMsgId时间的消息 都已读。 |
|||
*/ |
|||
@Setter |
|||
@Getter |
|||
public static class Data { |
|||
private Long time; |
|||
private String fromUserId; |
|||
private String fromDomain; |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public HasReadMessage(){ |
|||
setType(MessageConstant.ClientMessageType.HasRead.name()); |
|||
} |
|||
|
|||
public HasReadMessage(Long time){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setTime(time); |
|||
} |
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class PingMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data{ |
|||
private Integer major; |
|||
private Integer minor; |
|||
} |
|||
private Data data; |
|||
|
|||
public PingMessage(){ |
|||
setType(MessageConstant.ClientMessageType.Ping.name()); |
|||
} |
|||
|
|||
public PingMessage(Integer major,Integer minor){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setMajor(major); |
|||
data.setMinor(minor); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class SetDeletedStatusMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data { |
|||
String msgId; |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public SetDeletedStatusMessage(){ |
|||
setType(MessageConstant.ClientMessageType.SetMsgDeleted.name()); |
|||
} |
|||
|
|||
public SetDeletedStatusMessage(String msgId){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setMsgId(msgId); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class SetRevertedStatusMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data { |
|||
String msgId; |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public SetRevertedStatusMessage(){ |
|||
setType(MessageConstant.ClientMessageType.SetMsgReverted.name()); |
|||
} |
|||
|
|||
public SetRevertedStatusMessage(String msgId){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setMsgId(msgId); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.client; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class SetSuccessStatusMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data { |
|||
String msgId; |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public SetSuccessStatusMessage(){ |
|||
setType(MessageConstant.ClientMessageType.SetMsgSuccess.name()); |
|||
} |
|||
|
|||
public SetSuccessStatusMessage(String msgId){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setMsgId(msgId); |
|||
} |
|||
} |
@ -0,0 +1,95 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.common; |
|||
|
|||
import cn.hutool.core.date.DateUtil; |
|||
import wiki.tall.ccmq.common.util.JacksonUtil; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import lombok.Data; |
|||
|
|||
import java.util.*; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class InMessage { |
|||
/** |
|||
* 消息ID |
|||
*/ |
|||
@JsonProperty(access = JsonProperty.Access.READ_ONLY) |
|||
private String id; |
|||
/** |
|||
* 发送时间(s) |
|||
* Notice: 指的是服务器收到发送请求的时间,不是服务器发出消息的时间 |
|||
*/ |
|||
@JsonProperty(access = JsonProperty.Access.READ_ONLY) |
|||
private Long time; |
|||
/** |
|||
* 消息来自于那个域 |
|||
*/ |
|||
@JsonProperty(access = JsonProperty.Access.READ_ONLY) |
|||
private MessageConstant.DomainType fromDomain; |
|||
/** |
|||
* 发送者信息 |
|||
*/ |
|||
private String from; |
|||
/** |
|||
* 消息要发送到哪个域 |
|||
*/ |
|||
private MessageConstant.DomainType toDomain; |
|||
/** |
|||
* 接受者信息(列表) |
|||
*/ |
|||
private Set<String> tos; |
|||
/** |
|||
* 消息的标示符(通常是由用户传过来的消息ID,该消息在用户系统中的ID) |
|||
*/ |
|||
private String unikey; |
|||
/** |
|||
* 发送规则 |
|||
* DeSerialize but not serialize @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) |
|||
*/ |
|||
private MessageRule rule; |
|||
/** |
|||
* 发送内容 |
|||
* 如果toDomain是Server: 代表消息是发给当前服务器的,必须是MessageSysData的子类型 |
|||
* 其他:(格式不限,由用户指定) |
|||
*/ |
|||
private String data; |
|||
|
|||
public InMessage(){ |
|||
this.time = DateUtil.currentSeconds(); |
|||
} |
|||
public static InMessage newToServerMessage(MessageConstant.DomainType fromDomain,ServerMessage serverMessage) throws JsonProcessingException { |
|||
InMessage inMessage = new InMessage(); |
|||
inMessage.setFromDomain(fromDomain); |
|||
inMessage.setToDomain(MessageConstant.DomainType.Server); |
|||
inMessage.setData(JacksonUtil.beanToJson(serverMessage)); |
|||
return inMessage; |
|||
} |
|||
|
|||
public static InMessage newToQueueMessage(String from, Set<String> tos, String unikey, MessageRule rule, String data) throws JsonProcessingException { |
|||
InMessage inMessage = new InMessage(); |
|||
inMessage.setToDomain(MessageConstant.DomainType.Queue); |
|||
inMessage.setFrom(from); |
|||
inMessage.setTos(tos); |
|||
inMessage.setUnikey(unikey); |
|||
inMessage.setRule(rule); |
|||
inMessage.setData(data); |
|||
return inMessage; |
|||
} |
|||
public static InMessage newToUserMessage(String from, Set<String> tos, String unikey, MessageRule rule, String data) throws JsonProcessingException { |
|||
InMessage inMessage = new InMessage(); |
|||
inMessage.setToDomain(MessageConstant.DomainType.User); |
|||
inMessage.setFrom(from); |
|||
inMessage.setTos(tos); |
|||
inMessage.setUnikey(unikey); |
|||
inMessage.setRule(rule); |
|||
inMessage.setData(data); |
|||
return inMessage; |
|||
} |
|||
|
|||
|
|||
//TODO
|
|||
//添加方便链式调用的构造方法,类似builder
|
|||
} |
@ -0,0 +1,134 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.common; |
|||
|
|||
import wiki.tall.ccmq.common.util.DateUtil; |
|||
import lombok.Data; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class Message { |
|||
/** |
|||
* 消息ID (Message's ID) |
|||
*/ |
|||
private String id; |
|||
|
|||
/** |
|||
* 原始消息ID (InMessage's ID) |
|||
*/ |
|||
private String rawId; |
|||
|
|||
/** |
|||
* 发送时间(s) |
|||
* Notice: 指的是服务器收到发送请求的时间,不是服务器发出消息的时间 |
|||
*/ |
|||
private Long time; |
|||
/** |
|||
* 消息来自于那个域 |
|||
*/ |
|||
private MessageConstant.DomainType fromDomain; |
|||
/** |
|||
* 发送者信息 |
|||
*/ |
|||
private String from; |
|||
/** |
|||
* 消息要发送到哪个域 |
|||
*/ |
|||
private MessageConstant.DomainType toDomain; |
|||
/** |
|||
* 接受者信息(列表) |
|||
*/ |
|||
private String to; |
|||
/** |
|||
* 发送规则 |
|||
* DeSerialize but not serialize @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) |
|||
*/ |
|||
private MessageRule rule; |
|||
/** |
|||
* 发送内容 |
|||
* 如果toDomain是Server: 代表消息是发给当前服务器的,必须是MessageSysData的子类型 |
|||
* 其他:(格式不限,由用户指定) |
|||
*/ |
|||
private String data; |
|||
|
|||
/** |
|||
* 是否收到用户的ack消息 |
|||
*/ |
|||
private byte acked; |
|||
/** |
|||
* 是否收到用户的已读消息 |
|||
*/ |
|||
private byte hasRead; |
|||
/** |
|||
* 消息状态 |
|||
* 发送成功标志:根据配置由三种情况 |
|||
* 1. 配置不需要客户端ack, |
|||
* 发送完毕自动设置 ack = true; |
|||
* rule -> ackIsSuccess = true 发送即代表成功 |
|||
* rule -> ackIsSuccess = false 等待用户手动设置成功标志 |
|||
* 2. 配置需要ack,则发送并受到ack代表成功 |
|||
* rule -> ackIsSuccess = true 发送并收到ack即代表成功 |
|||
* rule -> ackIsSuccess = false 等待用户手动设置成功标志 |
|||
*/ |
|||
private MessageConstant.Status status; |
|||
|
|||
/** |
|||
* 发送次数 |
|||
*/ |
|||
private int sendTimes; |
|||
/** |
|||
* 发送时间 |
|||
*/ |
|||
private List<Long> sendTimeInSecondList; |
|||
|
|||
public Message(){ |
|||
} |
|||
|
|||
public Message(MessageConstant.DomainType fromDomain,String from,MessageConstant.DomainType toDomain,String to,String data,MessageRule rule){ |
|||
this.time = DateUtil.currentSeconds(); |
|||
this.fromDomain = fromDomain; |
|||
this.from = from; |
|||
this.toDomain = toDomain; |
|||
this.to = to; |
|||
this.data = data; |
|||
this.rule = rule; |
|||
} |
|||
|
|||
public Message(InMessage inEntity, String to){ |
|||
this.rawId = inEntity.getId(); |
|||
this.time = inEntity.getTime(); |
|||
this.fromDomain = inEntity.getFromDomain(); |
|||
this.from = inEntity.getFrom(); |
|||
this.toDomain = inEntity.getToDomain(); |
|||
this.to = to; |
|||
this.rule = inEntity.getRule(); |
|||
this.data = inEntity.getData(); |
|||
|
|||
this.acked = 0; |
|||
this.hasRead = 0; |
|||
this.status = MessageConstant.Status.Pending; |
|||
this.sendTimes = 0; |
|||
} |
|||
|
|||
public void incrementSendTimes() { |
|||
this.sendTimes++; |
|||
if(this.sendTimeInSecondList == null){ |
|||
this.sendTimeInSecondList = new ArrayList<>(); |
|||
} |
|||
this.sendTimeInSecondList.add(DateUtil.currentSeconds()); |
|||
} |
|||
|
|||
public boolean isSendTimesUpLimit() { |
|||
if((rule.getNoAckRetryTimes() > 0) && (sendTimes >= rule.getNoAckRetryTimes())){ |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public boolean isExpired() { |
|||
return DateUtil.currentSeconds() >= rule.getExpireAt(); |
|||
} |
|||
} |
@ -0,0 +1,217 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.common; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
|
|||
/** |
|||
* @author wei |
|||
* 消息相关常量 |
|||
*/ |
|||
public class MessageConstant { |
|||
public enum ClientMessageType{ |
|||
//客户端心跳
|
|||
Ping(0x00), |
|||
//客户端认证
|
|||
Auth(0x01), |
|||
//客户端收到消息ACK
|
|||
Ack(0x02), |
|||
//客户端收到消息ACK
|
|||
HasRead(0x03), |
|||
//客户端请求连接状态
|
|||
GetChannelStatus(0x04), |
|||
|
|||
//不可预期的错误
|
|||
UnExceptedError(0x21), |
|||
ClientIdleClosed(0x22), |
|||
ClientAuthTimeOut(0x23), |
|||
|
|||
//设置消息状态消息(disable撤销消息,适用于,消息撤回),针对消息本身(eg:消息撤回)
|
|||
SetMsgSuccess(0x52), |
|||
SetMsgReverted(0x53), |
|||
SetMsgDeleted(0x54); |
|||
|
|||
public int value; |
|||
|
|||
ClientMessageType(int value){ |
|||
this.value = value; |
|||
} |
|||
|
|||
/** |
|||
* 从int到enum的转换函数 |
|||
* @param value 枚举int值 |
|||
* @return 对应的枚举,找不到则返回null |
|||
*/ |
|||
public static ClientMessageType valueOf(int value) { |
|||
for(ClientMessageType type : values()){ |
|||
if(type.value == value){ |
|||
return type; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public enum ServerMessageType{ |
|||
//客户端心跳
|
|||
Pong(0x00), |
|||
//客户端收到消息ACK
|
|||
Ack(0x02), |
|||
//客户端请求连接状态
|
|||
ChannelStatus(0x03); |
|||
//撤销某个消息
|
|||
//DelMessage(0x04),
|
|||
//客户端请求连接状态
|
|||
//QueueStatus(0x05);
|
|||
|
|||
public int value; |
|||
|
|||
ServerMessageType(int value){ |
|||
this.value = value; |
|||
} |
|||
|
|||
/** |
|||
* 从int到enum的转换函数 |
|||
* @param value 枚举int值 |
|||
* @return 对应的枚举,找不到则返回null |
|||
*/ |
|||
public static ServerMessageType valueOf(int value) { |
|||
for(ServerMessageType type : values()){ |
|||
if(type.value == value){ |
|||
return type; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* JsonFormat是Json Serialize相关配置 |
|||
*/ |
|||
@JsonFormat(shape = JsonFormat.Shape.OBJECT) |
|||
public enum Error{ |
|||
//Ok
|
|||
Ok(200,"Ok"), |
|||
|
|||
/** |
|||
* 通用消息错误 |
|||
*/ |
|||
//消息无接收者错误
|
|||
MessageNoReceiversError(1001,"[用户]消息至少应该有一个接收者"), |
|||
//消息无数据错误
|
|||
MessageNoDataError(1002,"不允许消息内容为空"), |
|||
|
|||
/** |
|||
* server消息错误 |
|||
*/ |
|||
//server消息无type错误
|
|||
MessageNoTypeError(1003,"发送至Server域的消息必须有type字段"), |
|||
//Ack参数错误,没有找到对应的msgId
|
|||
AckParameterError(1103,"Ack参数错误,没有找到对应的msgId"), |
|||
//SetSuccess参数错误,没有找到对应的msgId
|
|||
SetSuccessParameterError(1104,"SetSuccess参数错误,没有找到对应的msgId"), |
|||
//SetStatusParameterError
|
|||
SetStatusParameterError(1105,"SetStatus参数错误,没有找到对应的msgId"), |
|||
//HasRead参数错误,没有找到对应的msgId
|
|||
HasReadParameterError(1106,"HasRead参数错误,没有找到对应的msgId"), |
|||
|
|||
/** |
|||
* 身份认证错误 |
|||
*/ |
|||
//无权限
|
|||
UnAuthed(1021, "未认证的用户"), |
|||
|
|||
/** |
|||
* 业务错误 |
|||
*/ |
|||
//认证失败
|
|||
AuthFailed(1301,"认证失败"), |
|||
//空闲断开连接(连续Ns没有收到数据)
|
|||
ChannelIdle(1302,"连接断开:连续N秒没有从收到客户端收到任何数据"), |
|||
//认证超时断开连接
|
|||
ChannelAuthTimeOut(1303,"连接断开:认证超时"), |
|||
//不可预期错误
|
|||
UnExpectedError(1304,"不可预期异常"); |
|||
|
|||
public int code; |
|||
public String text; |
|||
public String extra; |
|||
|
|||
Error(int code,String text) { |
|||
this.code = code; |
|||
this.text = text; |
|||
} |
|||
Error(int code,String text,String extra) { |
|||
this.code = code; |
|||
this.text = text; |
|||
this.extra = extra; |
|||
} |
|||
public Error joinExtra(String extra){ |
|||
this.extra = extra; |
|||
return this; |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 域(User、Queue、Rest、Server) |
|||
*/ |
|||
public enum DomainType{ |
|||
//Netty Client
|
|||
User(1), |
|||
//Queue Client
|
|||
Queue(2), |
|||
//Rest Client
|
|||
Rest(3), |
|||
//系统
|
|||
Server(4); |
|||
|
|||
public int value; |
|||
|
|||
DomainType(int value){ |
|||
this.value = value; |
|||
} |
|||
|
|||
public static DomainType valueOf(int value) { |
|||
for(DomainType domainType : values()){ |
|||
if(domainType.value == value){ |
|||
return domainType; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public enum Status{ |
|||
//未决状态(未完成)
|
|||
Pending(0), |
|||
//发送成功(投递成功)
|
|||
Succeed(1), |
|||
//发送失败(投递失败)
|
|||
Failed(2), |
|||
//消息过期
|
|||
Expired(3), |
|||
//消息被撤回
|
|||
Reverted(4), |
|||
//消息被删除
|
|||
Deleted(5), |
|||
//消息达到重试次数上限
|
|||
SendTimesUpLimit(6), |
|||
|
|||
//临时完成状态,当ackIsSuccess==0时,一旦ack设置消息为此状态
|
|||
TempSucceed(10); |
|||
|
|||
public int value; |
|||
|
|||
Status(int value){ |
|||
this.value = value; |
|||
} |
|||
|
|||
public static Status valueOf(int value){ |
|||
for(Status status : values()){ |
|||
if(status.value == value){ |
|||
return status; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,80 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.common; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 发送规则实体类 |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class MessageRule { |
|||
public enum AckRule{ |
|||
//不需要ack
|
|||
NONE, |
|||
//产生错误时回复ack
|
|||
ERROR, |
|||
//总是回复ack
|
|||
ALWAYS |
|||
} |
|||
/** |
|||
* 离线丢弃标志 |
|||
*/ |
|||
private byte offlineDiscard; |
|||
/** |
|||
* 消息是否需要ack,如果不需要ack,则发送完毕,即代表成功 |
|||
*/ |
|||
private AckRule ackRule; |
|||
/** |
|||
* 没有收到ack重试次数 |
|||
*/ |
|||
private Integer noAckRetryTimes; |
|||
/** |
|||
* 收到ack代表发送成功,不再发送 |
|||
* 如果该标志为0,则代表每次上线都发送,直到用户手动设置成功。 |
|||
*/ |
|||
private byte ackIsSuccess; |
|||
/** |
|||
* 消息过期时间(s) |
|||
*/ |
|||
private Long expireAt; |
|||
|
|||
public MessageRule(){ |
|||
|
|||
} |
|||
|
|||
public MessageRule(byte offlineDiscard,AckRule ackRule,Integer noAckRetryTimes,byte ackIsSuccess){ |
|||
this.offlineDiscard = offlineDiscard; |
|||
this.ackRule = ackRule; |
|||
this.noAckRetryTimes = noAckRetryTimes; |
|||
this.ackIsSuccess = ackIsSuccess; |
|||
} |
|||
|
|||
public MessageRule(byte offlineDiscard,AckRule ackRule,Integer noAckRetryTimes,byte ackIsSuccess,Long expireAt){ |
|||
this.offlineDiscard = offlineDiscard; |
|||
this.ackRule = ackRule; |
|||
this.noAckRetryTimes = noAckRetryTimes; |
|||
this.ackIsSuccess = ackIsSuccess; |
|||
this.expireAt = expireAt; |
|||
} |
|||
|
|||
public static MessageRule defaultRule(MessageConstant.DomainType fromDomain) { |
|||
MessageRule messageRule = null; |
|||
switch(fromDomain) { |
|||
case User: |
|||
messageRule = new MessageRule((byte) 0, AckRule.ALWAYS, 10, (byte) 1,0L); |
|||
break; |
|||
case Queue: |
|||
messageRule = new MessageRule((byte) 1, AckRule.ALWAYS, 10, (byte) 1,0L); |
|||
break; |
|||
case Rest: |
|||
messageRule = new MessageRule((byte) 1, AckRule.ALWAYS, 10, (byte) 1,0L); |
|||
break; |
|||
case Server: |
|||
messageRule = new MessageRule((byte) 1, AckRule.ALWAYS, 10, (byte) 1,0L); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
return messageRule; |
|||
} |
|||
} |
@ -0,0 +1,71 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.common; |
|||
|
|||
import cn.hutool.core.date.DateUtil; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class OutMessage { |
|||
/** |
|||
* 消息ID |
|||
*/ |
|||
private String id; |
|||
/** |
|||
* sender发送消息时间(s) |
|||
* Notice: 指的是服务器收到发送请求的时间,不是服务器发出消息的时间,服务器发出消息的时间客户端很好确定(就是当前时间) |
|||
*/ |
|||
private Long time; |
|||
/** |
|||
* 消息来自于那个域,通常不需要知道toDomain |
|||
*/ |
|||
private MessageConstant.DomainType fromDomain; |
|||
/** |
|||
* 发送者信息 |
|||
*/ |
|||
private String from; |
|||
/** |
|||
* 如果toDomain是Server: 代表消息是发给当前服务器的,必须是MessageSysData的子类型 |
|||
* 其他:(格式不限,由用户指定,消息系统会将data字段原封不动投递到接收方,由接收方进行解析) |
|||
*/ |
|||
private String data; |
|||
|
|||
public OutMessage(){ |
|||
} |
|||
|
|||
public OutMessage(String id, Long time, String from, String data){ |
|||
this.id = id; |
|||
this.time = time; |
|||
this.from = from; |
|||
this.data = data; |
|||
} |
|||
|
|||
public OutMessage(InMessage messageEntity){ |
|||
this.id = messageEntity.getId(); |
|||
this.time = messageEntity.getTime(); |
|||
this.fromDomain = messageEntity.getFromDomain(); |
|||
this.from = messageEntity.getFrom(); |
|||
this.data = messageEntity.getData(); |
|||
} |
|||
|
|||
public OutMessage(String data){ |
|||
this.time = DateUtil.currentSeconds(); |
|||
this.fromDomain = MessageConstant.DomainType.Server; |
|||
this.data = data; |
|||
} |
|||
|
|||
public OutMessage(MessageConstant.DomainType fromDomain, String data) { |
|||
this.time = DateUtil.currentSeconds(); |
|||
this.fromDomain = fromDomain; |
|||
this.data = data; |
|||
} |
|||
|
|||
public OutMessage(Message message){ |
|||
this.id = message.getId(); |
|||
this.time = message.getTime(); |
|||
this.fromDomain = message.getFromDomain(); |
|||
this.from = message.getFrom(); |
|||
this.data = message.getData(); |
|||
} |
|||
} |
@ -0,0 +1,43 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.common; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.ToString; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.HashSet; |
|||
import java.util.Set; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@Getter @Setter @ToString |
|||
public class OutMessageSet { |
|||
private String ackId; |
|||
private Set<OutMessage> messageSet; |
|||
|
|||
public OutMessageSet ackId(String ackId){ |
|||
this.ackId = ackId; |
|||
return this; |
|||
} |
|||
|
|||
public static OutMessageSet newInstance(){ |
|||
return new OutMessageSet(); |
|||
} |
|||
|
|||
public OutMessageSet add(OutMessage messageOutEntity){ |
|||
if(messageSet == null){ |
|||
messageSet = new HashSet<>(); |
|||
} |
|||
messageSet.add(messageOutEntity); |
|||
return this; |
|||
} |
|||
|
|||
public OutMessageSet addAll(Collection<OutMessage> theMessageSet){ |
|||
if(messageSet == null){ |
|||
messageSet = new HashSet<>(); |
|||
} |
|||
messageSet.addAll(theMessageSet); |
|||
return this; |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.common; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Data |
|||
public class ServerMessage { |
|||
private String type; |
|||
} |
@ -0,0 +1,37 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.server; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author zhangsan |
|||
*/ |
|||
@Data |
|||
public class ChannelStatusMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data{ |
|||
//认证状态
|
|||
private Boolean authed; |
|||
//已认证时长(s)
|
|||
private Long onlineSeconds; |
|||
//错误信息
|
|||
private MessageConstant.Error error; |
|||
} |
|||
private Data data; |
|||
|
|||
public ChannelStatusMessage(){ |
|||
setType(MessageConstant.ServerMessageType.ChannelStatus.name()); |
|||
} |
|||
|
|||
public ChannelStatusMessage(Boolean authed,Long onlineSeconds,MessageConstant.Error error){ |
|||
this(); |
|||
data = new Data(); |
|||
data.authed = authed; |
|||
data.onlineSeconds = onlineSeconds; |
|||
data.error = error; |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.server; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
public class InvertedMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data{ |
|||
private String msgId; |
|||
} |
|||
private Data data; |
|||
|
|||
public InvertedMessage(){ |
|||
setType(MessageConstant.ServerMessageType.Pong.name()); |
|||
} |
|||
|
|||
public InvertedMessage(String msgId){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setMsgId(msgId); |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.server; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author zhangsan |
|||
*/ |
|||
@Data |
|||
public class PongMessage extends ServerMessage { |
|||
public PongMessage(){ |
|||
setType(MessageConstant.ServerMessageType.Pong.name()); |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.server; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author zhangsan |
|||
*/ |
|||
@Data |
|||
public class QueueStatusMessage extends ServerMessage { |
|||
@Setter |
|||
@Getter |
|||
public static class Data{ |
|||
private MessageConstant.Error error; |
|||
} |
|||
private Data data; |
|||
|
|||
public QueueStatusMessage(){ |
|||
setType(MessageConstant.ServerMessageType.ChannelStatus.name()); |
|||
} |
|||
|
|||
public QueueStatusMessage(MessageConstant.Error error){ |
|||
this(); |
|||
data = new Data(); |
|||
data.error = error; |
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.server; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author zhangsan |
|||
*/ |
|||
@Data |
|||
public class ServerAckMessage extends ServerMessage { |
|||
@lombok.Data |
|||
public static class Data{ |
|||
/** |
|||
* 消息ID |
|||
*/ |
|||
private String msgId; |
|||
/** |
|||
* 发送者指定的unikey,如果发送者未指定,则为null |
|||
*/ |
|||
private String unikey; |
|||
/** |
|||
* 消息状态 |
|||
*/ |
|||
private MessageConstant.Error error; |
|||
|
|||
public Data(String msgId,String unikey,MessageConstant.Error error){ |
|||
this.msgId = msgId; |
|||
this.unikey = unikey; |
|||
this.error = error; |
|||
} |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public ServerAckMessage(){ |
|||
setType(MessageConstant.ServerMessageType.Ack.name()); |
|||
} |
|||
|
|||
public ServerAckMessage(MessageConstant.Error status){ |
|||
this(); |
|||
this.data = new Data(null,null,status); |
|||
} |
|||
|
|||
public ServerAckMessage(String msgId,String unikey,MessageConstant.Error status){ |
|||
this(); |
|||
this.data = new Data(msgId,unikey,status); |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package com.ccsens.ccmq.lowlevel.message.server; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage; |
|||
import lombok.Data; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
|
|||
/** |
|||
* @author wei |
|||
* 客户端向服务器请求当前连接状态 |
|||
*/ |
|||
@Data |
|||
public class UnExceptedErrorMessage extends ServerMessage { |
|||
@Getter@Setter |
|||
public static class Data{ |
|||
private String errorString; |
|||
} |
|||
|
|||
private Data data; |
|||
|
|||
public UnExceptedErrorMessage(){ |
|||
setType(MessageConstant.ClientMessageType.UnExceptedError.name()); |
|||
} |
|||
|
|||
public UnExceptedErrorMessage(String errorString){ |
|||
this(); |
|||
data = new Data(); |
|||
data.setErrorString(errorString); |
|||
} |
|||
} |
@ -0,0 +1,118 @@ |
|||
package com.ccsens.ccmq.lowlevel.persist; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.Message; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
public interface IMessageDao { |
|||
/** |
|||
* 保存原始消息 |
|||
* @param inMessage message to be saved |
|||
* @throws Exception 可能产生的异常 |
|||
*/ |
|||
void saveOrUpdateMessage(InMessage inMessage) throws Exception; |
|||
/** |
|||
* 保存message |
|||
* @param message message to be saved |
|||
* @throws Exception 可能产生的异常 |
|||
*/ |
|||
void saveOrUpdateMessage(Message message) throws Exception; |
|||
|
|||
/** |
|||
* 更新消息的某个接受者的hasRead状态 |
|||
* @param fromDomain 消息fromDomain |
|||
* @param fromUserId 消息from |
|||
* @param toDomain 消息toDomain |
|||
* @param toUserId 接受者ID |
|||
* @param time 该time之前(包括自己)的消息 |
|||
* @param hasRead 是否已读 |
|||
* @throws Exception |
|||
*/ |
|||
void updateMessageReadStatus(String fromDomain, String fromUserId, String toDomain, String toUserId, Long time, byte hasRead)throws Exception; |
|||
|
|||
/** |
|||
* 更新消息的某个接受者的 ack和success状态 |
|||
* @param msgId 消息ID |
|||
* @param acked 是否ack |
|||
* @param status status |
|||
*/ |
|||
void updateMessageAckAndStatus(String msgId, byte acked, MessageConstant.Status status); |
|||
|
|||
/** |
|||
* 根据rawId修改message status |
|||
* @param rawId 原始消息ID |
|||
* @param status 要修改的状态 |
|||
*/ |
|||
void updateMessageStatusByRawId(String rawId, MessageConstant.Status status); |
|||
/** |
|||
* 更新消息状态 |
|||
* @param msgId |
|||
* @param status Normal、Deleted |
|||
*/ |
|||
void updateMessageStatus(String msgId, MessageConstant.Status status); |
|||
|
|||
/** |
|||
* 更新所有的status==TempSucceed的消息为pending |
|||
*/ |
|||
void updateMessageStatusToPending(); |
|||
|
|||
/** |
|||
* 更新所有的status==TempSucceed的消息为pending |
|||
* @param domainType 域 |
|||
* @param userId 用户 |
|||
*/ |
|||
void updateMessageStatusToPending(MessageConstant.DomainType domainType, String userId); |
|||
|
|||
/** |
|||
* 检查pending消息是否过期,如果是,设置为expired状态 |
|||
*/ |
|||
void updateMessageToExpired(); |
|||
|
|||
/** |
|||
* 检查pending消息是否达到发送上限,如果是,设置为SendTimesUpLimit状态 |
|||
*/ |
|||
void updateMessageToSendTimesUpLimit(); |
|||
/** |
|||
* 增加某个消息的发送次数,并添加一条发送记录 |
|||
* @param id 消息ID |
|||
* @param time 发送时间 |
|||
*/ |
|||
void incrementSendTimes(String id, Long time); |
|||
|
|||
/** |
|||
* 根据ID获取MessageEntity |
|||
* @param msgId 消息ID |
|||
* @return 消息实体类 |
|||
*/ |
|||
Message getMessageById(String msgId); |
|||
|
|||
/** |
|||
* 根据ID获取MessageEntity |
|||
* @param rawId 消息ID |
|||
* @return 消息实体类集合 |
|||
*/ |
|||
List<Message> getMessageByRawId(String rawId); |
|||
|
|||
/** |
|||
* 获取某个客户端pending的消息集合,最多获取maxMessageNum个。 |
|||
* @param toDomain 消息的接收域 |
|||
* @param to 消息的接收者Id |
|||
* @param maxMessageNum 最多查询数量 |
|||
* @return 消息集合 |
|||
*/ |
|||
List<Message> getClientPendingMessage(MessageConstant.DomainType toDomain, String to, Integer maxMessageNum); |
|||
|
|||
/** |
|||
* 获取toDomain域、to用户的未ack消息数量 |
|||
* @param toDomain 域 |
|||
* @param to 用户 |
|||
* @return 未ack消息数量 |
|||
*/ |
|||
Long countClientPendingMessage(MessageConstant.DomainType toDomain, String to); |
|||
|
|||
} |
@ -0,0 +1,180 @@ |
|||
package com.ccsens.ccmq.lowlevel.persist; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.Message; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
import com.mongodb.BasicDBObject; |
|||
import com.mongodb.DBObject; |
|||
import org.bson.Document; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.data.domain.Sort; |
|||
import org.springframework.data.mongodb.core.MongoTemplate; |
|||
import org.springframework.data.mongodb.core.query.Criteria; |
|||
import org.springframework.data.mongodb.core.query.Query; |
|||
import org.springframework.data.mongodb.core.query.Update; |
|||
import org.springframework.stereotype.Repository; |
|||
import wiki.tall.ccmq.common.util.DateUtil; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Repository |
|||
public class MessageDao implements IMessageDao { |
|||
private static final String COLLECTION_RAW_MESSAGE = "col_message_raw"; |
|||
private static final String COLLECTION_MESSAGE = "col_message"; |
|||
@Autowired |
|||
private MongoTemplate mongoTemplate; |
|||
|
|||
@Override |
|||
public void saveOrUpdateMessage(InMessage inMessage) throws Exception { |
|||
mongoTemplate.save(inMessage,COLLECTION_RAW_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void saveOrUpdateMessage(Message message) throws Exception { |
|||
mongoTemplate.save(message,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageReadStatus(String fromDomain,String fromUserId,String toDomain,String toUserId,Long time,byte hasRead) throws Exception { |
|||
Query query = new Query( |
|||
Criteria.where("fromDomain").is(fromDomain).andOperator( |
|||
Criteria.where("from").is(fromUserId).andOperator( |
|||
Criteria.where("toDomain").is(toDomain).andOperator( |
|||
Criteria.where("to").is(toUserId).andOperator( |
|||
Criteria.where("time").lte(time) |
|||
) |
|||
) |
|||
)) |
|||
); |
|||
Update update = new Update().set("hasRead",hasRead); |
|||
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageAckAndStatus(String msgId, byte acked, MessageConstant.Status status) { |
|||
Query query = new Query(Criteria.where("id").is(msgId)); |
|||
Update update = new Update().set("acked",acked).set("status",status); |
|||
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageStatusByRawId(String rawId, MessageConstant.Status status) { |
|||
Query query = new Query(Criteria.where("rawId").is(rawId)); |
|||
Update update = new Update().set("status",status); |
|||
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageStatus(String msgId, MessageConstant.Status status) { |
|||
Query query = new Query(Criteria.where("id").is(msgId)); |
|||
Update update = new Update().set("status",status); |
|||
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageStatusToPending() { |
|||
Query query = new Query(Criteria.where("status").is(MessageConstant.Status.TempSucceed)); |
|||
Update update = new Update().set("status",MessageConstant.Status.Pending); |
|||
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageStatusToPending(MessageConstant.DomainType domainType, String userId) { |
|||
Query query = new Query() |
|||
.addCriteria(Criteria.where("toDomain").is(domainType)) |
|||
.addCriteria(Criteria.where("to").is(userId)) |
|||
.addCriteria(Criteria.where("status").is(MessageConstant.Status.TempSucceed)); |
|||
Update update = new Update().set("status",MessageConstant.Status.Pending); |
|||
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageToExpired() { |
|||
Query query = new Query() |
|||
.addCriteria(Criteria.where("status").is(MessageConstant.Status.Pending)) |
|||
.addCriteria(Criteria.where("rule.expireAt").gt(0).lt(DateUtil.currentSeconds())); |
|||
Update update = new Update().set("status",MessageConstant.Status.Expired); |
|||
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void updateMessageToSendTimesUpLimit() { |
|||
Query query = new Query() |
|||
.addCriteria(Criteria.where("status").is(MessageConstant.Status.Pending)) |
|||
.addCriteria(Criteria.where("rule.noAckRetryTimes").gt(0)) |
|||
.addCriteria(new Criteria(){ |
|||
@Override |
|||
public Document getCriteriaObject() { |
|||
Document obj = new Document(); |
|||
obj.put("$where", "this.sendTimes >= this.rule.noAckRetryTimes"); |
|||
return obj; |
|||
} |
|||
}); |
|||
Update update = new Update().set("status",MessageConstant.Status.SendTimesUpLimit); |
|||
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public void incrementSendTimes(String msgId,Long time) { |
|||
Query query = new Query(Criteria.where("id").is(msgId)); |
|||
Update update = new Update(); |
|||
update.inc("sendTimes"); |
|||
update.addToSet("sendTimeInSecondList", time); |
|||
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public Message getMessageById(String msgId) { |
|||
return mongoTemplate.findById(msgId, Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public List<Message> getMessageByRawId(String rawId) { |
|||
Query query = new Query().addCriteria(Criteria.where("rawId").is(rawId)); |
|||
return mongoTemplate.find(query,Message.class); |
|||
} |
|||
|
|||
@Override |
|||
public List<Message> getClientPendingMessage(MessageConstant.DomainType toDomain, String to, Integer maxMessageNum) { |
|||
Query query = clientPendingMessageQuery(toDomain,to) |
|||
.with(Sort.by(Sort.Order.asc("time"))); |
|||
if(maxMessageNum == null || maxMessageNum.intValue() == 0) { |
|||
query.limit(maxMessageNum); |
|||
} |
|||
return mongoTemplate.find(query,Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
@Override |
|||
public Long countClientPendingMessage(MessageConstant.DomainType toDomain, String to) { |
|||
Query query = clientPendingMessageQuery(toDomain,to); |
|||
return mongoTemplate.count(query,Message.class,COLLECTION_MESSAGE); |
|||
} |
|||
|
|||
private Query clientPendingMessageQuery(MessageConstant.DomainType toDomain, String to){ |
|||
return new Query().addCriteria(Criteria.where("toDomain").is(toDomain)) |
|||
.addCriteria(Criteria.where("to").is(to)) |
|||
//状态为pending
|
|||
.addCriteria(Criteria.where("status").is(MessageConstant.Status.Pending)) |
|||
.addCriteria(new Criteria().andOperator( |
|||
//未过期消息
|
|||
new Criteria().orOperator( |
|||
Criteria.where("rule.expireAt").is(0), |
|||
Criteria.where("rule.expireAt").gt(DateUtil.currentSeconds() |
|||
)), |
|||
//未达到发送次数上限
|
|||
new Criteria().orOperator( |
|||
Criteria.where("rule.noAckRetryTimes").is(0), |
|||
new Criteria() { |
|||
@Override |
|||
public Document getCriteriaObject() { |
|||
Document obj = new Document(); |
|||
obj.put("$where", "this.sendTimes < this.rule.noAckRetryTimes"); |
|||
return obj; |
|||
} |
|||
}) |
|||
)); |
|||
} |
|||
} |
@ -0,0 +1,9 @@ |
|||
package com.ccsens.ccmq.lowlevel.service; |
|||
|
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
|
|||
//@FeignClient(value = "eureka-client-btpro",fallback = TokenSer.class)
|
|||
public interface IUserService { |
|||
//@GetMapping(value="/btpro/v1.0/token")
|
|||
String getUserIdByToken(@RequestParam(value = "token") String token); |
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.ccsens.ccmq.lowlevel.service; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Service |
|||
public class UserService implements IUserService{ |
|||
@Autowired |
|||
private RestTemplate restTemplate; |
|||
|
|||
@Override |
|||
public String getUserIdByToken(String token){ |
|||
String userId = null; |
|||
// String url = "https://test.tall.wiki/gateway/tall/v1.0/users/claims?token="+token;
|
|||
String url = "http://localhost:7030/v1.0/users/claims?token="+token; |
|||
ResponseEntity<String> response = restTemplate.getForEntity(url,String.class); |
|||
String strBody = null; |
|||
if(response.getStatusCodeValue() == 200){ |
|||
strBody = response.getBody(); |
|||
} |
|||
if(StrUtil.isNotEmpty(strBody)) { |
|||
userId = strBody; |
|||
} |
|||
return userId; |
|||
} |
|||
} |
@ -0,0 +1,2 @@ |
|||
config.stopBubbling=true |
|||
lombok.equalsAndHashCode.callSuper=call |
@ -0,0 +1,64 @@ |
|||
package wiki.tall.ccmq.common; |
|||
|
|||
import com.ccsens.ccmq.lowlevel.client.netty.tcphexserver.NettyMBServer; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.tcptextserver.NettyTextServer; |
|||
import com.ccsens.ccmq.lowlevel.client.netty.wsserver.NettyWsServer; |
|||
import com.ccsens.ccmq.lowlevel.message.MessageHandler; |
|||
|
|||
import wiki.tall.ccmq.common.util.WebConstant; |
|||
import org.mybatis.spring.annotation.MapperScan; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.boot.web.servlet.ServletComponentScan; |
|||
import org.springframework.context.annotation.ComponentScan; |
|||
import org.springframework.scheduling.annotation.EnableAsync; |
|||
import org.springframework.scheduling.annotation.EnableScheduling; |
|||
|
|||
import java.io.File; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@ComponentScan(basePackages = {"com.ccsens.ccmq.lowlevel.*","wiki.tall.ccmq.common.*"}) |
|||
@MapperScan(basePackages = {"wiki.tall.ccmq.common.persist.*"}) |
|||
@ServletComponentScan |
|||
@EnableScheduling |
|||
@EnableAsync |
|||
@SpringBootApplication |
|||
//@EnableDiscoveryClient
|
|||
//@EnableFeignClients(basePackages = "com.ccsens.feignclient")
|
|||
//@EnableCircuitBreaker
|
|||
public class TallMessageApplication implements CommandLineRunner { |
|||
@Autowired |
|||
private NettyMBServer nettyMBServer; |
|||
@Autowired |
|||
private NettyWsServer nettyWsServer; |
|||
@Autowired |
|||
private NettyTextServer nettyTextServer; |
|||
@Autowired |
|||
private MessageHandler messageHandler; |
|||
|
|||
public static void main(String[] args) { |
|||
createNecessaryDir(); |
|||
SpringApplication.run(TallMessageApplication.class, args); |
|||
} |
|||
|
|||
private static void createNecessaryDir(){ |
|||
File dir = new File(WebConstant.IMG_PATH_PREFIX_SHARE_QR); |
|||
if(!dir.exists()){ |
|||
dir.mkdirs(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void run(String... args) throws Exception { |
|||
nettyWsServer.start(); |
|||
nettyMBServer.start(); |
|||
nettyTextServer.start(); |
|||
for(int i=0;i<1;i++) { |
|||
messageHandler.loopSendMessage(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,168 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
import wiki.tall.ccmq.common.util.CRCUtil; |
|||
import io.netty.buffer.ByteBuf; |
|||
|
|||
/** |
|||
* format: Filter(2) len(1) addr(1) oper(1) data(x) crc(2) |
|||
* eg: FF FE 0A 10 93 00 00 00 00 00 00 94 2A |
|||
* Notice: crc is example ,not the real data |
|||
*/ |
|||
public class CCModBusEntity { |
|||
public enum Error{ |
|||
ERROR_NONE, |
|||
ERROR_FILTER_NOT_MATCH, |
|||
ERROR_LEN_EXCLUEE_MAX, |
|||
ERROR_CRC_INVALID, |
|||
ERROR_NEED_MORE_DATA |
|||
} |
|||
|
|||
public static final int SIZE_FILTER = 2; |
|||
public static final int SIZE_LEN = 1; |
|||
public static final int SIZE_ADDR = 1; |
|||
public static final int SIZE_OPER = 1; |
|||
public static final int SIZE_CRC = 2; |
|||
public static final int SIZE_DATA_MIN = 1; |
|||
public static final int SIZE_DATA_MAX = 255 - SIZE_ADDR - SIZE_OPER - SIZE_CRC; |
|||
public static final int SIZE_MIN = SIZE_FILTER + SIZE_LEN + SIZE_ADDR + SIZE_OPER + SIZE_DATA_MIN + SIZE_CRC; |
|||
public static final int SIZE_MAX = SIZE_FILTER + SIZE_LEN + SIZE_ADDR + SIZE_OPER + SIZE_DATA_MAX + SIZE_CRC; |
|||
public static final byte[] FILTER = {(byte)0xFF,(byte)0xFE}; |
|||
// public static final byte[] TAILER = {0x0d,0x0a};
|
|||
public static final byte[] TAILER = {}; |
|||
|
|||
private byte modbusData[]; |
|||
private byte filter[]; |
|||
private int len; |
|||
private byte addr; |
|||
private byte oper; |
|||
private byte originData[]; |
|||
private byte crc[]; |
|||
|
|||
public CCModBusEntity(byte addr,byte oper,byte[] originData){ |
|||
modbusData = new byte[SIZE_FILTER + SIZE_LEN + SIZE_ADDR + SIZE_OPER + originData.length + SIZE_CRC + TAILER.length]; |
|||
int index = 0; |
|||
System.arraycopy(FILTER,0,modbusData,index,SIZE_FILTER); |
|||
index += SIZE_FILTER; |
|||
modbusData[index++] = (byte)((SIZE_ADDR + SIZE_OPER + originData.length + SIZE_CRC) & 0xFF); |
|||
modbusData[index++] = addr; |
|||
modbusData[index++] = oper; |
|||
System.arraycopy(originData,0,modbusData,index,originData.length); |
|||
index += originData.length; |
|||
|
|||
byte crc[] = new byte[SIZE_CRC]; |
|||
CRCUtil.crc16(crc,modbusData,SIZE_FILTER+SIZE_LEN,originData.length + SIZE_ADDR + SIZE_OPER); |
|||
modbusData[index++] = crc[1]; |
|||
modbusData[index++] = crc[0]; |
|||
for(int i=0;i<TAILER.length;i++){ |
|||
modbusData[index++] = TAILER[i]; |
|||
} |
|||
} |
|||
|
|||
public CCModBusEntity(ByteBuf in){ |
|||
this.modbusData = new byte[in.readableBytes()]; |
|||
in.getBytes(in.readerIndex(),this.modbusData); |
|||
} |
|||
|
|||
public byte[] getModbusData(){ |
|||
return modbusData; |
|||
} |
|||
|
|||
public byte[] getFilter(){ |
|||
if(filter == null){ |
|||
filter = new byte[SIZE_FILTER]; |
|||
} |
|||
int index = 0; |
|||
for(int i=0;i<SIZE_FILTER;i++){ |
|||
filter[i] = modbusData[index + i]; |
|||
} |
|||
return filter; |
|||
} |
|||
|
|||
public int getLen(){ |
|||
int index = SIZE_FILTER; |
|||
return modbusData[index] & 0xFF; |
|||
} |
|||
|
|||
public byte getAddr(){ |
|||
int index = SIZE_FILTER + SIZE_LEN; |
|||
return modbusData[index]; |
|||
} |
|||
|
|||
public byte getOper(){ |
|||
int index = SIZE_FILTER + SIZE_LEN + SIZE_ADDR; |
|||
return modbusData[index]; |
|||
} |
|||
|
|||
public byte[] getOriginData(){ |
|||
int dataLen = getLen() - SIZE_ADDR - SIZE_OPER - SIZE_CRC; |
|||
if(originData == null || originData.length != dataLen) { |
|||
originData = new byte[dataLen]; |
|||
} |
|||
int index = SIZE_FILTER + SIZE_LEN + SIZE_ADDR + SIZE_OPER; |
|||
System.arraycopy(modbusData,index,originData,0,dataLen); |
|||
return originData; |
|||
} |
|||
|
|||
public byte[] getCrc(){ |
|||
if(crc == null || crc.length != SIZE_CRC) |
|||
crc = new byte[SIZE_CRC]; |
|||
int index = SIZE_FILTER + SIZE_LEN + getLen() - SIZE_CRC; |
|||
System.arraycopy(modbusData,index,crc,0,SIZE_CRC); |
|||
return crc; |
|||
} |
|||
|
|||
public int getModbusLength(){ |
|||
return SIZE_FILTER + SIZE_LEN + getLen(); |
|||
} |
|||
|
|||
public boolean crcValid() { |
|||
// byte[] thisCRC = getCrc();
|
|||
// byte crc[] = {0,0};
|
|||
// int index = SIZE_FILTER + SIZE_LEN;
|
|||
// int length = getLen() - SIZE_CRC;
|
|||
// ProtocolUtil.crc16(crc, this.modbusData,index,length);
|
|||
// if(crc[1] == thisCRC[0] && crc[0]== thisCRC[1])
|
|||
// return true;
|
|||
// return false;
|
|||
return true; |
|||
} |
|||
|
|||
public void print(){ |
|||
filter = getFilter(); |
|||
System.out.printf("%02x %02x ",filter[0],filter[1]); |
|||
System.out.printf("%02x %02x %02x ",getLen(),getAddr(),getOper()); |
|||
originData = getOriginData(); |
|||
for(int i=0;i<originData.length;i++){ |
|||
System.out.printf("%02x ",originData[i]); |
|||
} |
|||
crc = getCrc(); |
|||
System.out.printf("%02x %02x ",crc[0],crc[1]); |
|||
System.out.println(); |
|||
} |
|||
|
|||
public Error valid(){ |
|||
//1.filter
|
|||
filter = getFilter(); |
|||
for(int i=0;i<SIZE_FILTER;i++){ |
|||
if(filter[i] != FILTER[i]) |
|||
return Error.ERROR_FILTER_NOT_MATCH; |
|||
} |
|||
|
|||
//2.len
|
|||
len = getLen(); |
|||
if(len > SIZE_DATA_MAX) |
|||
return Error.ERROR_LEN_EXCLUEE_MAX; |
|||
|
|||
//3.data 完整性
|
|||
if(this.modbusData.length < (SIZE_FILTER + SIZE_LEN + len)) { |
|||
return Error.ERROR_NEED_MORE_DATA; |
|||
} |
|||
|
|||
//4.crc
|
|||
if(!crcValid()){ |
|||
return Error.ERROR_CRC_INVALID; |
|||
} |
|||
|
|||
return Error.ERROR_NONE; |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
public class ChangeBeginTimeProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long nodeId; |
|||
public Long beginTime; |
|||
public Long offset; |
|||
} |
|||
private Data data; |
|||
|
|||
public Data getData() { |
|||
return data; |
|||
} |
|||
|
|||
public void setData(Data data){ |
|||
this.data = data; |
|||
} |
|||
|
|||
public void setData(Long nodeId,Long beginTime,Long offset){ |
|||
if(data == null) |
|||
data = new Data(); |
|||
data.nodeId = nodeId; |
|||
data.beginTime = beginTime; |
|||
data.offset = offset; |
|||
} |
|||
|
|||
public void setData(Long nodeId,Long offset){ |
|||
if(data == null) |
|||
data = new Data(); |
|||
data.nodeId = nodeId; |
|||
data.offset = offset; |
|||
data.beginTime = System.currentTimeMillis(); |
|||
} |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.nodeId + "," + data.beginTime; |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
public class ChangeDurationProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long taskId; |
|||
public Long nodeId; |
|||
public Long duration; |
|||
} |
|||
private Data data; |
|||
|
|||
public Data getData() { |
|||
return data; |
|||
} |
|||
|
|||
public void setData(Data data){ |
|||
this.data = data; |
|||
} |
|||
|
|||
public void setData(Long taskId,Long nodeId,Long duration){ |
|||
if(data == null) |
|||
data = new Data(); |
|||
data.taskId = taskId; |
|||
data.nodeId = nodeId; |
|||
data.duration = duration; |
|||
} |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.taskId + "," + data.nodeId + "," + data.duration; |
|||
} |
|||
} |
@ -0,0 +1,114 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
public class CommonProtocol { |
|||
public enum Event { |
|||
HEART(0x00, "heart"), |
|||
COUNTDOWN(0x01, "countdown"), |
|||
JOIN(0x02, "join"), |
|||
TIMING(0x03, "timing"), |
|||
PLAY(0x04, "play"), |
|||
LOAD(0x05, "load"), |
|||
CHANGEDURATION(0x06,"changeduration"), |
|||
CHANGEBEGINTIME(0x07,"changebegintime"), |
|||
ADD(0x08,"add"), |
|||
REMOVE(0x09,"remove"), |
|||
MOVE(0x0A,"MOVE"), |
|||
CHANGEAUTOMODE(0x0B,"changeautomode"), |
|||
TIMEHEART(0x10,"timeheart"); |
|||
|
|||
public int value; |
|||
public String phase; |
|||
|
|||
Event(int value, String thePhase) { |
|||
this.value = value; |
|||
this.phase = thePhase; |
|||
} |
|||
|
|||
public static Event valueOf(int value) { // 手写的从int到enum的转换函数
|
|||
switch (value) { |
|||
case 0x00: |
|||
return HEART; |
|||
case 0x01: |
|||
return COUNTDOWN; |
|||
case 0x02: |
|||
return JOIN; |
|||
case 0x03: |
|||
return TIMING; |
|||
case 0x04: |
|||
return PLAY; |
|||
case 0x05: |
|||
return LOAD; |
|||
case 0x06: |
|||
return CHANGEDURATION; |
|||
case 0x07: |
|||
return CHANGEBEGINTIME; |
|||
case 0x08: |
|||
return ADD; |
|||
case 0x09: |
|||
return REMOVE; |
|||
case 0x0A: |
|||
return MOVE; |
|||
case 0x0B: |
|||
return CHANGEAUTOMODE; |
|||
case 0x10: |
|||
return TIMEHEART; |
|||
default: |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public static Event textOf(String event) { |
|||
if (event.equalsIgnoreCase("heart")) { |
|||
return HEART; |
|||
} else if (event.equalsIgnoreCase("countdown")) { |
|||
return COUNTDOWN; |
|||
} else if (event.equalsIgnoreCase("join")) { |
|||
return JOIN; |
|||
} else if (event.equalsIgnoreCase("timing")) { |
|||
return TIMING; |
|||
} else if (event.equalsIgnoreCase("play")) { |
|||
return PLAY; |
|||
} else if (event.equalsIgnoreCase("load")) { |
|||
return LOAD; |
|||
} else if(event.equalsIgnoreCase("changeduration")){ |
|||
return CHANGEDURATION; |
|||
}else if(event.equalsIgnoreCase("changebegintime")){ |
|||
return CHANGEBEGINTIME; |
|||
}else if(event.equalsIgnoreCase("add")){ |
|||
return ADD; |
|||
}else if(event.equalsIgnoreCase("remove")){ |
|||
return REMOVE; |
|||
}else if(event.equalsIgnoreCase("move")){ |
|||
return MOVE; |
|||
}else if(event.equalsIgnoreCase("changeautomode")){ |
|||
return CHANGEAUTOMODE; |
|||
}else if(event.equalsIgnoreCase("timeheart")){ |
|||
return TIMEHEART; |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
private Event event; |
|||
|
|||
public void setModbusEvent(int value) { |
|||
this.event = Event.valueOf(value); |
|||
} |
|||
|
|||
public byte getModbusEvent() { |
|||
return (byte) (event.value & 0xFF); |
|||
} |
|||
|
|||
public void setEvent(String text) { |
|||
this.event = Event.textOf(text); |
|||
} |
|||
|
|||
public String getEvent() { |
|||
return event.phase; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return getEvent(); |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class CountdownProtocol extends CommonProtocol { |
|||
|
|||
public static class Data{ |
|||
public Long second; |
|||
public Long beepSecond; |
|||
public Long beepType; |
|||
} |
|||
private Data data; |
|||
|
|||
public CountdownProtocol(){ |
|||
setEvent("countdown"); |
|||
} |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.second + "," + data.beepSecond + "," + data.beepType; |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class HeartProtocol extends CommonProtocol { |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent(); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class JoinProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long taskId; |
|||
} |
|||
private Data data; |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.taskId; |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class LoadProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long taskId; |
|||
} |
|||
private Data data; |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.taskId; |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
import java.util.List; |
|||
|
|||
public class MoveProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long taskId; |
|||
public List<String> nodeIds; |
|||
} |
|||
private Data data; |
|||
|
|||
public Data getData() { |
|||
return data; |
|||
} |
|||
|
|||
public void setData(Data data){ |
|||
this.data = data; |
|||
} |
|||
|
|||
public void setData(Long taskId,List<String> nodeIds){ |
|||
if(data == null) |
|||
data = new Data(); |
|||
data.taskId = taskId; |
|||
data.nodeIds = nodeIds; |
|||
} |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.taskId + "," + data.nodeIds; |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
public class PlayProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long nodeId; |
|||
public Long beginTime; |
|||
} |
|||
private Data data; |
|||
|
|||
public Data getData() { |
|||
return data; |
|||
} |
|||
|
|||
public void setData(Data data){ |
|||
this.data = data; |
|||
} |
|||
|
|||
public void setData(Long nodeId,Long beginTime){ |
|||
if(data == null) |
|||
data = new Data(); |
|||
data.nodeId = nodeId; |
|||
data.beginTime = beginTime; |
|||
} |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.nodeId + "," + data.beginTime; |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
public class RemoveProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long taskId; |
|||
public Long nodeId; |
|||
} |
|||
private Data data; |
|||
|
|||
public Data getData() { |
|||
return data; |
|||
} |
|||
|
|||
public void setData(Data data){ |
|||
this.data = data; |
|||
} |
|||
|
|||
public void setData(Long taskId,Long nodeId){ |
|||
if(data == null) |
|||
data = new Data(); |
|||
data.taskId = taskId; |
|||
data.nodeId = nodeId; |
|||
} |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.taskId + "," + data.nodeId; |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
public class TimeHeartProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long ctime; |
|||
public Long stime; |
|||
} |
|||
private Data data; |
|||
|
|||
public Data getData() { |
|||
return data; |
|||
} |
|||
|
|||
public void setData(Data data){ |
|||
this.data = data; |
|||
} |
|||
|
|||
public void setData(Long ctime,Long stime){ |
|||
if(data == null) { |
|||
data = new Data(); |
|||
} |
|||
data.ctime = ctime; |
|||
data.stime = stime; |
|||
} |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.ctime + "," + data.stime; |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package wiki.tall.ccmq.common.bean.dto.ccmodbus; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class TimingProtocol extends CommonProtocol { |
|||
public static class Data{ |
|||
public Long timestamp; |
|||
} |
|||
private Data data; |
|||
|
|||
@Override |
|||
public String toString(){ |
|||
return getEvent() + "," + data.timestamp; |
|||
} |
|||
} |
@ -0,0 +1,108 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import cn.hutool.core.codec.Base64; |
|||
import cn.hutool.core.util.CharsetUtil; |
|||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm; |
|||
import cn.hutool.crypto.symmetric.SymmetricCrypto; |
|||
import com.alibaba.druid.pool.DruidDataSource; |
|||
import wiki.tall.ccmq.common.util.WebConstant; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.PropertySource; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.sql.DataSource; |
|||
import java.sql.SQLException; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Component |
|||
@PropertySource(value = { "classpath:druid-${spring.profiles.active}.properties" }) |
|||
@ConfigurationProperties(prefix="spring.datasource.druid") |
|||
@Getter @Setter |
|||
public class DruidProps { |
|||
private Logger logger = LoggerFactory.getLogger(DruidProps.class); |
|||
|
|||
private String dynamicUrl; |
|||
private String url; |
|||
private String username; |
|||
private String password; |
|||
private String driverClassName; |
|||
private int initialSize; |
|||
private int minIdle; |
|||
private int maxActive; |
|||
private int maxWait; |
|||
private int timeBetweenEvictionRunsMillis; |
|||
private int minEvictableIdleTimeMillis; |
|||
private String validationQuery; |
|||
private boolean testWhileIdle; |
|||
private boolean testOnBorrow; |
|||
private boolean testOnReturn; |
|||
private boolean poolPreparedStatements; |
|||
private int maxPoolPreparedStatementPerConnectionSize; |
|||
private String filters; |
|||
private String connectionProperties; |
|||
private String logSlowSql; |
|||
private String servletName; |
|||
private String servletUrlMapping; |
|||
private String servletLoginUsername; |
|||
private String servletLoginPassword; |
|||
private String servletLogSlowSql; |
|||
private String servletResetEnable; |
|||
private String filterName; |
|||
private String filterUrlPattern; |
|||
private String filterExclusions; |
|||
private String filterProfileEnable; |
|||
|
|||
public DataSource createDruidDataSource() { |
|||
String url = this.url; |
|||
return createDruidDataSource(url); |
|||
} |
|||
|
|||
public DataSource createDynamicDruidDataSource(String schema) { |
|||
String url = dynamicUrl.replace(WebConstant.DYNAMIC_DATASOURCE_SCHEMA_KEY,schema); |
|||
return createDruidDataSource(url); |
|||
} |
|||
|
|||
public String getPassword(){ |
|||
String key = System.getenv("CCSENS_TALL_TEST"); |
|||
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, Base64.decode(key)); |
|||
return key; |
|||
// return aes.decryptStr(password, CharsetUtil.CHARSET_UTF_8);
|
|||
} |
|||
|
|||
private DataSource createDruidDataSource(String url){ |
|||
DruidDataSource datasource = new DruidDataSource(); |
|||
|
|||
datasource.setUrl(url); |
|||
datasource.setUsername(username); |
|||
datasource.setPassword(getPassword()); |
|||
datasource.setDriverClassName(driverClassName); |
|||
|
|||
//configuration
|
|||
datasource.setInitialSize(initialSize); |
|||
datasource.setMinIdle(minIdle); |
|||
datasource.setMaxActive(maxActive); |
|||
datasource.setMaxWait(maxWait); |
|||
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); |
|||
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); |
|||
datasource.setValidationQuery(validationQuery); |
|||
datasource.setTestWhileIdle(testWhileIdle); |
|||
datasource.setTestOnBorrow(testOnBorrow); |
|||
datasource.setTestOnReturn(testOnReturn); |
|||
datasource.setPoolPreparedStatements(poolPreparedStatements); |
|||
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); |
|||
try { |
|||
datasource.setFilters(filters); |
|||
} catch (SQLException e) { |
|||
logger.error("druid configuration initialization filter : {0}", e); |
|||
e.printStackTrace(); |
|||
} |
|||
datasource.setConnectionProperties(connectionProperties); |
|||
return datasource; |
|||
} |
|||
} |
@ -0,0 +1,44 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import com.jfinal.template.Engine; |
|||
import com.jfinal.template.ext.spring.JFinalViewResolver; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@Configuration |
|||
public class EnjoyConfig { |
|||
@Bean(name = "jfinalViewResolver") |
|||
public JFinalViewResolver getJFinalViewResolver() { |
|||
|
|||
// 创建用于整合 spring boot 的 ViewResolver 扩展对象
|
|||
JFinalViewResolver jfr = new JFinalViewResolver(); |
|||
|
|||
// 对 spring boot 进行配置
|
|||
jfr.setSuffix(".html"); |
|||
jfr.setContentType("text/html;charset=UTF-8"); |
|||
jfr.setOrder(0); |
|||
|
|||
// 获取 engine 对象,对 enjoy 模板引擎进行配置,配置方式与前面章节完全一样
|
|||
Engine engine = JFinalViewResolver.engine; |
|||
|
|||
// 热加载配置能对后续配置产生影响,需要放在最前面
|
|||
engine.setDevMode(true); |
|||
|
|||
// 使用 ClassPathSourceFactory 从 class path 与 jar 包中加载模板文件
|
|||
engine.setToClassPathSourceFactory(); |
|||
|
|||
// 在使用 ClassPathSourceFactory 时要使用 setBaseTemplatePath
|
|||
// 代替 jfr.setPrefix("/view/")
|
|||
engine.setBaseTemplatePath("/template/"); |
|||
|
|||
// 添加模板函数
|
|||
//engine.addSharedFunction("/common/_layout.html");
|
|||
//engine.addSharedFunction("/common/_paginate.html");
|
|||
|
|||
// 更多配置与前面章节完全一样
|
|||
// engine.addDirective(...)
|
|||
// engine.addSharedMethod(...);
|
|||
|
|||
return jfr; |
|||
} |
|||
} |
@ -0,0 +1,71 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import wiki.tall.ccmq.common.exception.BaseException; |
|||
import wiki.tall.ccmq.common.util.JsonResponse; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.http.converter.HttpMessageNotReadableException; |
|||
import org.springframework.validation.BindException; |
|||
import org.springframework.validation.BindingResult; |
|||
import org.springframework.validation.FieldError; |
|||
import org.springframework.web.bind.MethodArgumentNotValidException; |
|||
import org.springframework.web.bind.annotation.ControllerAdvice; |
|||
import org.springframework.web.bind.annotation.ExceptionHandler; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@ControllerAdvice |
|||
public class ExcepHandler { |
|||
private Logger logger = LoggerFactory.getLogger(ExcepHandler.class); |
|||
|
|||
@ExceptionHandler(value = MethodArgumentNotValidException.class) |
|||
@ResponseBody |
|||
public JsonResponse handleValidateException(HttpServletRequest req,MethodArgumentNotValidException e){ |
|||
logger.info("ExpHandler.MethodArgumentNotValidException: {}",e.getMessage()); |
|||
BindingResult bindingResult = e.getBindingResult(); |
|||
StringBuilder errorMesssage = new StringBuilder(); |
|||
errorMesssage.append(bindingResult.getFieldErrors().get(0).getDefaultMessage()); |
|||
e.printStackTrace(); |
|||
return JsonResponse.newInstance().fail(-10,errorMesssage.toString()); |
|||
} |
|||
|
|||
@ExceptionHandler(value =BindException.class) |
|||
@ResponseBody |
|||
public JsonResponse handleBindException(BindException e) { |
|||
logger.info("ExpHandler.BindException: {}", e.getMessage()); |
|||
FieldError fieldError = e.getFieldError(); |
|||
StringBuilder sb = new StringBuilder(); |
|||
sb.append(fieldError.getField()).append("=[").append(fieldError.getRejectedValue()).append("]") |
|||
.append(fieldError.getDefaultMessage()); |
|||
e.printStackTrace(); |
|||
return JsonResponse.newInstance().fail(-11,sb.toString()); |
|||
} |
|||
|
|||
@ExceptionHandler(value =HttpMessageNotReadableException.class) |
|||
@ResponseBody |
|||
public JsonResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { |
|||
logger.info("ExpHandler.HttpMessageNotReadableException: {}", e.getMessage()); |
|||
e.printStackTrace(); |
|||
return JsonResponse.newInstance().fail(-12,e.getMessage()); |
|||
} |
|||
|
|||
@ExceptionHandler(value = BaseException.class) |
|||
@ResponseBody |
|||
public JsonResponse jsonBaseExceptionHandler(HttpServletRequest req, BaseException e) { |
|||
logger.info("ExpHandler.BaseException: {}",e.getMessage()); |
|||
e.printStackTrace(); |
|||
return JsonResponse.newInstance().fail(e.getCode(),e.getMessage()); |
|||
} |
|||
|
|||
@ExceptionHandler(value = Exception.class) |
|||
@ResponseBody |
|||
public JsonResponse jsonExceptionHandler(HttpServletRequest req, Exception e) { |
|||
logger.info("ExpHandler.Exception: {}",e.getMessage()); |
|||
e.printStackTrace(); |
|||
return JsonResponse.newInstance().fail(-1,e.getMessage()); |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import org.springframework.amqp.core.Queue; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Configuration |
|||
public class RabbitMqConfig { |
|||
@Autowired |
|||
private SettingProps settingProps; |
|||
|
|||
@Bean |
|||
public Queue receivedQueue(){ |
|||
return new Queue(settingProps.getMq().getInName()); |
|||
} |
|||
|
|||
@Bean |
|||
public Queue sendQueue(){ |
|||
return new Queue(settingProps.getMq().getOutName()); |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonAutoDetect; |
|||
import com.fasterxml.jackson.annotation.PropertyAccessor; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Configuration |
|||
public class RedisConfig { |
|||
@Bean("redisTemplate") |
|||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { |
|||
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); |
|||
template.setConnectionFactory(factory); |
|||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); |
|||
ObjectMapper om = new ObjectMapper(); |
|||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); |
|||
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); |
|||
jackson2JsonRedisSerializer.setObjectMapper(om); |
|||
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); |
|||
// key采用String的序列化方式
|
|||
template.setKeySerializer(stringRedisSerializer); |
|||
// hash的key也采用String的序列化方式
|
|||
template.setHashKeySerializer(stringRedisSerializer); |
|||
// value序列化方式采用jackson
|
|||
template.setValueSerializer(jackson2JsonRedisSerializer); |
|||
// hash的value序列化方式采用jackson
|
|||
template.setHashValueSerializer(jackson2JsonRedisSerializer); |
|||
template.afterPropertiesSet(); |
|||
return template; |
|||
} |
|||
} |
@ -0,0 +1,74 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import wiki.tall.ccmq.common.util.HttpsClientRequestFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.autoconfigure.AutoConfigureAfter; |
|||
import org.springframework.boot.web.client.RestTemplateBuilder; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.http.converter.HttpMessageConverter; |
|||
import org.springframework.http.converter.StringHttpMessageConverter; |
|||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Configuration |
|||
@AutoConfigureAfter(SpringConfig.class) |
|||
public class RestClientConfig { |
|||
@Autowired |
|||
private RestTemplateBuilder builder; |
|||
|
|||
/** |
|||
* 支持http和https,以及GZIP压缩,UTF-8 |
|||
* @return |
|||
*/ |
|||
@Bean |
|||
public RestTemplate restTemplate(){ |
|||
return builder.build(); |
|||
} |
|||
|
|||
@Bean |
|||
public RestTemplate sslRestTemplate(){ |
|||
RestTemplate restTemplate = new RestTemplate(httpsClientRequestFactory()); |
|||
changeConverters(restTemplate); |
|||
return restTemplate; |
|||
} |
|||
|
|||
/** |
|||
* 发送HTTPS的逻辑代码是可以拿来发送HTTP的。但是根据我们写得HttpsClientRequestFactory类中的代码可知,会打印出异常(异常抛出后被catch了) |
|||
* 如果不想看见该异常,可以注释掉抛出异常的代码 |
|||
* class HttpsClientRequestFactory: |
|||
* if (!(connection instanceof HttpsURLConnection)) { |
|||
* //throw new RuntimeException("An instance of HttpsURLConnection is expected");
|
|||
* } |
|||
* @return |
|||
*/ |
|||
@Bean |
|||
public HttpsClientRequestFactory httpsClientRequestFactory(){ |
|||
return new HttpsClientRequestFactory(); |
|||
} |
|||
|
|||
private void changeConverters(RestTemplate restTemplate){ |
|||
//解决有可能出现的中文乱码问题
|
|||
List<MediaType> mediaTypeList = new ArrayList<>(); |
|||
mediaTypeList.add(MediaType.TEXT_HTML); |
|||
mediaTypeList.add(MediaType.APPLICATION_JSON_UTF8); |
|||
|
|||
List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); |
|||
for (HttpMessageConverter<?> httpMessageConverter : converterList) { |
|||
if(httpMessageConverter instanceof StringHttpMessageConverter) { |
|||
((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8); |
|||
} |
|||
if(httpMessageConverter instanceof MappingJackson2HttpMessageConverter) { |
|||
((MappingJackson2HttpMessageConverter) httpMessageConverter).setSupportedMediaTypes(mediaTypeList); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,123 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import com.alibaba.druid.support.http.StatViewServlet; |
|||
import com.alibaba.druid.support.http.WebStatFilter; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.web.servlet.FilterRegistrationBean; |
|||
import org.springframework.boot.web.servlet.ServletRegistrationBean; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
/** |
|||
* 在springboot添加过滤器有两种方式: |
|||
1、通过创建FilterRegistrationBean的方式 |
|||
1.1 建议使用此种方式,统一管理,且通过注解的方式若不是本地调试, |
|||
如果在filter中需要增加cookie可能会存在写不进前端情况 |
|||
1.2 有多个filter就创建多个FilterRegistrationBean ,若需注明filter的执行顺序, |
|||
可通过registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE - 1)配置, |
|||
值越大,执行顺序越靠后 |
|||
2、通过注解@WebFilter的方式 |
|||
2.1 在启动类上增加@ServletComponentScan注解,自动扫描带有过滤器注解的包 |
|||
2.2 在类上使用@WebFilter和@Order组合实现 |
|||
3、通过注解@WebFilter的方式 |
|||
3.1 在类上使用@WebFilter和@Order组合实现 |
|||
3.2 在类上添加@Component注解 |
|||
*/ |
|||
|
|||
/** |
|||
* 原生方案 |
|||
* @WebServlet 、@WebListener、@WebFilter |
|||
* 在启动类上增加@ServletComponentScan注解,自动扫描带有Servlet原生方案注解的包 |
|||
* |
|||
* SpringBoot方案 |
|||
* SpringBoot提供了三种Bean |
|||
* FilterRegistrationBean、 |
|||
* ServletRegistrationBean、 |
|||
* ServletListenerRegistrationBean |
|||
* 分别对应配置原生的Filter、Servlet、Listener |
|||
*/ |
|||
|
|||
/** |
|||
* 注册系统过滤器 |
|||
* @author wei |
|||
*/ |
|||
@Configuration |
|||
public class ServletConfig { |
|||
@Autowired |
|||
private DruidProps druidConfig; |
|||
|
|||
//配置Filter
|
|||
// @Bean
|
|||
// public FilterRegistrationBean companyUrlFilterRegister() {
|
|||
// FilterRegistrationBean registration = new FilterRegistrationBean();
|
|||
// //注入过滤器
|
|||
// registration.setFilter(new TestFilter1());
|
|||
// //拦截规则
|
|||
// registration.addUrlPatterns("/*");
|
|||
// //过滤器名称
|
|||
// registration.setName("testFilter1");
|
|||
// //过滤器顺序
|
|||
// registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
|
|||
// return registration;
|
|||
// }
|
|||
//
|
|||
// @Bean
|
|||
// public FilterRegistrationBean outLinkSecurityFilterRegister() {
|
|||
// FilterRegistrationBean registration = new FilterRegistrationBean();
|
|||
// //注入过滤器
|
|||
// registration.setFilter(new TestFilter2());
|
|||
// //拦截规则
|
|||
// registration.addUrlPatterns("/*");
|
|||
// //过滤器名称
|
|||
// registration.setName("testFilter2");
|
|||
// //过滤器顺序
|
|||
// registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE - 1);
|
|||
// return registration;
|
|||
// }
|
|||
|
|||
//配置Servlet
|
|||
// @Bean
|
|||
// public ServletRegistrationBean indexServletRegistration() {
|
|||
// ServletRegistrationBean registration = new ServletRegistrationBean(new IndexServlet());
|
|||
// registration.addUrlMappings("/hello");
|
|||
// return registration;
|
|||
// }
|
|||
|
|||
// //配置Listener
|
|||
// @Bean
|
|||
// public ServletListenerRegistrationBean servletListenerRegistrationBean(){
|
|||
// ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
|
|||
// servletListenerRegistrationBean.setListener(new IndexListener());
|
|||
// return servletListenerRegistrationBean;
|
|||
// }
|
|||
|
|||
|
|||
@Bean |
|||
public ServletRegistrationBean<StatViewServlet> druidServlet() { |
|||
ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<StatViewServlet>(); |
|||
reg.setServlet(new StatViewServlet()); |
|||
reg.setName(druidConfig.getFilterName()); |
|||
reg.addUrlMappings(druidConfig.getServletUrlMapping()); |
|||
//白名单:
|
|||
//reg.addInitParameter("allow","127.0.0.1");
|
|||
//IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
|
|||
//reg.addInitParameter("deny","192.168.1.73");
|
|||
reg.addInitParameter("loginUsername", druidConfig.getServletLoginUsername()); |
|||
reg.addInitParameter("loginPassword", druidConfig.getServletLoginPassword()); |
|||
reg.addInitParameter("logSlowSql", druidConfig.getServletLogSlowSql()); |
|||
//是否能够重置数据.
|
|||
reg.addInitParameter("resetEnable",druidConfig.getServletResetEnable()); |
|||
return reg; |
|||
} |
|||
|
|||
@Bean |
|||
public FilterRegistrationBean<WebStatFilter> druidFilter() { |
|||
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(); |
|||
filterRegistrationBean.setFilter(new WebStatFilter()); |
|||
filterRegistrationBean.setName(druidConfig.getFilterName()); |
|||
filterRegistrationBean.addUrlPatterns(druidConfig.getFilterUrlPattern()); |
|||
filterRegistrationBean.addInitParameter("exclusions", druidConfig.getFilterExclusions()); |
|||
filterRegistrationBean.addInitParameter("profileEnable", druidConfig.getFilterProfileEnable()); |
|||
return filterRegistrationBean; |
|||
} |
|||
} |
@ -0,0 +1,60 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import lombok.ToString; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.PropertySource; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Component |
|||
@PropertySource(value = {"classpath:setting-${spring.profiles.active}.properties"}) |
|||
@ConfigurationProperties(prefix="setting") |
|||
@Getter @Setter @ToString |
|||
public class SettingProps { |
|||
@Getter @Setter @ToString |
|||
public static class Snowflake{ |
|||
private String workerId; |
|||
private String dataCenterId; |
|||
} |
|||
@Getter @Setter @ToString |
|||
public static class MessageQueue{ |
|||
private String inName; |
|||
private String outName; |
|||
} |
|||
@Getter @Setter @ToString |
|||
public static class Server{ |
|||
private String name; |
|||
} |
|||
@Getter @Setter @ToString |
|||
public static class KeepAlive{ |
|||
private boolean enable; |
|||
private Integer maxIdleSeconds; |
|||
} |
|||
private Snowflake snowflake; |
|||
private MessageQueue mq; |
|||
private Server server; |
|||
private KeepAlive keepAlive; |
|||
|
|||
@Value("${setting.netty.ws.port}") |
|||
private Short nettyWsPort; |
|||
@Value("${setting.netty.ws.uri}") |
|||
private String nettyWsUri; |
|||
@Value("${setting.netty.ws.type}") |
|||
private String nettyWsType; |
|||
|
|||
@Value("${setting.netty.tcptext.port}") |
|||
private String nettyTcpTextPort; |
|||
@Value("${setting.netty.tcptext.type}") |
|||
private String nettyTcpTextType; |
|||
|
|||
@Value("${setting.netty.tcphex.port}") |
|||
private String nettyTcpHexPort; |
|||
@Value("${setting.netty.tcphex.type}") |
|||
private String nettyTcpHexType; |
|||
} |
|||
|
@ -0,0 +1,136 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
|
|||
import cn.hutool.core.lang.Snowflake; |
|||
import cn.hutool.core.util.IdUtil; |
|||
import wiki.tall.ccmq.common.controller.interceptor.TokenInterceptor; |
|||
import com.fasterxml.jackson.databind.DeserializationFeature; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import com.fasterxml.jackson.databind.module.SimpleModule; |
|||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.http.converter.HttpMessageConverter; |
|||
import org.springframework.http.converter.StringHttpMessageConverter; |
|||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; |
|||
import org.springframework.web.servlet.config.annotation.*; |
|||
|
|||
import javax.sql.DataSource; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Configuration |
|||
public class SpringConfig implements WebMvcConfigurer { |
|||
@Autowired |
|||
private DruidProps druidPropsUtil; |
|||
@Autowired |
|||
private SettingProps settingProps; |
|||
|
|||
/** |
|||
* 配置Converter |
|||
* @return |
|||
*/ |
|||
@Bean |
|||
public HttpMessageConverter<String> responseStringConverter() { |
|||
return new StringHttpMessageConverter(StandardCharsets.UTF_8); |
|||
} |
|||
|
|||
@Bean |
|||
public HttpMessageConverter<Object> responseJsonConverter(){ |
|||
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); |
|||
List<MediaType> mediaTypeList = new ArrayList<>(); |
|||
mediaTypeList.add(MediaType.TEXT_HTML); |
|||
mediaTypeList.add(MediaType.APPLICATION_JSON_UTF8); |
|||
converter.setSupportedMediaTypes(mediaTypeList); |
|||
|
|||
ObjectMapper objectMapper = new ObjectMapper(); |
|||
SimpleModule simpleModule = new SimpleModule(); |
|||
simpleModule.addSerializer(Long.class, ToStringSerializer.instance); |
|||
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); |
|||
objectMapper.registerModule(simpleModule); |
|||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
|||
converter.setObjectMapper(objectMapper); |
|||
|
|||
return converter; |
|||
} |
|||
|
|||
@Override |
|||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { |
|||
converters.add(responseStringConverter()); |
|||
converters.add(responseJsonConverter()); |
|||
} |
|||
|
|||
@Override |
|||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { |
|||
configurer.favorPathExtension(false); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 配置视图解析器 SpringBoot建议使用Thymeleaf代替jsp,动态页面默认路径:resources/template,静态页面默认路径: resources/static |
|||
* @return |
|||
*/ |
|||
// @Bean
|
|||
// public ViewResolver getViewResolver() {
|
|||
// InternalResourceViewResolver resolver = new InternalResourceViewResolver();
|
|||
// resolver.setPrefix("/WEB-INF/views/");
|
|||
// resolver.setSuffix(".jsp");
|
|||
// return resolver;
|
|||
// }
|
|||
// @Override
|
|||
// public void configureDefaultServletHandling(
|
|||
// DefaultServletHandlerConfigurer configurer) {
|
|||
// configurer.enable();
|
|||
// }
|
|||
|
|||
|
|||
/** |
|||
* 配置静态资源 |
|||
* @param registry |
|||
*/ |
|||
@Override |
|||
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|||
registry.addResourceHandler("swagger-ui.html") |
|||
.addResourceLocations("classpath:/META-INF/resources/"); |
|||
registry.addResourceHandler("/webjars/**") |
|||
.addResourceLocations("classpath:/META-INF/resources/webjars/"); |
|||
// registry.addResourceHandler("/uploads/**")
|
|||
// .addResourceLocations("file:///home/umg/uploads/");
|
|||
} |
|||
|
|||
/** |
|||
* 配置拦截器 |
|||
* @param registry |
|||
* @Descripton addPathPatterns 用于添加拦截规则、excludePathPatterns 用于排除拦截 |
|||
*/ |
|||
@Override |
|||
public void addInterceptors(InterceptorRegistry registry) { |
|||
registry.addInterceptor(tokenInterceptor()) |
|||
.addPathPatterns("/message/**"); |
|||
} |
|||
|
|||
@Bean |
|||
public TokenInterceptor tokenInterceptor(){ |
|||
return new TokenInterceptor(); |
|||
} |
|||
|
|||
/** |
|||
* 配置数据源(单数据源) |
|||
*/ |
|||
@Bean |
|||
public DataSource dataSource(){ |
|||
return druidPropsUtil.createDruidDataSource(); |
|||
} |
|||
|
|||
@Bean |
|||
public Snowflake snowflake(){ |
|||
return IdUtil.createSnowflake(Long.parseLong(settingProps.getSnowflake().getWorkerId()), |
|||
Long.parseLong(settingProps.getSnowflake().getDataCenterId())); |
|||
} |
|||
} |
@ -0,0 +1,64 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import wiki.tall.ccmq.common.util.WebConstant; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import springfox.documentation.builders.ParameterBuilder; |
|||
import springfox.documentation.builders.RequestHandlerSelectors; |
|||
import springfox.documentation.schema.ModelRef; |
|||
import springfox.documentation.service.ApiInfo; |
|||
import springfox.documentation.service.Contact; |
|||
import springfox.documentation.service.Parameter; |
|||
import springfox.documentation.spi.DocumentationType; |
|||
import springfox.documentation.spring.web.plugins.Docket; |
|||
import springfox.documentation.swagger2.annotations.EnableSwagger2; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Configuration |
|||
@EnableSwagger2 |
|||
public class SwaggerConfigure { |
|||
@Bean |
|||
public Docket customDocket() { |
|||
return new Docket(DocumentationType.SWAGGER_2) |
|||
.apiInfo(apiInfo()).select() |
|||
.apis(RequestHandlerSelectors |
|||
.basePackage("com.ccsens.ccmq")) |
|||
.build() |
|||
.globalOperationParameters(setHeaderToken()); |
|||
} |
|||
|
|||
private ApiInfo apiInfo() { |
|||
Contact contact = new Contact("__zHangSan", "", "weizezhao@gamil.com"); |
|||
return new ApiInfo( |
|||
//大标题 title
|
|||
"Anyring Core v3.0 Plugin Version.", |
|||
//小标题
|
|||
"", |
|||
//版本
|
|||
"1.0.0", |
|||
//termsOfServiceUrl
|
|||
"", |
|||
//作者
|
|||
contact.getName(), |
|||
//链接显示文字
|
|||
"", |
|||
//网站链接
|
|||
"www.ccsens.com" |
|||
); |
|||
} |
|||
|
|||
private List<Parameter> setHeaderToken() { |
|||
ParameterBuilder tokenPar = new ParameterBuilder(); |
|||
List<Parameter> pars = new ArrayList<>(); |
|||
tokenPar.name(WebConstant.HEADER_KEY_TOKEN).description("token") |
|||
.defaultValue(WebConstant.HEADER_KEY_TOKEN_PREFIX) |
|||
.modelRef(new ModelRef("string")).parameterType("header").required(false).build(); |
|||
pars.add(tokenPar.build()); |
|||
return pars; |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
package wiki.tall.ccmq.common.config; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
|||
|
|||
import java.util.concurrent.Executor; |
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
@Configuration |
|||
public class TaskExecutorConfig { |
|||
@Bean(name = "cc-msg-executor") |
|||
public Executor taskExecutor() { |
|||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); |
|||
executor.setCorePoolSize(10); |
|||
executor.setMaxPoolSize(10); |
|||
executor.setQueueCapacity(200); |
|||
|
|||
executor.setKeepAliveSeconds(60); |
|||
executor.setThreadNamePrefix("cc-msg-executor"); |
|||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); |
|||
executor.setWaitForTasksToCompleteOnShutdown(true); |
|||
executor.setAwaitTerminationSeconds(60); |
|||
return executor; |
|||
} |
|||
} |
@ -0,0 +1,75 @@ |
|||
package wiki.tall.ccmq.common.controller.interceptor; |
|||
|
|||
import io.jsonwebtoken.Claims; |
|||
import io.jsonwebtoken.ExpiredJwtException; |
|||
import io.jsonwebtoken.SignatureException; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
import org.springframework.web.servlet.ModelAndView; |
|||
import wiki.tall.ccmq.common.util.*; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
public class TokenInterceptor implements HandlerInterceptor { |
|||
@Override |
|||
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { |
|||
System.out.println("TokenInterceptor-->preHandle"); |
|||
// 验证token是否存在
|
|||
final String authHeader = httpServletRequest.getHeader(WebConstant.HEADER_KEY_TOKEN); |
|||
if (authHeader == null || !authHeader.startsWith(WebConstant.HEADER_KEY_TOKEN_PREFIX)) { |
|||
HttpServletUtil.responseJson(httpServletResponse, |
|||
JacksonUtil.beanToJson(JsonResponse.newInstance().tokenNotFound())); |
|||
return false; |
|||
} |
|||
final String token = authHeader.substring(WebConstant.HEADER_KEY_TOKEN_PREFIX.length()); |
|||
|
|||
//验证token是否有效
|
|||
Claims claims = null; |
|||
try { |
|||
claims = JwtUtil.parseJWT(token, WebConstant.JWT_ACCESS_TOKEN_SECERT); |
|||
}catch(SignatureException e){ |
|||
HttpServletUtil.responseJson(httpServletResponse, |
|||
JacksonUtil.beanToJson(JsonResponse.newInstance().tokenSignatureFail(e.getMessage()))); |
|||
return false; |
|||
}catch(ExpiredJwtException e){ |
|||
HttpServletUtil.responseJson(httpServletResponse, |
|||
JacksonUtil.beanToJson(JsonResponse.newInstance().tokenExpire(e.getMessage()))); |
|||
return false; |
|||
}catch(Exception e){ |
|||
HttpServletUtil.responseJson(httpServletResponse, |
|||
JacksonUtil.beanToJson(JsonResponse.newInstance().tokenFailed(e.getMessage()))); |
|||
return false; |
|||
} |
|||
|
|||
//验证用户存根
|
|||
// if(userService.tokenNotExistInCache(Long.valueOf(claims.getSubject()))){
|
|||
// HttpServletUtil.responseJson(httpServletResponse,
|
|||
// JacksonUtil.beanToJson(JsonResponse.newInstance().tokenStubNotFound()));
|
|||
// return false;
|
|||
// }
|
|||
|
|||
//验证用户是否禁用
|
|||
// User user = userService.getUserByAuthId(Long.valueOf(claims.getSubject()));
|
|||
// if(user.getRecStatus() == WebConstant.REC_STATUS.Disabled.value){
|
|||
// HttpServletUtil.responseJson(httpServletResponse,
|
|||
// JacksonUtil.beanToJson(JsonResponse.newInstance().userDisabled()));
|
|||
// return false;
|
|||
// }
|
|||
|
|||
httpServletRequest.setAttribute(WebConstant.REQUEST_KEY_CLAIMS,claims); |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { |
|||
|
|||
} |
|||
} |
@ -0,0 +1,106 @@ |
|||
package wiki.tall.ccmq.common.controller.web; |
|||
|
|||
import wiki.tall.ccmq.common.util.JsonResponse; |
|||
import org.springframework.stereotype.Controller; |
|||
import org.springframework.ui.Model; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.ResponseBody; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@Controller |
|||
@RequestMapping("/learn") |
|||
public class LearnController { |
|||
|
|||
/** |
|||
* url forward (转发) |
|||
* @param request |
|||
* @param response |
|||
* @param model |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@RequestMapping(value = "/") |
|||
public String root(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { |
|||
//forward:/index 中间不能有空格,/开头代表绝对路径,否则为相对路径
|
|||
// return "forward:/index";
|
|||
return "forward:index"; |
|||
} |
|||
|
|||
/** |
|||
* url redirect(重定向) |
|||
* @param request 请求 |
|||
* @param response 响应 |
|||
* @param model 模型 |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@RequestMapping(value = "/home") |
|||
public String home(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { |
|||
//redirect:/index 中间不能有空格,/开头代表绝对路径,否则为相对路径
|
|||
// return "redirect:/index";
|
|||
return "redirect:index"; |
|||
} |
|||
|
|||
/** |
|||
* 使用模板渲染页面 |
|||
* @param request 请求 |
|||
* @param response 响应 |
|||
* @param model 模型 |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@RequestMapping(value = "/index") |
|||
public String index(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { |
|||
model.addAttribute("welcome","Welcome to tall-message."); |
|||
return "index"; |
|||
} |
|||
|
|||
/** |
|||
* Restful Json 请求 |
|||
* @param request |
|||
* @param response |
|||
* @param model |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@RequestMapping(value = "/json",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"}) |
|||
@ResponseBody |
|||
public JsonResponse api(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { |
|||
return JsonResponse.newInstance().ok(); |
|||
} |
|||
|
|||
/** |
|||
* Restful Xml 请求 |
|||
* @param request |
|||
* @param response |
|||
* @param model |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@RequestMapping(value = "/xml",method = RequestMethod.GET,produces = {"application/xml;charset=UTF-8"}) |
|||
@ResponseBody |
|||
public JsonResponse xml(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { |
|||
return JsonResponse.newInstance().ok(); |
|||
} |
|||
|
|||
/** |
|||
* 字符串请求 |
|||
* @param request |
|||
* @param response |
|||
* @param model |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@RequestMapping(value = "/text",method = RequestMethod.GET,produces = {"text/plain;charset=UTF-8"}) |
|||
@ResponseBody |
|||
public String text(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception { |
|||
model.addAttribute("welcome","Welcome to tall-message."); |
|||
return "welcome"; |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
package wiki.tall.ccmq.common.controller.web; |
|||
|
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
/** |
|||
* @author wei |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("/message") |
|||
public class MessageController { |
|||
|
|||
// @RequestMapping(value = "/nodeMsg",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
|
|||
// public JsonResponse getNodeMessage(HttpServletRequest request,Long taskId) throws Exception {
|
|||
// Long currentUserId = Long.valueOf(((Claims) request.getAttribute(WebConstant.REQUEST_KEY_CLAIMS)).getSubject());
|
|||
// List<BaseMessageDto> messages = messageService.getNodeMessages(currentUserId,taskId);
|
|||
// return JsonResponse.newInstance().ok(messages);
|
|||
// }
|
|||
//
|
|||
// @RequestMapping(value = "/userMsg",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
|
|||
// public JsonResponse getUserMessage(HttpServletRequest request,Long projectId,Integer offset,Integer limit) throws Exception {
|
|||
// Long currentUserId = Long.valueOf(((Claims) request.getAttribute(WebConstant.REQUEST_KEY_CLAIMS)).getSubject());
|
|||
// List<BaseMessageDto> messages = messageService.getUserMessages(currentUserId,projectId,offset,limit);
|
|||
// return JsonResponse.newInstance().ok(messages);
|
|||
// }
|
|||
} |
@ -0,0 +1,17 @@ |
|||
package wiki.tall.ccmq.common.exception; |
|||
|
|||
import lombok.Getter; |
|||
|
|||
@Getter |
|||
public class BaseException extends RuntimeException { |
|||
private Integer code; |
|||
public BaseException(String message){ |
|||
super(message); |
|||
this.code = -1; |
|||
} |
|||
|
|||
public BaseException(Integer code,String message){ |
|||
super(message); |
|||
this.code = code; |
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import cn.hutool.core.bean.BeanUtil; |
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
public class BeanWrapperUtil { |
|||
|
|||
public static <V,T> T warpVoToObj(V userVo,Class<T> tClazz){ |
|||
if(userVo == null) |
|||
return null; |
|||
T user = null; |
|||
try { |
|||
user = tClazz.newInstance(); |
|||
BeanUtil.copyProperties(userVo,user); |
|||
} catch (InstantiationException e) { |
|||
e.printStackTrace(); |
|||
} catch (IllegalAccessException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return user; |
|||
} |
|||
|
|||
public static <V,T> V warpObjToVo(T user,Class<V> vClass){ |
|||
if(user == null) |
|||
return null; |
|||
V userVo = null; |
|||
try { |
|||
userVo = vClass.newInstance(); |
|||
} catch (InstantiationException e) { |
|||
e.printStackTrace(); |
|||
} catch (IllegalAccessException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
BeanUtil.copyProperties(user,userVo); |
|||
return userVo; |
|||
} |
|||
|
|||
public static <V,T> List<V> warpObjToVo(List<T> userList,Class<V> vClass){ |
|||
if(CollectionUtil.isEmpty(userList)) |
|||
return null; |
|||
List<V> userVoList = new ArrayList<>(); |
|||
for(T user: userList) { |
|||
userVoList.add(warpObjToVo(user,vClass)); |
|||
} |
|||
return userVoList; |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
public class CRCUtil { |
|||
public static void crc16(byte crc[], byte data[], int index, int len) { |
|||
int wcrc = 0xFFFF;// 预置16位crc寄存器,初值全部为1
|
|||
int temp;// 定义中间变量
|
|||
int i = 0, j = 0, k = index;// 定义计数
|
|||
|
|||
for (i = 0; i < len; i++)// 循环计算每个数据
|
|||
{ |
|||
temp = (data[k] & 0xFF);// 将八位数据与crc寄存器亦或
|
|||
k++;// 指针地址增加,指向下个数据
|
|||
wcrc ^= temp;// 将数据存入crc寄存器
|
|||
for (j = 0; j < 8; j++)// 循环计算数据的
|
|||
{ |
|||
if ((wcrc & 0x0001) == 1)// 判断右移出的是不是1,如果是1则与多项式进行异或。
|
|||
{ |
|||
wcrc >>= 1;// 先将数据右移一位
|
|||
wcrc ^= 0XA001;// 与上面的多项式进行异或
|
|||
} else// 如果不是1,则直接移出
|
|||
{ |
|||
wcrc >>= 1;// 直接移出
|
|||
} |
|||
} |
|||
} |
|||
temp = wcrc;// crc的值
|
|||
crc[0] = (byte)(wcrc & 0xFF); // crc的低八位
|
|||
crc[1] = (byte)((wcrc >> 8) & 0xFF); // crc的高八位
|
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.ccsens.ccmq.lowlevel.message.common.InMessage; |
|||
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author __zHangSan |
|||
*/ |
|||
public class CcMessageUtil { |
|||
private static final char TYPE_TO_JOIN_CHAR = '_'; |
|||
|
|||
/** |
|||
* tos命名矫正后的集合 |
|||
* 为了避免 id重复(queue的id和user的id),将to的id矫正为 domain_id |
|||
* @param inMessage 消息 |
|||
* @return tos命名矫正后的集合 |
|||
*/ |
|||
public static List<Object> getNameManglingToList(InMessage inMessage) { |
|||
List<Object> list = new ArrayList<Object>(inMessage.getTos().size()); |
|||
for(String to : inMessage.getTos()){ |
|||
list.add(StrUtil.format("{}{}{}",inMessage.getToDomain(),TYPE_TO_JOIN_CHAR,to)); |
|||
} |
|||
return list; |
|||
} |
|||
|
|||
public static String[] splitTypeAndUserId(String typeAndUserId) { |
|||
String []strings = new String[2]; |
|||
int splitCharPos = typeAndUserId.indexOf(TYPE_TO_JOIN_CHAR); |
|||
strings[0] = typeAndUserId.substring(0,splitCharPos); |
|||
strings[1] = typeAndUserId.substring(splitCharPos+1,typeAndUserId.length()); |
|||
return strings; |
|||
} |
|||
|
|||
public static String getDomainTypeAndUserIdString(MessageConstant.DomainType domainType,String to){ |
|||
return StrUtil.format("{}{}{}",domainType.name(),TYPE_TO_JOIN_CHAR,to); |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import java.util.TimeZone; |
|||
|
|||
public class DateUtil extends cn.hutool.core.date.DateUtil { |
|||
public static Long dateBeginSeconds(Long seconds){ |
|||
if(seconds == null) |
|||
seconds = currentSeconds(); |
|||
return (seconds - (seconds + TimeZone.getDefault().getRawOffset()/1000 )% (3600*24)); |
|||
} |
|||
|
|||
public static Long dateEndSeconds(Long seconds){ |
|||
return dateBeginSeconds(seconds) + (3600 * 24 - 1); |
|||
} |
|||
} |
@ -0,0 +1,285 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
|
|||
import javax.servlet.http.HttpSession; |
|||
import java.lang.reflect.Field; |
|||
import java.lang.reflect.Method; |
|||
import java.lang.reflect.ParameterizedType; |
|||
import java.lang.reflect.Type; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Random; |
|||
|
|||
/** |
|||
* 泛型工具类 |
|||
* |
|||
* @author <a href="http://www.blogjava.net/lishunli/" |
|||
* target="_jeecg">ShunLi</a> |
|||
* @notes Created on 2010-1-21<br> |
|||
* Revision of last commit:$Revision: 1.1 $<br> |
|||
* Author of last commit:$Author: ghp $<br> |
|||
* Date of last commit:$Date: 2010-01-25 16:48:17 +0800 (周一, 25 一月 2010) |
|||
* $<br> |
|||
* <p> |
|||
*/ |
|||
public class GenericsUtils { |
|||
/** |
|||
* 通过反射,获得指定类的父类的泛型参数的实际类型. 如BuyerServiceBean extends DaoSupport<Buyer> |
|||
* |
|||
* @param clazz |
|||
* clazz 需要反射的类,该类必须继承范型父类 |
|||
* @param index |
|||
* 泛型参数所在索引,从0开始. |
|||
* @return 范型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回 |
|||
* <code>Object.class</code> |
|||
*/ |
|||
public static Class getSuperClassGenricType(Class clazz, int index) { |
|||
Type genType = clazz.getGenericSuperclass();// 得到泛型父类
|
|||
// 如果没有实现ParameterizedType接口,即不支持泛型,直接返回Object.class
|
|||
if (!(genType instanceof ParameterizedType)) { |
|||
Type genTypeArray[] = clazz.getGenericInterfaces(); |
|||
boolean isParameterizedType = false; |
|||
for(Type tmpGenType : genTypeArray){ |
|||
if(tmpGenType instanceof ParameterizedType) { |
|||
genType = tmpGenType; |
|||
isParameterizedType = true; |
|||
break; |
|||
} |
|||
} |
|||
if(isParameterizedType == false) { |
|||
return Object.class; |
|||
} |
|||
} |
|||
// 返回表示此类型实际类型参数的Type对象的数组,数组里放的都是对应类型的Class, 如BuyerServiceBean extends
|
|||
// DaoSupport<Buyer,Contact>就返回Buyer和Contact类型
|
|||
Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); |
|||
if (index >= params.length || index < 0) { |
|||
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数")); |
|||
} |
|||
if (!(params[index] instanceof Class)) { |
|||
return Object.class; |
|||
} |
|||
return (Class) params[index]; |
|||
} |
|||
|
|||
/** |
|||
* 通过反射,获得指定类的父类的第一个泛型参数的实际类型. 如BuyerServiceBean extends DaoSupport<Buyer> |
|||
* |
|||
* @param clazz |
|||
* clazz 需要反射的类,该类必须继承泛型父类 |
|||
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回 |
|||
* <code>Object.class</code> |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static Class getSuperClassGenricType(Class clazz) { |
|||
return getSuperClassGenricType(clazz, 0); |
|||
} |
|||
|
|||
/** |
|||
* 通过反射,获得方法返回值泛型参数的实际类型. 如: public Map<String, Buyer> getNames(){} |
|||
* |
|||
* @param method 方法 |
|||
* @param index 泛型参数所在索引,从0开始. |
|||
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回 |
|||
* <code>Object.class</code> |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static Class getMethodGenericReturnType(Method method, int index) { |
|||
Type returnType = method.getGenericReturnType(); |
|||
if (returnType instanceof ParameterizedType) { |
|||
ParameterizedType type = (ParameterizedType) returnType; |
|||
Type[] typeArguments = type.getActualTypeArguments(); |
|||
if (index >= typeArguments.length || index < 0) { |
|||
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数")); |
|||
} |
|||
return (Class) typeArguments[index]; |
|||
} |
|||
return Object.class; |
|||
} |
|||
|
|||
/** |
|||
* 通过反射,获得方法返回值第一个泛型参数的实际类型. 如: public Map<String, Buyer> getNames(){} |
|||
* |
|||
* @param method 方法 |
|||
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回 |
|||
* <code>Object.class</code> |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static Class getMethodGenericReturnType(Method method) { |
|||
return getMethodGenericReturnType(method, 0); |
|||
} |
|||
|
|||
/** |
|||
* 通过反射,获得方法输入参数第index个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String, |
|||
* Buyer> maps, List<String> names){} |
|||
* |
|||
* @param method 方法 |
|||
* @param index 第几个输入参数 |
|||
* @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回空集合 |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static List<Class> getMethodGenericParameterTypes(Method method, int index) { |
|||
List<Class> results = new ArrayList<Class>(); |
|||
Type[] genericParameterTypes = method.getGenericParameterTypes(); |
|||
if (index >= genericParameterTypes.length || index < 0) { |
|||
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数")); |
|||
} |
|||
Type genericParameterType = genericParameterTypes[index]; |
|||
if (genericParameterType instanceof ParameterizedType) { |
|||
ParameterizedType aType = (ParameterizedType) genericParameterType; |
|||
Type[] parameterArgTypes = aType.getActualTypeArguments(); |
|||
for (Type parameterArgType : parameterArgTypes) { |
|||
Class parameterArgClass = (Class) parameterArgType; |
|||
results.add(parameterArgClass); |
|||
} |
|||
return results; |
|||
} |
|||
return results; |
|||
} |
|||
|
|||
/** |
|||
* 通过反射,获得方法输入参数第一个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String, Buyer> |
|||
* maps, List<String> names){} |
|||
* |
|||
* @param method 方法 |
|||
* @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回空集合 |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static List<Class> getMethodGenericParameterTypes(Method method) { |
|||
return getMethodGenericParameterTypes(method, 0); |
|||
} |
|||
|
|||
/** |
|||
* 通过反射,获得Field泛型参数的实际类型. 如: public Map<String, Buyer> names; |
|||
* |
|||
* @param field 字段 |
|||
* @param index 泛型参数所在索引,从0开始. |
|||
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回 |
|||
* <code>Object.class</code> |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static Class getFieldGenericType(Field field, int index) { |
|||
Type genericFieldType = field.getGenericType(); |
|||
|
|||
if (genericFieldType instanceof ParameterizedType) { |
|||
ParameterizedType aType = (ParameterizedType) genericFieldType; |
|||
Type[] fieldArgTypes = aType.getActualTypeArguments(); |
|||
if (index >= fieldArgTypes.length || index < 0) { |
|||
throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数")); |
|||
} |
|||
return (Class) fieldArgTypes[index]; |
|||
} |
|||
return Object.class; |
|||
} |
|||
|
|||
/** |
|||
* 通过反射,获得Field泛型参数的实际类型. 如: public Map<String, Buyer> names; |
|||
* |
|||
* @param field 字段 |
|||
* @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口,即不支持泛型,所以直接返回 |
|||
* <code>Object.class</code> |
|||
*/ |
|||
@SuppressWarnings("unchecked") |
|||
public static Class getFieldGenericType(Field field) { |
|||
return getFieldGenericType(field, 0); |
|||
} |
|||
/** |
|||
* 根据实体得到实体的所有属性 |
|||
* @param objClass |
|||
* @return |
|||
* @throws ClassNotFoundException |
|||
*/ |
|||
public static String[] getColumnNames(String objClass) throws ClassNotFoundException { |
|||
String[] wageStrArray = null; |
|||
if (objClass != null) { |
|||
Class class1 = Class.forName(objClass); |
|||
Field[] field = class1.getDeclaredFields();// 这里便是获得实体Bean中所有属性的方法
|
|||
StringBuffer sb = new StringBuffer(); |
|||
for (int i = 0; i < field.length; i++) {// 这里不多说了
|
|||
|
|||
sb.append(field[i].getName()); |
|||
|
|||
// 这是分割符 是为了去掉最后那个逗号
|
|||
|
|||
// 比如 如果不去最后那个逗号 最后打印出来的结果是 "id,name,"
|
|||
|
|||
// 去了以后打印出来的是 "id,name"
|
|||
if (i < field.length - 1) { |
|||
sb.append(","); |
|||
|
|||
} |
|||
} |
|||
|
|||
// split(",");这是根据逗号来切割字符串使字符串变成一个数组
|
|||
|
|||
wageStrArray = sb.toString().split(","); |
|||
return wageStrArray; |
|||
} else { |
|||
return wageStrArray; |
|||
} |
|||
} |
|||
public static Object[] field2Value(Field[] f, Object o) throws Exception { |
|||
Object[] value = new Object[f.length]; |
|||
for (int i = 0; i < f.length; i++) { |
|||
value[i] = f[i].get(o); |
|||
} |
|||
return value; |
|||
} |
|||
/** |
|||
* returns the current http session object |
|||
* |
|||
* @return session |
|||
*/ |
|||
public HttpSession getSession() { |
|||
HttpSession session=null; |
|||
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); |
|||
HttpSession contextSess = attr == null ? session : attr.getRequest().getSession(true); |
|||
|
|||
return contextSess; |
|||
} |
|||
/** |
|||
* 得到实体类 |
|||
* @param objClass 实体类包含包名 |
|||
* @return |
|||
*/ |
|||
public static Class getEntityClass(String objClass){ |
|||
Class entityClass = null; |
|||
try { |
|||
entityClass = Class.forName(objClass); |
|||
} catch (ClassNotFoundException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return entityClass; |
|||
} |
|||
|
|||
/** |
|||
* 定义字符集 |
|||
* @param |
|||
* @return |
|||
*/ |
|||
private static char[] chars = { '0', '1', '2', '3', '4', '5', '6', '7', |
|||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', |
|||
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', |
|||
'y', 'z', 'A', 'B','C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', |
|||
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', |
|||
'Z'}; //72个字符集
|
|||
|
|||
/** |
|||
* |
|||
* @param passLength |
|||
* 随机密码长度 |
|||
* @return 随机密码数组 |
|||
*/ |
|||
public static String getPasswords(int passLength) { |
|||
String passwords = "";// 新建一个长度为指定需要密码个数的字符串数组
|
|||
Random random = new Random(); |
|||
StringBuilder password = new StringBuilder("");// 保存生成密码的变量
|
|||
for (int m = 1; m <= passLength; m++) {// 内循环 从1开始到密码长度 正式开始生成密码
|
|||
password.append(chars[random.nextInt(62)]);// 为密码变量随机增加上面字符中的一个
|
|||
} |
|||
passwords = password.toString();// 将生成出来的密码赋值给密码数组
|
|||
return passwords; |
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
import java.io.PrintWriter; |
|||
|
|||
public class HttpServletUtil { |
|||
public static void responseJson(HttpServletResponse response, String json) throws IOException { |
|||
response.setCharacterEncoding("UTF-8"); |
|||
response.setContentType("application/json;charset=utf-8"); |
|||
PrintWriter out = response.getWriter(); |
|||
out.append(json); |
|||
out.close(); |
|||
} |
|||
|
|||
/** |
|||
* 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址, |
|||
* |
|||
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢? |
|||
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。 |
|||
* |
|||
* 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130, |
|||
* 192.168.1.100 |
|||
* |
|||
* 用户真实IP为: 192.168.1.110 |
|||
* |
|||
* @param request |
|||
* @return |
|||
*/ |
|||
public static String getIpAddress(HttpServletRequest request) { |
|||
String ip = request.getHeader("x-forwarded-for"); |
|||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getHeader("Proxy-Client-IP"); |
|||
} |
|||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getHeader("WL-Proxy-Client-IP"); |
|||
} |
|||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getHeader("HTTP_CLIENT_IP"); |
|||
} |
|||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getHeader("HTTP_X_FORWARDED_FOR"); |
|||
} |
|||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
|||
ip = request.getRemoteAddr(); |
|||
} |
|||
return ip; |
|||
} |
|||
|
|||
public static String getSiteBasePath(HttpServletRequest request){ |
|||
return request.getScheme() + "://" |
|||
+ request.getServerName() + ":" + request.getServerPort(); |
|||
} |
|||
|
|||
public static String getSiteContextPath(HttpServletRequest request){ |
|||
return getSiteBasePath(request) + "/" + request.getContextPath(); |
|||
} |
|||
} |
@ -0,0 +1,133 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import org.springframework.http.client.SimpleClientHttpRequestFactory; |
|||
|
|||
import javax.net.ssl.*; |
|||
import java.io.IOException; |
|||
import java.net.HttpURLConnection; |
|||
import java.net.InetAddress; |
|||
import java.net.Socket; |
|||
import java.security.cert.X509Certificate; |
|||
|
|||
/** |
|||
* 声明:此代码摘录自https://blog.csdn.net/wltsysterm/article/details/80977455
|
|||
* 声明:关于Socket的相关知识,本人会在后面的闲暇时间进行学习整理,请持续关注博客更新 |
|||
* |
|||
* @author JustryDeng |
|||
* @DATE 2018年9月8日 下午4:34:02 |
|||
*/ |
|||
public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { |
|||
|
|||
@Override |
|||
protected void prepareConnection(HttpURLConnection connection, String httpMethod) { |
|||
try { |
|||
if (!(connection instanceof HttpsURLConnection)) { |
|||
throw new RuntimeException("An instance of HttpsURLConnection is expected"); |
|||
} |
|||
|
|||
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; |
|||
|
|||
TrustManager[] trustAllCerts = new TrustManager[]{ |
|||
new X509TrustManager() { |
|||
@Override |
|||
public java.security.cert.X509Certificate[] getAcceptedIssuers() { |
|||
return null; |
|||
} |
|||
@Override |
|||
public void checkClientTrusted(X509Certificate[] certs, String authType) { |
|||
} |
|||
@Override |
|||
public void checkServerTrusted(X509Certificate[] certs, String authType) { |
|||
} |
|||
|
|||
} |
|||
}; |
|||
SSLContext sslContext = SSLContext.getInstance("TLS"); |
|||
sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); |
|||
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); |
|||
|
|||
httpsConnection.setHostnameVerifier(new HostnameVerifier() { |
|||
@Override |
|||
public boolean verify(String s, SSLSession sslSession) { |
|||
return true; |
|||
} |
|||
}); |
|||
|
|||
super.prepareConnection(httpsConnection, httpMethod); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); |
|||
* see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
|
|||
*/ |
|||
// SSLSocketFactory用于创建 SSLSockets
|
|||
private static class MyCustomSSLSocketFactory extends SSLSocketFactory { |
|||
|
|||
private final SSLSocketFactory delegate; |
|||
|
|||
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { |
|||
this.delegate = delegate; |
|||
} |
|||
|
|||
// 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
|
|||
// 这些默认的服务的最低质量要求保密保护和服务器身份验证
|
|||
@Override |
|||
public String[] getDefaultCipherSuites() { |
|||
return delegate.getDefaultCipherSuites(); |
|||
} |
|||
|
|||
// 返回的密码套件可用于SSL连接启用的名字
|
|||
@Override |
|||
public String[] getSupportedCipherSuites() { |
|||
return delegate.getSupportedCipherSuites(); |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public Socket createSocket(final Socket socket, final String host, final int port, |
|||
final boolean autoClose) throws IOException { |
|||
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); |
|||
return overrideProtocol(underlyingSocket); |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public Socket createSocket(final String host, final int port) throws IOException { |
|||
final Socket underlyingSocket = delegate.createSocket(host, port); |
|||
return overrideProtocol(underlyingSocket); |
|||
} |
|||
|
|||
@Override |
|||
public Socket createSocket(final String host, final int port, final InetAddress localAddress, |
|||
final int localPort) throws |
|||
IOException { |
|||
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); |
|||
return overrideProtocol(underlyingSocket); |
|||
} |
|||
|
|||
@Override |
|||
public Socket createSocket(final InetAddress host, final int port) throws IOException { |
|||
final Socket underlyingSocket = delegate.createSocket(host, port); |
|||
return overrideProtocol(underlyingSocket); |
|||
} |
|||
|
|||
@Override |
|||
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, |
|||
final int localPort) throws |
|||
IOException { |
|||
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); |
|||
return overrideProtocol(underlyingSocket); |
|||
} |
|||
|
|||
private Socket overrideProtocol(final Socket socket) { |
|||
if (!(socket instanceof SSLSocket)) { |
|||
throw new RuntimeException("An instance of SSLSocket is expected"); |
|||
} |
|||
((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"}); |
|||
return socket; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,256 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import org.apache.http.HttpEntity; |
|||
import org.apache.http.client.methods.CloseableHttpResponse; |
|||
import org.apache.http.client.methods.HttpPost; |
|||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; |
|||
import org.apache.http.conn.ssl.SSLContexts; |
|||
import org.apache.http.conn.ssl.TrustSelfSignedStrategy; |
|||
import org.apache.http.entity.StringEntity; |
|||
import org.apache.http.impl.client.CloseableHttpClient; |
|||
import org.apache.http.impl.client.HttpClients; |
|||
import org.apache.http.util.EntityUtils; |
|||
|
|||
import javax.net.ssl.*; |
|||
import java.io.*; |
|||
import java.net.MalformedURLException; |
|||
import java.net.URL; |
|||
import java.net.URLConnection; |
|||
import java.security.KeyStore; |
|||
import java.security.cert.CertificateException; |
|||
import java.security.cert.X509Certificate; |
|||
|
|||
/** |
|||
* 类名: MyX509TrustManager </br> |
|||
* 描述:信任管理器 </br> |
|||
* 开发人员: souvc </br> |
|||
* 创建时间: 2015-11-27 </br> |
|||
* 发布版本:V1.0 </br> |
|||
*/ |
|||
class MyX509TrustManager implements X509TrustManager { |
|||
|
|||
// 检查客户端证书
|
|||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
|||
} |
|||
|
|||
// 检查服务器端证书
|
|||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
|||
} |
|||
|
|||
// 返回受信任的X509证书数组
|
|||
public X509Certificate[] getAcceptedIssuers() { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public class HttpsUtil { |
|||
/** |
|||
* 发送https请求 |
|||
* |
|||
* @param requestUrl 请求地址 |
|||
* @param requestMethod 请求方式(GET、POST) |
|||
* @param postStr 提交的数据 |
|||
* @return String(Json) |
|||
*/ |
|||
public static String httpsRequest(String requestUrl, String requestMethod, String postStr) throws Exception { |
|||
StringBuffer buffer = null; |
|||
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
|
|||
TrustManager[] tm = {new MyX509TrustManager()}; |
|||
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); |
|||
sslContext.init(null, tm, new java.security.SecureRandom()); |
|||
// 从上述SSLContext对象中得到SSLSocketFactory对象
|
|||
SSLSocketFactory ssf = sslContext.getSocketFactory(); |
|||
|
|||
URL url = new URL(requestUrl); |
|||
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); |
|||
conn.setSSLSocketFactory(ssf); |
|||
|
|||
conn.setDoOutput(true); |
|||
conn.setDoInput(true); |
|||
conn.setUseCaches(false); |
|||
// 设置请求方式(GET/POST)
|
|||
conn.setRequestMethod(requestMethod); |
|||
|
|||
// 当outputStr不为null时向输出流写数据
|
|||
if (null != postStr) { |
|||
OutputStream outputStream = conn.getOutputStream(); |
|||
// 注意编码格式
|
|||
outputStream.write(postStr.getBytes("UTF-8")); |
|||
outputStream.close(); |
|||
} |
|||
|
|||
// 从输入流读取返回内容
|
|||
InputStream inputStream = conn.getInputStream(); |
|||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); |
|||
BufferedReader bufferedReader = new BufferedReader(inputStreamReader); |
|||
String str = null; |
|||
buffer = new StringBuffer(); |
|||
while ((str = bufferedReader.readLine()) != null) { |
|||
buffer.append(str); |
|||
} |
|||
|
|||
// 释放资源
|
|||
bufferedReader.close(); |
|||
inputStreamReader.close(); |
|||
inputStream.close(); |
|||
inputStream = null; |
|||
conn.disconnect(); |
|||
return buffer == null ? null : buffer.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 发送https请求 |
|||
* |
|||
* @param requestUrl 请求地址 |
|||
* @param requestMethod 请求方式(GET、POST) |
|||
* @param postStr 提交的数据 |
|||
* @param path 将结果以二进制方式写入文件 |
|||
* @return void |
|||
*/ |
|||
public static void httpsRequest(String requestUrl, String requestMethod, String postStr,String path) |
|||
throws Exception { |
|||
StringBuffer buffer = null; |
|||
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
|
|||
TrustManager[] tm = {new MyX509TrustManager()}; |
|||
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); |
|||
sslContext.init(null, tm, new java.security.SecureRandom()); |
|||
// 从上述SSLContext对象中得到SSLSocketFactory对象
|
|||
SSLSocketFactory ssf = sslContext.getSocketFactory(); |
|||
|
|||
URL url = new URL(requestUrl); |
|||
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); |
|||
conn.setSSLSocketFactory(ssf); |
|||
|
|||
conn.setDoOutput(true); |
|||
conn.setDoInput(true); |
|||
conn.setUseCaches(false); |
|||
// 设置请求方式(GET/POST)
|
|||
conn.setRequestMethod(requestMethod); |
|||
|
|||
// 当outputStr不为null时向输出流写数据
|
|||
if (null != postStr) { |
|||
OutputStream outputStream = conn.getOutputStream(); |
|||
// 注意编码格式
|
|||
outputStream.write(postStr.getBytes("UTF-8")); |
|||
outputStream.close(); |
|||
} |
|||
|
|||
// 从输入流读取返回内容
|
|||
InputStream inputStream = conn.getInputStream(); |
|||
BufferedInputStream bis = new BufferedInputStream(inputStream); |
|||
OutputStream os = new FileOutputStream(new File(path)); |
|||
int len; |
|||
byte[] arr = new byte[1024]; |
|||
while ((len = bis.read(arr)) != -1) |
|||
{ |
|||
os.write(arr, 0, len); |
|||
os.flush(); |
|||
} |
|||
os.close(); |
|||
} |
|||
|
|||
/** |
|||
* 发送https请求 使用PKCS12类型证书 |
|||
* |
|||
* @param requestUrl 请求地址 |
|||
* @param requestMethod 请求方式(GET、POST) |
|||
* @param postStr 提交的数据 |
|||
* @return String(Json) |
|||
*/ |
|||
public static String httpsRequest(String requestUrl, String requestMethod, String postStr,String pKCS12Path,String pKCS12Pwd) throws Exception { |
|||
SSLContext sc = null; |
|||
FileInputStream instream = null; |
|||
KeyStore keyStore = null; |
|||
|
|||
keyStore = KeyStore.getInstance("PKCS12"); |
|||
instream = new FileInputStream(new File(pKCS12Path)); |
|||
keyStore.load(instream,pKCS12Pwd.toCharArray()); |
|||
instream.close(); |
|||
|
|||
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial( |
|||
keyStore, pKCS12Pwd.toCharArray()).build(); |
|||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( |
|||
sslcontext, |
|||
new String[] { "TLSv1" }, |
|||
null, |
|||
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER |
|||
); |
|||
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf) .build(); |
|||
HttpPost httpost = new HttpPost(requestUrl); |
|||
httpost.addHeader("Connection", "keep-alive"); |
|||
httpost.addHeader("Accept", "*/*"); |
|||
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); |
|||
httpost.addHeader("Host", "api.mch.weixin.qq.com"); |
|||
httpost.addHeader("X-Requested-With", "XMLHttpRequest"); |
|||
httpost.addHeader("Cache-Control", "max-age=0"); |
|||
httpost.addHeader("User-Agent", |
|||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); |
|||
httpost.setEntity(new StringEntity(postStr, "UTF-8")); |
|||
CloseableHttpResponse response = httpclient.execute(httpost); |
|||
HttpEntity entity = response.getEntity(); |
|||
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); |
|||
EntityUtils.consume(entity); |
|||
|
|||
return jsonStr; |
|||
} |
|||
|
|||
/** |
|||
* 设置信任自签名证书 |
|||
* |
|||
* @param keyStorePath 密钥库路径 |
|||
* @param keyStorepass 密钥库密码 |
|||
* @return |
|||
*/ |
|||
|
|||
public static SSLContext custom(String keyStorePath, String keyStorepass) { |
|||
SSLContext sc = null; |
|||
FileInputStream instream = null; |
|||
KeyStore trustStore = null; |
|||
try { |
|||
trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); |
|||
instream = new FileInputStream(new File(keyStorePath)); |
|||
trustStore.load(instream, keyStorepass.toCharArray()); |
|||
// 相信自己的CA和所有自签名的证书
|
|||
sc = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build(); |
|||
// 构造 javax.net.ssl.TrustManager 对象
|
|||
TrustManagerFactory tmf = |
|||
TrustManagerFactory.getInstance("SunX509", "SunJSSE"); |
|||
tmf.init(trustStore); |
|||
TrustManager tms [] = tmf.getTrustManagers(); |
|||
// 使用构造好的 TrustManager 访问相应的 https 站点
|
|||
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); |
|||
sslContext.init(null, tms, new java.security.SecureRandom()); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} finally { |
|||
try { |
|||
instream.close(); |
|||
} catch (IOException e) { |
|||
} |
|||
} |
|||
return sc; |
|||
} |
|||
|
|||
public static String sendGet(String url, String charset, int timeout) { |
|||
String result = ""; |
|||
try { |
|||
URL u = new URL(url); |
|||
try { |
|||
URLConnection conn = u.openConnection(); |
|||
conn.connect(); |
|||
conn.setConnectTimeout(timeout); |
|||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset)); |
|||
String line = ""; |
|||
while ((line = in.readLine()) != null) { |
|||
result = result + line; |
|||
} |
|||
in.close(); |
|||
} catch (IOException e) { |
|||
return result; |
|||
} |
|||
} catch (MalformedURLException e) { |
|||
return result; |
|||
} |
|||
return result; |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import cn.hutool.core.lang.Snowflake; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
public class IDGenerator { |
|||
@Autowired |
|||
private Snowflake snowflake; |
|||
|
|||
public Long nextId(){ |
|||
return snowflake.nextId(); |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import sun.font.FontDesignMetrics; |
|||
|
|||
import java.awt.*; |
|||
|
|||
public class ImgUtil { |
|||
public static int getStringWidth(String text,Font f){ |
|||
FontMetrics fm = FontDesignMetrics.getMetrics(f); |
|||
int w = fm.stringWidth(text); |
|||
return w; |
|||
} |
|||
} |
@ -0,0 +1,311 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.fasterxml.jackson.annotation.JsonInclude; |
|||
import com.fasterxml.jackson.core.JsonParser; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import com.fasterxml.jackson.databind.*; |
|||
import com.fasterxml.jackson.databind.module.SimpleModule; |
|||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
|||
import com.fasterxml.jackson.dataformat.xml.XmlMapper; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author wei |
|||
* @date 2019.11.25 |
|||
*/ |
|||
public class JacksonUtil { |
|||
private static ObjectMapper objectMapper = new ObjectMapper(); |
|||
private static XmlMapper xmlMapper = new XmlMapper(); |
|||
static { |
|||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
|||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); |
|||
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); |
|||
SimpleModule simpleModule = new SimpleModule(); |
|||
simpleModule.addSerializer(Long.class, ToStringSerializer.instance); |
|||
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); |
|||
objectMapper.registerModule(simpleModule); |
|||
|
|||
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
|||
xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); |
|||
} |
|||
|
|||
/** |
|||
* 获取泛型的Collection Type |
|||
* eg: collectionClass==List.class elementClasses == MessageBean.class 则返回 List<MessageBean>对应的java类型 |
|||
* @param collectionClass 泛型的Collection |
|||
* @param elementClasses 元素类 |
|||
* @return JavaType Java类型 |
|||
* @since 1.0 |
|||
*/ |
|||
private static JavaType getCollectionType(ObjectMapper mapper, Class<?> collectionClass, Class<?>... elementClasses) { |
|||
return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); |
|||
} |
|||
|
|||
/** |
|||
* Json And Bean Converter |
|||
* @param json 要转换的json |
|||
* @param clazz 目标类型类对象 |
|||
* @param isSet 是否集合类型 |
|||
* @param <T> 目标类型 |
|||
* @return 集合(如果,只有1个对象,则添加到集合中返回) |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static <T> List<T> jsonToBean(String json, Class<T> clazz, boolean isSet) throws IOException { |
|||
if (!isSet) { |
|||
return CollectionUtil.newArrayList(objectMapper.readValue(json, clazz)); |
|||
} |
|||
else { |
|||
JavaType javaType = getCollectionType(objectMapper,List.class,clazz); |
|||
return objectMapper.readValue(json, javaType); |
|||
} |
|||
} |
|||
|
|||
public static <T> T jsonToBean(String json, Class<T> clazz) throws IOException { |
|||
return objectMapper.readValue(json, clazz); |
|||
} |
|||
|
|||
public static <T> String beanToJson(T bean, boolean pretty) throws JsonProcessingException { |
|||
if (!pretty) { |
|||
return objectMapper.writeValueAsString(bean); |
|||
} |
|||
else { |
|||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean); |
|||
} |
|||
} |
|||
|
|||
public static <T> String beanToJson(T bean) throws JsonProcessingException { |
|||
return beanToJson(bean,false); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Json And Map Converter |
|||
* |
|||
* @param json 要转换的json |
|||
* eg: {"time":"1574602523","from":{"id":"1","name":"zs"},"to":[{"id":"1","name":"ls","avatar":"xx.jpg"}],"rule":{"offlineDiscard":1}} |
|||
* @return Map<String,Object> 如果value还是一个json对象,则也会被转化为map |
|||
* eg: entry1: "time"->String entry2: "from"--> Map<id:1,name:zs> entry3: "to"->List<Map> entry4: "rule"-->Map |
|||
* @throws IOException |
|||
*/ |
|||
public static Map<String,Object> jsonToMap(String json) throws IOException { |
|||
JavaType javaType = getCollectionType(objectMapper,Map.class,String.class,Object.class); |
|||
return objectMapper.readValue(json,javaType); |
|||
} |
|||
|
|||
public static String mapToJson(Map<String,Object> map,boolean pretty) throws JsonProcessingException { |
|||
if(!pretty){ |
|||
return objectMapper.writeValueAsString(map); |
|||
}else{ |
|||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(map); |
|||
} |
|||
} |
|||
|
|||
public static String mapToJson(Map<String,Object> map) throws JsonProcessingException { |
|||
return mapToJson(map,false); |
|||
} |
|||
|
|||
/** |
|||
* Map And Bean Converter |
|||
* @param map 要转换的集合 |
|||
* @param clazz 目标类型类对象 |
|||
* @param <T> 目标类型 |
|||
* @return JavaBean |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static <T> T mapToBean(Map<String,Object> map,Class<T> clazz) throws IOException { |
|||
return jsonToBean(mapToJson(map),clazz); |
|||
} |
|||
|
|||
public static <T> Map<String,Object> beanToMap(T bean) throws IOException { |
|||
return jsonToMap(beanToJson(bean)); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Xml And Bean Converter |
|||
* Javabean可以使用Jackson xml标注: @JacksonXmlRootElement(localName = "Class") 指定root元素名称 |
|||
* 其他标注:@JacksonXmlElementWrapper(localName = "Students")指定List类型包装元素名称比如:学生列表 @JacksonXmlProperty(localName = "Stu") 指定字段元素名称比如:学生 |
|||
* @param bean 要转换的JavaBean |
|||
* @param root xml root元素名称,建议使用@JacksonXmlElementWrapper表示 |
|||
* @param pretty 是否启用pretty模式 |
|||
* @param <T> Javabean类型 |
|||
* @return xml字符串 |
|||
* @throws JsonProcessingException Json处理异常 |
|||
*/ |
|||
public static <T> String beanToXml(T bean,String root,boolean pretty) throws JsonProcessingException { |
|||
if(!pretty) { |
|||
if(StrUtil.isEmpty(root)) { |
|||
return xmlMapper.writeValueAsString(bean); |
|||
}else{ |
|||
return xmlMapper.writer().with(SerializationFeature.WRAP_ROOT_VALUE).withRootName(root).writeValueAsString(bean); |
|||
} |
|||
}else{ |
|||
if(StrUtil.isEmpty(root)) { |
|||
return xmlMapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean); |
|||
}else{ |
|||
return xmlMapper.writer().with(SerializationFeature.WRAP_ROOT_VALUE).withRootName(root).withDefaultPrettyPrinter().writeValueAsString(bean); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static <T> String beanToXml(T bean,String root) throws JsonProcessingException { |
|||
return beanToXml(bean,root,false); |
|||
} |
|||
|
|||
public static <T> String beanToXml(T bean,boolean pretty) throws JsonProcessingException { |
|||
return beanToXml(bean,null,pretty); |
|||
} |
|||
|
|||
public static <T> String beanToXml(T bean) throws JsonProcessingException { |
|||
return beanToXml(bean,null,false); |
|||
} |
|||
|
|||
public static <T> T xmlToBean(String xml, Class<T> clazz) throws IOException { |
|||
return xmlMapper.readValue(xml, clazz); |
|||
} |
|||
|
|||
public static <T> T xmlToBean(InputStream xmlInStream, Class<T> clazz) throws IOException { |
|||
return xmlMapper.readValue(xmlInStream, clazz); |
|||
} |
|||
|
|||
/** |
|||
* Xml And Map Converter |
|||
* 注意:存在一直问题: xml转map时,对于相同属性,比如<receivers><to>123</to><to>234</to></receivers>不会自动转化为list,而还是map,由于map不能重复,因此导致第二个to丢失 |
|||
* 暂未找到解决办法 |
|||
* 暂时解决办法:xml转map时,先转成bean,bean再转map |
|||
* @param xml 要转换的xml字符串 |
|||
* @return Map |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static Map<String,Object> xmlToMap(String xml) throws IOException { |
|||
JavaType javaType = getCollectionType(objectMapper,Map.class,String.class,Object.class); |
|||
return xmlMapper.readValue(xml, javaType); |
|||
} |
|||
|
|||
public static Map<String,Object> xmlToMap(InputStream xmlInStream) throws IOException { |
|||
JavaType javaType = getCollectionType(objectMapper,Map.class,String.class,Object.class); |
|||
return xmlMapper.readValue(xmlInStream, javaType); |
|||
} |
|||
|
|||
public static String mapToXml(Map<String,Object> map,String root,boolean pretty) throws JsonProcessingException { |
|||
if(!pretty) { |
|||
if(StrUtil.isEmpty(root)) { |
|||
return xmlMapper.writeValueAsString(map); |
|||
}else{ |
|||
return xmlMapper.writer().with(SerializationFeature.WRAP_ROOT_VALUE).withRootName(root).writeValueAsString(map); |
|||
} |
|||
}else{ |
|||
if(StrUtil.isEmpty(root)) { |
|||
return xmlMapper.writerWithDefaultPrettyPrinter().writeValueAsString(map); |
|||
}else{ |
|||
return xmlMapper.writer().with(SerializationFeature.WRAP_ROOT_VALUE).withRootName(root).withDefaultPrettyPrinter().writeValueAsString(map); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static String mapToXml(Map<String,Object> map,String root) throws JsonProcessingException { |
|||
return mapToXml(map,root,false); |
|||
} |
|||
|
|||
public static String mapToXml(Map<String,Object> map,boolean pretty) throws JsonProcessingException { |
|||
return mapToXml(map,null,pretty); |
|||
} |
|||
|
|||
public static String mapToXml(Map<String,Object> map) throws JsonProcessingException { |
|||
return mapToXml(map,null,false); |
|||
} |
|||
|
|||
/** |
|||
* Json And Xml Converter |
|||
* @param json 要转换的json字符串 |
|||
* @return xml |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static String jsonToXml(String json) throws IOException { |
|||
return mapToXml(jsonToMap(json)); |
|||
} |
|||
|
|||
public static String jsonToXml(String json,String root) throws IOException { |
|||
return mapToXml(jsonToMap(json),root); |
|||
} |
|||
|
|||
public static String jsonToXml(String json,boolean pretty) throws IOException { |
|||
return mapToXml(jsonToMap(json),pretty); |
|||
} |
|||
|
|||
public static String jsonToXml(String json,String root,boolean pretty) throws IOException { |
|||
return mapToXml(jsonToMap(json),root,pretty); |
|||
} |
|||
|
|||
public static String xmlToJson(String xml) throws IOException { |
|||
return mapToJson(xmlToMap(xml)); |
|||
} |
|||
|
|||
public static String xmlToJson(String xml,boolean pretty) throws IOException { |
|||
return mapToJson(xmlToMap(xml),pretty); |
|||
} |
|||
|
|||
/** |
|||
* 获取单个字段值 |
|||
* @param json |
|||
* @param property |
|||
* @return |
|||
* @throws IOException |
|||
* |
|||
* JsonNode tmpNode, node = objectMapper.readTree(json); |
|||
* System.out.println("voName:" + node.get("voName").textValue()); |
|||
* System.out.println("pers: " + node.get("pers").toString()); |
|||
* System.out.println("age: " + node.get("age").toString()); |
|||
* JsonNode persNode = node.get("pers"); |
|||
* if (persNode.isArray()) { |
|||
* for (int i = 0; i < persNode.size(); i++) { |
|||
* tmpNode = persNode.get(i); |
|||
* System.out.println(tmpNode.get("name")); |
|||
* JsonNode childNode = tmpNode.get("childs"); |
|||
* if(!childNode.isNull() && childNode.isArray()){ |
|||
* for(int j=0;j<childNode.size();j++){ |
|||
* tmpNode = childNode.get(j); |
|||
* System.out.println("\t" + tmpNode.get("name")); |
|||
* } |
|||
* } |
|||
* } |
|||
* } |
|||
*/ |
|||
public static JsonNode getJsonProperty(String json,String property) throws IOException { |
|||
return objectMapper.readTree(json).get(property); |
|||
} |
|||
|
|||
public static JsonNode getJsonProperty(JsonNode jsonNode,String property) throws IOException { |
|||
return jsonNode.get(property); |
|||
} |
|||
|
|||
/** |
|||
* 根据表达式返回对应的JsonNode |
|||
* @param json 要解析的Json字符串 |
|||
* @param path JsonPath表达式 类似 $.a.b.c[2].d |
|||
* @return 对应的JsonNode |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static JsonNode getJsonPropertyByPath(String json,String path) throws IOException{ |
|||
//TODO
|
|||
return null; |
|||
} |
|||
/** |
|||
* 根据表达式返回对应的JsonNode |
|||
* @param json 要解析的Json字符串 |
|||
* @param path JsonPath表达式 类似 $.a.b.c[2].d |
|||
* @param t 预期类型 |
|||
* @return 对应的JsonNode |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static <T> T getJsonPropertyValueByPath(String json,String path,T t) throws IOException{ |
|||
//TODO
|
|||
return null; |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import com.jayway.jsonpath.Configuration; |
|||
import com.jayway.jsonpath.Option; |
|||
import com.jayway.jsonpath.spi.json.JacksonJsonProvider; |
|||
import com.jayway.jsonpath.spi.json.JsonProvider; |
|||
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; |
|||
import com.jayway.jsonpath.spi.mapper.MappingProvider; |
|||
|
|||
import java.util.EnumSet; |
|||
import java.util.Set; |
|||
|
|||
public class JsonPathUtil { |
|||
private Configuration jacksonConfiguration(){ |
|||
Configuration conf = Configuration.defaultConfiguration(); |
|||
Configuration conf2 = conf.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL); |
|||
Configuration conf3 = Configuration.defaultConfiguration(); |
|||
|
|||
Configuration.setDefaults(new Configuration.Defaults() { |
|||
|
|||
private final JsonProvider jsonProvider = new JacksonJsonProvider(); |
|||
private final MappingProvider mappingProvider = new JacksonMappingProvider(); |
|||
|
|||
@Override |
|||
public JsonProvider jsonProvider() { |
|||
return jsonProvider; |
|||
} |
|||
|
|||
@Override |
|||
public MappingProvider mappingProvider() { |
|||
return mappingProvider; |
|||
} |
|||
|
|||
@Override |
|||
public Set<Option> options() { |
|||
return EnumSet.noneOf(Option.class); |
|||
} |
|||
}); |
|||
|
|||
return conf; |
|||
} |
|||
} |
@ -0,0 +1,158 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@ApiModel |
|||
public class JsonResponse<T> { |
|||
|
|||
/** |
|||
* Common Error Constant |
|||
*/ |
|||
public static class RegularError { |
|||
public static final int ERRCODE_SUCCESS = 200; |
|||
public static final String ERRCODE_SUCCESS_PHASE = "ok"; |
|||
public static final int ERRCODE_FAIL = -1; |
|||
public static final String ERRCODE_FAIL_PHASE = "error"; |
|||
} |
|||
|
|||
/** |
|||
* Jwt Error Constant |
|||
*/ |
|||
public static class TokenError{ |
|||
public static final int TOKEN_ERRCODE_OK = JwtUtil.JwtError.TOKEN_ERRCODE_OK; |
|||
public static final String TOKEN_ERRCODE_OK_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_OK_PHASE; |
|||
public static final int TOKEN_ERRCODE_NOTFOUND = JwtUtil.JwtError.TOKEN_ERRCODE_NOTFOUND; |
|||
public static final String TOKEN_ERRCODE_NOTFOUND_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_NOTFOUND_PHASE; |
|||
public static final int TOKEN_ERRCODE_SIGNATURE_INVALIDATE = JwtUtil.JwtError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE; |
|||
public static final String TOKEN_ERRCODE_SIGNATURE_INVALIDATE_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE_PHASE; |
|||
public static final int TOKEN_ERRCODE_EXPIRE = JwtUtil.JwtError.TOKEN_ERRCODE_EXPIRE; |
|||
public static final String TOKEN_ERRCODE_EXPIRE_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_EXPIRE_PHASE; |
|||
public static final int TOKEN_ERRCODE_STUB_NOT_FOUND = JwtUtil.JwtError.TOKEN_ERRCODE_STUB_NOT_FOUND; |
|||
public static final String TOKEN_ERRCODE_STUB_NOT_FOUND_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_STUB_NOT_FOUND_PHASE; |
|||
public static final int TOKEN_ERRCODE_USER_DISABLED = JwtUtil.JwtError.TOKEN_ERRCODE_USER_DISABLED; |
|||
public static final String TOKEN_ERRCODE_USER_DISABLED_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_USER_DISABLED_PHASE; |
|||
public static final int TOKEN_ERRCODE_FAILED = JwtUtil.JwtError.TOKEN_ERRCODE_FAILED; |
|||
public static final String TOKEN_ERRCODE_FAILED_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_FAILED_PHASE; |
|||
} |
|||
|
|||
@ApiModelProperty(value="状态码") |
|||
private Integer code; |
|||
@ApiModelProperty(value="数据") |
|||
private T data; |
|||
@ApiModelProperty(value="消息") |
|||
private String msg; |
|||
@ApiModelProperty(value="成功与否") |
|||
private boolean success; |
|||
|
|||
private JsonResponse() { |
|||
} |
|||
|
|||
public static JsonResponse newInstance(){ |
|||
return new JsonResponse(); |
|||
} |
|||
|
|||
public JsonResponse ok(){ |
|||
this.code = RegularError.ERRCODE_SUCCESS; |
|||
this.msg = RegularError.ERRCODE_SUCCESS_PHASE; |
|||
this.success = true; |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse ok(T data){ |
|||
ok(); |
|||
this.data = data; |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse fail(){ |
|||
this.code = RegularError.ERRCODE_FAIL; |
|||
this.msg = RegularError.ERRCODE_FAIL_PHASE; |
|||
this.success = false; |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse fail(String error){ |
|||
fail(); |
|||
this.msg = error; |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse fail(int code){ |
|||
fail(); |
|||
this.code = code; |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse fail(int code, String error){ |
|||
fail(); |
|||
this.code = code; |
|||
this.msg = error; |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse fail(int code, String error, T obj){ |
|||
fail(); |
|||
this.code = code; |
|||
this.msg = error; |
|||
this.data = obj; |
|||
return this; |
|||
} |
|||
|
|||
//Token null
|
|||
public JsonResponse tokenNotFound(){ |
|||
fail(TokenError.TOKEN_ERRCODE_NOTFOUND, TokenError.TOKEN_ERRCODE_NOTFOUND_PHASE); |
|||
return this; |
|||
} |
|||
|
|||
//Token expire
|
|||
public JsonResponse tokenExpire(){ |
|||
fail(TokenError.TOKEN_ERRCODE_EXPIRE, TokenError.TOKEN_ERRCODE_EXPIRE_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse tokenExpire(String phase){ |
|||
fail(TokenError.TOKEN_ERRCODE_EXPIRE,phase); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse tokenSignatureFail(){ |
|||
fail(TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE, TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse tokenSignatureFail(String phase){ |
|||
fail(TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE,phase); |
|||
return this; |
|||
} |
|||
|
|||
//Token Stub Not Found
|
|||
public JsonResponse tokenStubNotFound(){ |
|||
fail(TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND, TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse tokenDisabled(String phase){ |
|||
fail(TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND,phase); |
|||
return this; |
|||
} |
|||
|
|||
//User Disabled
|
|||
public JsonResponse userDisabled(){ |
|||
fail(TokenError.TOKEN_ERRCODE_USER_DISABLED, TokenError.TOKEN_ERRCODE_USER_DISABLED_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse userDisabled(String phase){ |
|||
fail(TokenError.TOKEN_ERRCODE_USER_DISABLED,phase); |
|||
return this; |
|||
} |
|||
|
|||
//Token Failed
|
|||
public JsonResponse tokenFailed(){ |
|||
fail(TokenError.TOKEN_ERRCODE_FAILED, TokenError.TOKEN_ERRCODE_FAILED_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse tokenFailed(String phase){ |
|||
fail(TokenError.TOKEN_ERRCODE_FAILED,phase); |
|||
return this; |
|||
} |
|||
} |
@ -0,0 +1,298 @@ |
|||
package wiki.tall.ccmq.common.util; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
|
|||
public class JsonResponse1 { |
|||
|
|||
/** |
|||
* Hypermedia API |
|||
*/ |
|||
private static class Link{ |
|||
private String rel; //API与当前调用API的关系
|
|||
private String href; //API url
|
|||
private String title; //API title
|
|||
private String type; //API 返回类型
|
|||
public Link() { |
|||
} |
|||
|
|||
public Link(String title,String rel, String href) { |
|||
this.title = title; |
|||
this.rel = rel; |
|||
this.href = href; |
|||
} |
|||
|
|||
public Link(String title,String rel, String href,String type) { |
|||
this.title = title; |
|||
this.rel = rel; |
|||
this.href = href; |
|||
this.type = type; |
|||
} |
|||
|
|||
public String getRel() { |
|||
return rel; |
|||
} |
|||
|
|||
public void setRel(String rel) { |
|||
this.rel = rel; |
|||
} |
|||
|
|||
public String getHref() { |
|||
return href; |
|||
} |
|||
|
|||
public void setHref(String href) { |
|||
this.href = href; |
|||
} |
|||
|
|||
public String getTitle() { |
|||
return title; |
|||
} |
|||
|
|||
public void setTitle(String title) { |
|||
this.title = title; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Meta data |
|||
*/ |
|||
public static class Meta{ |
|||
/** |
|||
* Common Error Constant |
|||
*/ |
|||
public static class RegularError { |
|||
public static final int ERRCODE_SUCCESS = 0; |
|||
public static final String ERRCODE_SUCCESS_PHASE = "ok"; |
|||
public static final int ERRCODE_FAIL = -1; |
|||
public static final String ERRCODE_FAIL_PHASE = "error"; |
|||
} |
|||
|
|||
/** |
|||
* Jwt Error Constant |
|||
*/ |
|||
public static class TokenError{ |
|||
public static final int TOKEN_ERRCODE_OK = JwtUtil.JwtError.TOKEN_ERRCODE_OK; |
|||
public static final String TOKEN_ERRCODE_OK_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_OK_PHASE; |
|||
public static final int TOKEN_ERRCODE_NOTFOUND = JwtUtil.JwtError.TOKEN_ERRCODE_NOTFOUND; |
|||
public static final String TOKEN_ERRCODE_NOTFOUND_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_NOTFOUND_PHASE; |
|||
public static final int TOKEN_ERRCODE_SIGNATURE_INVALIDATE = JwtUtil.JwtError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE; |
|||
public static final String TOKEN_ERRCODE_SIGNATURE_INVALIDATE_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE_PHASE; |
|||
public static final int TOKEN_ERRCODE_EXPIRE = JwtUtil.JwtError.TOKEN_ERRCODE_EXPIRE; |
|||
public static final String TOKEN_ERRCODE_EXPIRE_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_EXPIRE_PHASE; |
|||
public static final int TOKEN_ERRCODE_STUB_NOT_FOUND = JwtUtil.JwtError.TOKEN_ERRCODE_STUB_NOT_FOUND; |
|||
public static final String TOKEN_ERRCODE_STUB_NOT_FOUND_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_STUB_NOT_FOUND_PHASE; |
|||
public static final int TOKEN_ERRCODE_USER_DISABLED = JwtUtil.JwtError.TOKEN_ERRCODE_USER_DISABLED; |
|||
public static final String TOKEN_ERRCODE_USER_DISABLED_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_USER_DISABLED_PHASE; |
|||
public static final int TOKEN_ERRCODE_FAILED = JwtUtil.JwtError.TOKEN_ERRCODE_FAILED; |
|||
public static final String TOKEN_ERRCODE_FAILED_PHASE = JwtUtil.JwtError.TOKEN_ERRCODE_FAILED_PHASE; |
|||
} |
|||
|
|||
private int code; |
|||
private String error; |
|||
|
|||
public Meta() { |
|||
} |
|||
|
|||
public Meta(int code) { |
|||
this.code = code; |
|||
} |
|||
|
|||
public Meta(int code,String error) { |
|||
this.code = code; |
|||
this.error = error; |
|||
} |
|||
|
|||
public int getCode() { |
|||
return code; |
|||
} |
|||
|
|||
public void setCode(int code) { |
|||
this.code = code; |
|||
} |
|||
|
|||
public String getError() { |
|||
return error; |
|||
} |
|||
|
|||
public void setError(String error) { |
|||
this.error = error; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Page Data |
|||
*/ |
|||
public class Data{ |
|||
private long total; |
|||
private int size; |
|||
private Collection list; |
|||
|
|||
public Data(){} |
|||
|
|||
public Data(long total,Collection list){ |
|||
if(list != null && list.size() == 1){ //处理Arrays.asList(null)的情况
|
|||
if(list.toArray()[0] == null) |
|||
list = null; |
|||
} |
|||
this.total = total; |
|||
this.size = list == null ? 0 : list.size(); |
|||
this.list = list; |
|||
} |
|||
|
|||
public long getTotal() { |
|||
return total; |
|||
} |
|||
|
|||
public void setTotal(long total) { |
|||
this.total = total; |
|||
} |
|||
|
|||
public int getSize() { |
|||
return size; |
|||
} |
|||
|
|||
public void setSize(int size) { |
|||
this.size = size; |
|||
} |
|||
|
|||
public Collection getList() { |
|||
return list; |
|||
} |
|||
|
|||
public void setList(Collection list) { |
|||
this.list = list; |
|||
} |
|||
} |
|||
|
|||
private Meta meta; |
|||
private Data data; |
|||
private List<Link> links; |
|||
|
|||
public static JsonResponse1 newInstance(){ |
|||
return new JsonResponse1(); |
|||
} |
|||
|
|||
public JsonResponse1 addLink(String title, String rel, String url){ |
|||
if(links==null) |
|||
links = new ArrayList<Link>(); |
|||
links.add(new Link(title,rel,url)); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1 ok(){ |
|||
this.meta = new Meta(Meta.RegularError.ERRCODE_SUCCESS, Meta.RegularError.ERRCODE_SUCCESS_PHASE); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1 ok(Collection list){ |
|||
ok(); |
|||
this.data = new Data(-1,list); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1 ok(long total, Collection list){ |
|||
ok(); |
|||
this.data = new Data(total,list); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1 fail(){ |
|||
this.meta = new Meta(Meta.RegularError.ERRCODE_FAIL, Meta.RegularError.ERRCODE_FAIL_PHASE); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1 fail(String error){ |
|||
this.meta = new Meta(Meta.RegularError.ERRCODE_FAIL,error); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1 fail(int code){ |
|||
this.meta = new Meta(code, Meta.RegularError.ERRCODE_FAIL_PHASE); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1 fail(int code, String error){ |
|||
this.meta = new Meta(code,error); |
|||
return this; |
|||
} |
|||
|
|||
//Token null
|
|||
public JsonResponse1 tokenNotFound(){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_NOTFOUND, Meta.TokenError.TOKEN_ERRCODE_NOTFOUND_PHASE); |
|||
return this; |
|||
} |
|||
|
|||
//Token expire
|
|||
public JsonResponse1 tokenExpire(){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_EXPIRE, Meta.TokenError.TOKEN_ERRCODE_EXPIRE_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse1 tokenExpire(String phase){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_EXPIRE,phase); |
|||
return this; |
|||
} |
|||
|
|||
//Token Signature failed
|
|||
public JsonResponse1 tokenSignatureFail(){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE, Meta.TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse1 tokenSignatureFail(String phase){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE,phase); |
|||
return this; |
|||
} |
|||
|
|||
//Token Stub Not Found
|
|||
public JsonResponse1 tokenStubNotFound(){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND, Meta.TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND_PHASE); |
|||
return this; |
|||
}public JsonResponse1 tokenDisabled(String phase){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND,phase); |
|||
return this; |
|||
} |
|||
|
|||
//User Disabled
|
|||
public JsonResponse1 userDisabled(){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_USER_DISABLED, Meta.TokenError.TOKEN_ERRCODE_USER_DISABLED_PHASE); |
|||
return this; |
|||
}public JsonResponse1 userDisabled(String phase){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_USER_DISABLED,phase); |
|||
return this; |
|||
} |
|||
|
|||
//Token Failed
|
|||
public JsonResponse1 tokenFailed(){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_FAILED, Meta.TokenError.TOKEN_ERRCODE_FAILED_PHASE); |
|||
return this; |
|||
} |
|||
public JsonResponse1 tokenFailed(String phase){ |
|||
this.meta = new Meta(Meta.TokenError.TOKEN_ERRCODE_FAILED, phase); |
|||
return this; |
|||
} |
|||
|
|||
public JsonResponse1() { |
|||
} |
|||
|
|||
public Meta getMeta() { |
|||
return meta; |
|||
} |
|||
|
|||
public Object getData() { |
|||
return data; |
|||
} |
|||
|
|||
public Object setData(Data data) { |
|||
this.data = data; |
|||
return this; |
|||
} |
|||
|
|||
public List<Link> getLinks() { |
|||
return links; |
|||
} |
|||
|
|||
public void setLinks(List<Link> links) { |
|||
this.links = links; |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue