├── .gitignore ├── LICENSE ├── build.gradle ├── docs ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── java │ └── com │ └── clusterws │ ├── Channel.java │ ├── ClusterWS.java │ ├── Emitter.java │ ├── IClusterWSListener.java │ ├── IEmitterListener.java │ ├── ISocketEvents.java │ ├── MessageHandler.java │ ├── PingHandler.java │ ├── ReconnectionParams.java │ └── Socket.java └── test └── java └── com └── clusterws ├── ChannelServerTest.java ├── ChannelTest.java ├── ClusterWSStatesBool.java ├── ClusterWSTest.java ├── EmitterTest.java ├── MessageHandlerTest.java └── PingHandlerTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /build 3 | *.iml 4 | /.gradle 5 | /out -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ClusterWS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'idea' 3 | apply plugin: 'maven' 4 | 5 | group 'com.github.clusterws' 6 | version '1.5' 7 | sourceCompatibility = 1.8 8 | targetCompatibility = 1.8 9 | 10 | repositories { 11 | mavenCentral() 12 | jcenter() 13 | } 14 | 15 | configurations { 16 | deployerJars 17 | } 18 | 19 | dependencies { 20 | testCompile 'junit:junit:4.12' 21 | testCompile 'org.mockito:mockito-core:2.7.22' 22 | compile "org.java-websocket:Java-WebSocket:1.3.6" 23 | compile 'com.alibaba:fastjson:1.2.44' 24 | } 25 | 26 | task sourcesJar(type: Jar, dependsOn: classes) { 27 | classifier = 'sources' 28 | from sourceSets.main.allJava 29 | } 30 | 31 | task javadocJar(type: Jar, dependsOn: javadoc) { 32 | classifier = 'javadoc' 33 | from javadoc.destinationDir 34 | } 35 | 36 | artifacts { 37 | archives sourcesJar 38 | archives javadocJar 39 | } 40 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team . The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /docs/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Submitting 2 | 3 | - [ ] Bug 4 | - [ ] Question 5 | - [ ] Suggetion 6 | - [ ] Other 7 | 8 | ### Details 9 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Submitting 2 | 3 | - [ ] Bug fix 4 | - [ ] Feature 5 | - [ ] Other 6 | 7 | ### Details 8 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 |

ClusterWS Java Client

2 |
Build Scalable Node.js WebSocket Applications
3 | 4 |

5 | 6 |

7 | 8 |

9 | 10 | 11 | Maintain 12 |

13 | 14 |

15 | Official Java Client library for ClusterWS - lightweight, fast and powerful framework for building horizontally & vertically scalable WebSocket applications in Node.js 16 |

17 | 18 |

19 |

20 | Find more about ClusterWS Java Client in Wiki Documentation 21 |

-------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClusterWS/ClusterWS-Client-Java/50deb4992b414aad001fc47744aad801cabd5662/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Dec 10 10:00:07 NZDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-rc-2-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'clusterws-client-java' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/Channel.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import java.util.List; 4 | 5 | public class Channel { 6 | public interface IChannelListener { 7 | void onDataReceived(String channelName, Object data); 8 | } 9 | 10 | private IChannelListener mChannelListener; 11 | private String mChannelName; 12 | private ClusterWS mClusterWS; 13 | 14 | public Channel(String channelName, ClusterWS clusterWS) { 15 | mChannelName = channelName; 16 | mClusterWS = clusterWS; 17 | } 18 | 19 | public Channel watch(IChannelListener listener) { 20 | mChannelListener = listener; 21 | return this; 22 | } 23 | 24 | public Channel publish(Object data) { 25 | mClusterWS.send(mChannelName, data, "publish"); 26 | return this; 27 | } 28 | 29 | public void unsubscribe() { 30 | mClusterWS.send("unsubscribe", mChannelName, "system"); 31 | List channelArrayList = mClusterWS.getChannels(); 32 | channelArrayList.remove(this); 33 | } 34 | 35 | String getChannelName() { 36 | return mChannelName; 37 | } 38 | 39 | void onMessage(Object data) { 40 | if (mChannelListener != null) { 41 | mChannelListener.onDataReceived(mChannelName, data); 42 | } 43 | } 44 | 45 | void subscribe() { 46 | mClusterWS.send("subscribe", mChannelName, "system"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/ClusterWS.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import org.java_websocket.WebSocket; 4 | 5 | import java.net.URI; 6 | import java.nio.ByteBuffer; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Timer; 11 | import java.util.TimerTask; 12 | import java.util.concurrent.ThreadLocalRandom; 13 | 14 | public class ClusterWS { 15 | private Socket mSocket; 16 | private String mUrl; 17 | private Emitter mEmitter; 18 | private boolean mUseBinary; 19 | private IClusterWSListener mClusterWSListener; 20 | private MessageHandler mMessageHandler; 21 | private PingHandler mPingHandler; 22 | private List mChannels; 23 | private ReconnectionParams mReconnectionParams; 24 | private static final byte[] PONG = "A".getBytes(); 25 | 26 | public ClusterWS(String url) { 27 | if (url == null) { 28 | throw new NullPointerException("Url must be provided"); 29 | } 30 | mUrl = url; 31 | mChannels = new ArrayList<>(); 32 | mReconnectionParams = new ReconnectionParams( 33 | false, 34 | null, 35 | null, 36 | null); 37 | createSocket(); 38 | } 39 | 40 | public ClusterWS setReconnection(Boolean autoReconnect, 41 | Integer reconnectionIntervalMin, 42 | Integer reconnectionIntervalMax, 43 | Integer reconnectionAttempts) { 44 | mReconnectionParams = new ReconnectionParams(autoReconnect, 45 | reconnectionIntervalMin, 46 | reconnectionIntervalMax, 47 | reconnectionAttempts); 48 | return this; 49 | } 50 | 51 | public ClusterWS setClusterWSListener(IClusterWSListener clusterWSListener) { 52 | mClusterWSListener = clusterWSListener; 53 | return this; 54 | } 55 | 56 | public void connect() { 57 | mSocket.connect(); 58 | } 59 | 60 | public void disconnect(Integer closeCode, String reason) { 61 | mSocket.close(closeCode == null ? 1000 : closeCode, reason); 62 | } 63 | 64 | public void on(String event, IEmitterListener listener) { 65 | mEmitter.addEventListener(event, listener); 66 | } 67 | 68 | public void send(String event, Object data) { 69 | send(event, data, "emit"); 70 | } 71 | 72 | public WebSocket.READYSTATE getState() { 73 | return mSocket.getReadyState(); 74 | } 75 | 76 | public Channel getChannelByName(String channelName) { 77 | for (Channel channel : 78 | mChannels) { 79 | if (channel.getChannelName().equals(channelName)) { 80 | return channel; 81 | } 82 | } 83 | return null; 84 | } 85 | 86 | public Channel subscribe(String channelName) { 87 | for (Channel channel : 88 | mChannels) { 89 | if (channel.getChannelName().equals(channelName)) { 90 | return channel; 91 | } 92 | } 93 | Channel newChannel = new Channel(channelName, this); 94 | newChannel.subscribe(); 95 | mChannels.add(newChannel); 96 | return newChannel; 97 | } 98 | 99 | public List getChannels() { 100 | return mChannels; 101 | } 102 | 103 | IClusterWSListener getClusterWSListener() { 104 | return mClusterWSListener; 105 | } 106 | 107 | Emitter getEmitter() { 108 | return mEmitter; 109 | } 110 | 111 | void setUseBinary(boolean useBinary) { 112 | mUseBinary = useBinary; 113 | } 114 | 115 | 116 | void send(String event, Object data, String type) { 117 | if (mUseBinary) { 118 | mSocket.send(mMessageHandler.messageEncode(event, data, type).getBytes()); 119 | } else { 120 | mSocket.send(mMessageHandler.messageEncode(event, data, type)); 121 | 122 | } 123 | } 124 | 125 | PingHandler getPingHandler() { 126 | return mPingHandler; 127 | } 128 | 129 | private void createSocket() { 130 | mSocket = new Socket(URI.create(mUrl), new ISocketEvents() { 131 | @Override 132 | public void onOpen() { 133 | for (Channel channel : 134 | mChannels) { 135 | channel.subscribe(); 136 | } 137 | } 138 | 139 | @Override 140 | public void onError(Exception exception) { 141 | if (mClusterWSListener != null) { 142 | mClusterWSListener.onError(exception); 143 | } 144 | } 145 | 146 | @Override 147 | public void onClose(int code, String reason) { 148 | if (mPingHandler.getPingTimer() != null) { 149 | mPingHandler.getPingTimer().cancel(); 150 | } 151 | if (mReconnectionParams.isAutoReconnect() 152 | && code != 1000 153 | && (mReconnectionParams.getReconnectionAttempts() == 0 || mReconnectionParams.getReconnectionsAttempted() < mReconnectionParams.getReconnectionAttempts())) { 154 | if (mSocket.getReadyState() == WebSocket.READYSTATE.CLOSED || mSocket.getReadyState() == WebSocket.READYSTATE.NOT_YET_CONNECTED || mSocket.getReadyState() == WebSocket.READYSTATE.CLOSING) { 155 | mReconnectionParams.incrementReconnectionsAttempted(); 156 | int randomDelay = ThreadLocalRandom.current().nextInt(1, 157 | mReconnectionParams.getReconnectionIntervalMax() - 158 | mReconnectionParams.getReconnectionIntervalMin() + 159 | 1); 160 | new Timer().schedule(new TimerTask() { 161 | @Override 162 | public void run() { 163 | connect(); 164 | } 165 | }, randomDelay); 166 | } 167 | } 168 | mClusterWSListener.onDisconnected(code, reason); 169 | } 170 | 171 | @Override 172 | public void onBinaryMessage(ByteBuffer bytes) { 173 | byte[] arr = new byte[bytes.remaining()]; 174 | bytes.get(arr); 175 | if (arr.length == 1 && arr[0] == 57) { 176 | mPingHandler.setMissedPingToZero(); 177 | mSocket.send(PONG); 178 | } else { 179 | String message = new String(arr, StandardCharsets.UTF_8); 180 | onMessageReceived(message); 181 | } 182 | 183 | } 184 | 185 | @Override 186 | public void onMessage(String message) { 187 | onMessageReceived(message); 188 | } 189 | }); 190 | mUseBinary = false; 191 | mEmitter = new Emitter(); 192 | mMessageHandler = new MessageHandler(); 193 | mPingHandler = new PingHandler(); 194 | } 195 | 196 | private void onMessageReceived(String message) { 197 | mMessageHandler.messageDecode(ClusterWS.this, message); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/Emitter.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | class Emitter { 6 | private ConcurrentHashMap mEvents; 7 | 8 | Emitter() { 9 | mEvents = new ConcurrentHashMap<>(); 10 | } 11 | 12 | void addEventListener(String event, IEmitterListener listener) { 13 | if (mEvents.containsKey(event)) { 14 | mEvents.replace(event, listener); 15 | } else { 16 | mEvents.put(event, listener); 17 | } 18 | } 19 | 20 | void emit(String event, Object object) { 21 | IEmitterListener listener = mEvents.get(event); 22 | if (listener != null) { 23 | listener.onDataReceived(object); 24 | } 25 | } 26 | 27 | void removeAllEvents() { 28 | mEvents = new ConcurrentHashMap<>(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/IClusterWSListener.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | public interface IClusterWSListener { 4 | void onConnected(); 5 | 6 | void onError(Exception exception); 7 | 8 | void onDisconnected(int code, String reason); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/IEmitterListener.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | public interface IEmitterListener { 4 | void onDataReceived(Object data); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/ISocketEvents.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public interface ISocketEvents { 6 | void onOpen(); 7 | 8 | void onError(Exception exception); 9 | 10 | void onClose(int code, String reason); 11 | 12 | void onBinaryMessage(ByteBuffer bytes); 13 | 14 | void onMessage(String message); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | 7 | import java.util.List; 8 | import java.util.TimerTask; 9 | 10 | 11 | class MessageHandler { 12 | String messageEncode(String event, Object data, String type) { 13 | JSONObject jsonObject = new JSONObject(); 14 | JSONArray jsonArray = new JSONArray(); 15 | switch (type) { 16 | case "publish": 17 | jsonArray.add("p"); 18 | jsonArray.add(event); 19 | jsonArray.add(data); 20 | jsonObject.put("#", jsonArray); 21 | return jsonObject.toJSONString(); 22 | case "emit": 23 | jsonArray.add("e"); 24 | jsonArray.add(event); 25 | jsonArray.add(data); 26 | jsonObject.put("#", jsonArray); 27 | return jsonObject.toJSONString(); 28 | case "system": 29 | switch (event) { 30 | case "subscribe": 31 | jsonArray.add("s"); 32 | jsonArray.add("s"); 33 | jsonArray.add(data); 34 | jsonObject.put("#", jsonArray); 35 | return jsonObject.toJSONString(); 36 | case "unsubscribe": 37 | jsonArray.add("s"); 38 | jsonArray.add("u"); 39 | jsonArray.add(data); 40 | jsonObject.put("#", jsonArray); 41 | return jsonObject.toJSONString(); 42 | } 43 | case "ping": 44 | return event; 45 | default: 46 | return event; 47 | } 48 | } 49 | 50 | void messageDecode(final ClusterWS socket, String message) { 51 | JSONArray jsonArray = JSON.parseObject(message).getJSONArray("#"); 52 | switch (jsonArray.getString(0)) { 53 | case "p": 54 | // 55 | List channelArrayList = socket.getChannels(); 56 | String channelName = jsonArray.getString(1); 57 | for (Channel channel : 58 | channelArrayList) { 59 | if (channel.getChannelName().equals(channelName)) { 60 | channel.onMessage(jsonArray.get(2)); 61 | break; 62 | } 63 | } 64 | break; 65 | case "e": 66 | socket.getEmitter().emit(jsonArray.getString(1), jsonArray.get(2)); 67 | break; 68 | case "s": 69 | if (jsonArray.getString(1).equals("c")) { 70 | socket.getPingHandler().getPingTimer().scheduleAtFixedRate(new TimerTask() { 71 | @Override 72 | public void run() { 73 | if (socket.getPingHandler().getMissedPing() < 3) { 74 | socket.getPingHandler().incrementMissedPing(); 75 | } else { 76 | socket.disconnect(4001, "No pings"); 77 | cancel(); 78 | } 79 | } 80 | }, 0, jsonArray.getJSONObject(2).getInteger("ping")); 81 | boolean useBinary = jsonArray.getJSONObject(2).getBoolean("binary"); 82 | socket.setUseBinary(useBinary); 83 | if (socket.getClusterWSListener() != null) { 84 | socket.getClusterWSListener().onConnected(); 85 | } 86 | } 87 | break; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/PingHandler.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import java.util.Timer; 4 | 5 | class PingHandler { 6 | private Timer mPingTimer; 7 | private int mMissedPing; 8 | 9 | PingHandler() { 10 | mPingTimer = new Timer(); 11 | mMissedPing = 0; 12 | } 13 | 14 | void incrementMissedPing() { 15 | mMissedPing++; 16 | } 17 | 18 | void setMissedPingToZero() { 19 | mMissedPing = 0; 20 | } 21 | 22 | Timer getPingTimer() { 23 | return mPingTimer; 24 | } 25 | 26 | int getMissedPing() { 27 | return mMissedPing; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/ReconnectionParams.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | public class ReconnectionParams { 4 | private Boolean mAutoReconnect; 5 | private Integer mReconnectionIntervalMin; 6 | private Integer mReconnectionIntervalMax; 7 | private Integer mReconnectionAttempts; 8 | private Integer mReconnectionsAttempted; 9 | 10 | public ReconnectionParams(Boolean autoReconnect, 11 | Integer reconnectionIntervalMin, 12 | Integer reconnectionIntervalMax, 13 | Integer reconnectionAttempts) { 14 | mAutoReconnect = autoReconnect != null ? autoReconnect : false; 15 | mReconnectionIntervalMin = reconnectionIntervalMin != null ? reconnectionIntervalMin : 1000; 16 | mReconnectionIntervalMax = reconnectionIntervalMax != null ? reconnectionIntervalMax : 5000; 17 | mReconnectionAttempts = reconnectionAttempts != null ? reconnectionAttempts : 0; 18 | mReconnectionsAttempted = 0; 19 | } 20 | 21 | public boolean isAutoReconnect() { 22 | return mAutoReconnect; 23 | } 24 | 25 | public Integer getReconnectionIntervalMin() { 26 | return mReconnectionIntervalMin; 27 | } 28 | 29 | public Integer getReconnectionIntervalMax() { 30 | return mReconnectionIntervalMax; 31 | } 32 | 33 | public Integer getReconnectionAttempts() { 34 | return mReconnectionAttempts; 35 | } 36 | 37 | public void incrementReconnectionsAttempted() { 38 | mReconnectionsAttempted++; 39 | } 40 | 41 | public void resetReconnectionsAttempted() { 42 | mReconnectionsAttempted = 0; 43 | } 44 | 45 | public Integer getReconnectionsAttempted() { 46 | return mReconnectionsAttempted; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/clusterws/Socket.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import org.java_websocket.client.WebSocketClient; 4 | import org.java_websocket.handshake.ServerHandshake; 5 | 6 | import java.net.URI; 7 | import java.nio.ByteBuffer; 8 | 9 | class Socket extends WebSocketClient { 10 | private ISocketEvents mSocketEvents; 11 | 12 | Socket(URI serverUri, ISocketEvents socketEvents) { 13 | super(serverUri); 14 | mSocketEvents = socketEvents; 15 | } 16 | 17 | 18 | @Override 19 | public void onOpen(ServerHandshake handshakedata) { 20 | mSocketEvents.onOpen(); 21 | } 22 | 23 | @Override 24 | public void onMessage(String message) { 25 | mSocketEvents.onMessage(message); 26 | } 27 | 28 | @Override 29 | public void onClose(int code, String reason, boolean remote) { 30 | mSocketEvents.onClose(code, reason); 31 | } 32 | 33 | @Override 34 | public void onError(Exception ex) { 35 | mSocketEvents.onError(ex); 36 | } 37 | 38 | @Override 39 | public void onMessage(ByteBuffer bytes) { 40 | mSocketEvents.onBinaryMessage(bytes); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/com/clusterws/ChannelServerTest.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class ChannelServerTest { 13 | private ClusterWS mClusterWS; 14 | private boolean mGotTheData; 15 | private Object mReceivedData; 16 | 17 | @Before 18 | public void init() throws Exception { 19 | mClusterWS = new ClusterWS("ws://localhost:3000"); 20 | mGotTheData = false; 21 | mReceivedData = null; 22 | } 23 | 24 | @Test 25 | public void testChannelPublishAndWatchInt() throws Exception { 26 | mClusterWS.connect(); 27 | Thread.sleep(1000); 28 | mClusterWS.subscribe("testChannel") 29 | .watch(new Channel.IChannelListener() { 30 | @Override 31 | public void onDataReceived(String channelName, Object data) { 32 | mGotTheData = true; 33 | mReceivedData = data; 34 | } 35 | }) 36 | .publish(24); 37 | Thread.sleep(1000); 38 | 39 | assertTrue("Did not get the data", mGotTheData); 40 | assertEquals("Data send and data received are not the same", 24, mReceivedData); 41 | } 42 | 43 | @Test 44 | public void testChannelPublishAndWatchString() throws Exception { 45 | mClusterWS.connect(); 46 | Thread.sleep(1000); 47 | mClusterWS.subscribe("testChannel") 48 | .watch(new Channel.IChannelListener() { 49 | @Override 50 | public void onDataReceived(String channelName, Object data) { 51 | mGotTheData = true; 52 | mReceivedData = data; 53 | } 54 | }) 55 | .publish("test string"); 56 | Thread.sleep(1000); 57 | 58 | assertTrue("Did not get the data", mGotTheData); 59 | assertEquals("Data send and data received are not the same", "test string", mReceivedData); 60 | } 61 | 62 | @Test 63 | public void testChannelPublishAndWatchObject() throws Exception { 64 | JSONObject jsonObject = new JSONObject(); 65 | jsonObject.put("int", 30); 66 | jsonObject.put("bool", true); 67 | jsonObject.put("string", "CHLEN"); 68 | JSONArray jsonArray = new JSONArray(); 69 | jsonArray.add(30); 70 | jsonArray.add(true); 71 | jsonArray.add("CHLEN"); 72 | jsonObject.put("array", jsonArray); 73 | 74 | mClusterWS.connect(); 75 | Thread.sleep(1000); 76 | mClusterWS.subscribe("testChannel") 77 | .watch(new Channel.IChannelListener() { 78 | @Override 79 | public void onDataReceived(String channelName, Object data) { 80 | mGotTheData = true; 81 | mReceivedData = data; 82 | } 83 | }) 84 | .publish(jsonObject); 85 | Thread.sleep(1000); 86 | 87 | assertTrue("Did not get the data", mGotTheData); 88 | assertEquals("Data send and data received are not the same", jsonObject, mReceivedData); 89 | } 90 | 91 | @Test 92 | public void testChannelPublishAndWatchNull() throws Exception { 93 | mClusterWS.connect(); 94 | Thread.sleep(1000); 95 | mClusterWS.subscribe("testChannel") 96 | .watch(new Channel.IChannelListener() { 97 | @Override 98 | public void onDataReceived(String channelName, Object data) { 99 | mGotTheData = true; 100 | mReceivedData = data; 101 | } 102 | }) 103 | .publish(null); 104 | Thread.sleep(1000); 105 | 106 | assertTrue("Did not get the data", mGotTheData); 107 | assertEquals("Data send and data received are not the same", null, mReceivedData); 108 | } 109 | 110 | @Test 111 | public void testChannelPublishAndWatchArray() throws Exception { 112 | ArrayList mObjectArrayList; 113 | mObjectArrayList = new ArrayList<>(); 114 | mObjectArrayList.add(30); 115 | mObjectArrayList.add(false); 116 | mObjectArrayList.add("Test message"); 117 | 118 | JSONObject jsonObject = new JSONObject(); 119 | jsonObject.put("int", 30); 120 | jsonObject.put("bool", true); 121 | jsonObject.put("string", "CHLEN"); 122 | JSONArray jsonArray = new JSONArray(); 123 | jsonArray.add(30); 124 | jsonArray.add(true); 125 | jsonArray.add("CHLEN"); 126 | jsonObject.put("array", jsonArray); 127 | 128 | mObjectArrayList.add(jsonObject); 129 | 130 | mClusterWS.connect(); 131 | Thread.sleep(1000); 132 | mClusterWS.subscribe("testChannel") 133 | .watch(new Channel.IChannelListener() { 134 | @Override 135 | public void onDataReceived(String channelName, Object data) { 136 | mGotTheData = true; 137 | mReceivedData = data; 138 | } 139 | }) 140 | .publish(mObjectArrayList); 141 | Thread.sleep(1000); 142 | 143 | assertTrue("Did not get the data", mGotTheData); 144 | assertEquals("Data send and data received are not the same", mObjectArrayList, mReceivedData); 145 | } 146 | 147 | @Test 148 | public void testGetChannelByName() throws Exception { 149 | mClusterWS.connect(); 150 | Thread.sleep(1000); 151 | mClusterWS.subscribe("testChannel"); 152 | assertNotNull(mClusterWS.getChannelByName("testChannel")); 153 | } 154 | 155 | @Test 156 | public void testGetChannelList() throws Exception { 157 | mClusterWS.connect(); 158 | Thread.sleep(1000); 159 | mClusterWS.subscribe("testChannel1"); 160 | mClusterWS.subscribe("testChannel2"); 161 | mClusterWS.subscribe("testChannel3"); 162 | 163 | assertEquals(mClusterWS.getChannels().size(), 3); 164 | assertEquals(mClusterWS.getChannels().get(0).getChannelName(), "testChannel1"); 165 | assertEquals(mClusterWS.getChannels().get(1).getChannelName(), "testChannel2"); 166 | assertEquals(mClusterWS.getChannels().get(2).getChannelName(), "testChannel3"); 167 | } 168 | 169 | @Test 170 | public void testTryGetChannelByNameAfterUnsubscribing() throws Exception { 171 | mClusterWS.connect(); 172 | Thread.sleep(1000); 173 | Channel channel = mClusterWS.subscribe("testChannel1"); 174 | channel.unsubscribe(); 175 | assertNull(mClusterWS.getChannelByName("testChannel1")); 176 | } 177 | 178 | @Test 179 | public void testResubscribeOnAllChannelsAfterReconnection() throws Exception { 180 | mClusterWS.setReconnection(true, 1000, 2000, null); 181 | mClusterWS.connect(); 182 | Thread.sleep(1000); 183 | Channel channel = mClusterWS.subscribe("testChannel") 184 | .watch(new Channel.IChannelListener() { 185 | @Override 186 | public void onDataReceived(String channelName, Object data) { 187 | mGotTheData = true; 188 | mReceivedData = data; 189 | } 190 | }); 191 | mClusterWS.disconnect(4001, "hui"); 192 | Thread.sleep(3000); 193 | channel.publish("testData"); 194 | Thread.sleep(1000); 195 | assertTrue("Did not get the data", mGotTheData); 196 | assertEquals("Data send and data received are not the same", "testData", mReceivedData); 197 | } 198 | 199 | } -------------------------------------------------------------------------------- /src/test/java/com/clusterws/ChannelTest.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import org.junit.Before; 4 | import org.junit.Rule; 5 | import org.junit.Test; 6 | import org.mockito.Mock; 7 | import org.mockito.junit.MockitoJUnit; 8 | import org.mockito.junit.MockitoRule; 9 | 10 | import java.util.ArrayList; 11 | 12 | import static org.junit.Assert.*; 13 | import static org.mockito.Mockito.when; 14 | 15 | public class ChannelTest { 16 | private Channel mChannel; 17 | private static final String CHANNEL_NAME = "testChannel"; 18 | private static final String TEST_STRING_DATA = "testData"; 19 | 20 | @Mock 21 | private ClusterWS mClusterWS; 22 | 23 | @Rule 24 | public MockitoRule mockitoRule = MockitoJUnit.rule(); 25 | 26 | @Before 27 | public void init(){ 28 | mChannel = new Channel(CHANNEL_NAME,mClusterWS); 29 | } 30 | 31 | @Test 32 | public void watchAndOnMessage() throws Exception { 33 | mChannel.watch(new Channel.IChannelListener() { 34 | @Override 35 | public void onDataReceived(String channelName, Object data) { 36 | assertEquals("Data is not the same",TEST_STRING_DATA,data); 37 | } 38 | }); 39 | mChannel.onMessage(TEST_STRING_DATA); 40 | } 41 | 42 | 43 | @Test 44 | public void unsubscribe() throws Exception { 45 | ArrayList channelArrayList = new ArrayList<>(); 46 | channelArrayList.add(mChannel); 47 | channelArrayList.add(new Channel("GAY",mClusterWS)); 48 | channelArrayList.add(new Channel("SHIT",mClusterWS)); 49 | when(mClusterWS.getChannels()).thenReturn(channelArrayList); 50 | mChannel.unsubscribe(); 51 | assertNotEquals("There is an old channel",CHANNEL_NAME,channelArrayList.get(0).getChannelName()); 52 | } 53 | 54 | @Test 55 | public void getChannelName() throws Exception { 56 | assertEquals(mChannel.getChannelName(),CHANNEL_NAME); 57 | } 58 | 59 | 60 | } -------------------------------------------------------------------------------- /src/test/java/com/clusterws/ClusterWSStatesBool.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | public class ClusterWSStatesBool { 4 | private boolean mClosed; 5 | private boolean mClosing; 6 | private boolean mNotYetConnected; 7 | private boolean mConnecting; 8 | private boolean mOpen; 9 | 10 | public ClusterWSStatesBool() { 11 | mClosed = false; 12 | mClosing = false; 13 | mNotYetConnected = false; 14 | mConnecting = false; 15 | mOpen = false; 16 | } 17 | 18 | public boolean isClosed() { 19 | return mClosed; 20 | } 21 | 22 | public void setClosed(boolean closed) { 23 | mClosed = closed; 24 | } 25 | 26 | public boolean isClosing() { 27 | return mClosing; 28 | } 29 | 30 | public void setClosing(boolean closing) { 31 | mClosing = closing; 32 | } 33 | 34 | public boolean isNotYetConnected() { 35 | return mNotYetConnected; 36 | } 37 | 38 | public void setNotYetConnected(boolean notYetConnected) { 39 | mNotYetConnected = notYetConnected; 40 | } 41 | 42 | public boolean isConnecting() { 43 | return mConnecting; 44 | } 45 | 46 | public void setConnecting(boolean connecting) { 47 | mConnecting = connecting; 48 | } 49 | 50 | public boolean isOpen() { 51 | return mOpen; 52 | } 53 | 54 | public void setOpen(boolean open) { 55 | mOpen = open; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/com/clusterws/ClusterWSTest.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | import org.java_websocket.WebSocket; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.mockito.internal.matchers.Null; 10 | 11 | import java.util.ArrayList; 12 | 13 | import static org.junit.Assert.*; 14 | 15 | public class ClusterWSTest { 16 | 17 | private ClusterWS mClusterWS; 18 | private Object receivedData; 19 | private boolean gotTheData; 20 | 21 | @Before 22 | public void init() { 23 | mClusterWS = new ClusterWS("ws://localhost:3000"); 24 | receivedData = null; 25 | gotTheData = false; 26 | } 27 | 28 | @After 29 | public void clearSocket() { 30 | mClusterWS.disconnect(null, null); 31 | mClusterWS = null; 32 | } 33 | 34 | @Test 35 | public void connect() throws Exception { 36 | mClusterWS.connect(); 37 | Thread.sleep(1000); 38 | 39 | assertEquals("Socket did not connect", WebSocket.READYSTATE.OPEN, mClusterWS.getState()); 40 | } 41 | 42 | @Test 43 | public void testOnAndSendString() throws Exception { 44 | mClusterWS.connect(); 45 | mClusterWS.on("String", new IEmitterListener() { 46 | @Override 47 | public void onDataReceived(Object data) { 48 | gotTheData = true; 49 | receivedData = data; 50 | } 51 | }); 52 | Thread.sleep(1000); 53 | mClusterWS.send("String", "test message"); 54 | Thread.sleep(1000); 55 | 56 | assertTrue("Did not get the data", gotTheData); 57 | assertEquals("Data send and data received are not the same", "test message", receivedData); 58 | } 59 | 60 | @Test 61 | public void testSendAndOnBoolean() throws Exception { 62 | mClusterWS.connect(); 63 | mClusterWS.on("String", new IEmitterListener() { 64 | @Override 65 | public void onDataReceived(Object data) { 66 | gotTheData = true; 67 | receivedData = data; 68 | } 69 | }); 70 | Thread.sleep(1000); 71 | mClusterWS.send("String", true); 72 | Thread.sleep(1000); 73 | 74 | assertTrue("Did not get the data", gotTheData); 75 | assertTrue("Data send and data received are not the same", (Boolean) receivedData); 76 | } 77 | 78 | @Test 79 | public void testSendAndOnInteger() throws Exception { 80 | mClusterWS.connect(); 81 | mClusterWS.on("String", new IEmitterListener() { 82 | @Override 83 | public void onDataReceived(Object data) { 84 | gotTheData = true; 85 | receivedData = data; 86 | } 87 | }); 88 | Thread.sleep(1000); 89 | mClusterWS.send("String", 30); 90 | Thread.sleep(1000); 91 | 92 | assertTrue("Did not get the data", gotTheData); 93 | assertEquals("Data send and data received are not the same", 30, (int) receivedData); 94 | } 95 | 96 | @Test 97 | public void testSendAndOnNull() throws Exception { 98 | mClusterWS.connect(); 99 | mClusterWS.on("String", new IEmitterListener() { 100 | @Override 101 | public void onDataReceived(Object data) { 102 | gotTheData = true; 103 | if (data.equals(null)) { 104 | receivedData = null; 105 | } 106 | } 107 | }); 108 | Thread.sleep(1000); 109 | mClusterWS.send("String", null); 110 | Thread.sleep(1000); 111 | 112 | assertTrue("Did not get the data", gotTheData); 113 | assertNull("Data send and data received are not the same", receivedData); 114 | } 115 | 116 | @Test 117 | public void testSendAndOnObject() throws Exception { 118 | mClusterWS.connect(); 119 | mClusterWS.on("String", new IEmitterListener() { 120 | @Override 121 | public void onDataReceived(Object data) { 122 | gotTheData = true; 123 | receivedData = data; 124 | } 125 | }); 126 | JSONObject jsonObject = new JSONObject(); 127 | jsonObject.put("int", 30); 128 | jsonObject.put("bool", true); 129 | jsonObject.put("string", "CHLEN"); 130 | JSONArray jsonArray = new JSONArray(); 131 | jsonArray.add(30); 132 | jsonArray.add(true); 133 | jsonArray.add("CHLEN"); 134 | jsonObject.put("array", jsonArray); 135 | Thread.sleep(1000); 136 | mClusterWS.send("String", jsonObject); 137 | Thread.sleep(1000); 138 | 139 | assertTrue("Did not get the data", gotTheData); 140 | assertEquals("Data send and data received are not the same", jsonObject.toString(), receivedData.toString()); 141 | } 142 | 143 | @Test 144 | public void testSendAndOnArray() throws Exception { 145 | mClusterWS.connect(); 146 | mClusterWS.on("String", new IEmitterListener() { 147 | @Override 148 | public void onDataReceived(Object data) { 149 | gotTheData = true; 150 | receivedData = data; 151 | } 152 | }); 153 | ArrayList mObjectArrayList; 154 | mObjectArrayList = new ArrayList<>(); 155 | mObjectArrayList.add(30); 156 | mObjectArrayList.add(false); 157 | mObjectArrayList.add("Test message"); 158 | 159 | JSONObject jsonObject = new JSONObject(); 160 | jsonObject.put("int", 30); 161 | jsonObject.put("bool", true); 162 | jsonObject.put("string", "CHLEN"); 163 | JSONArray jsonArray = new JSONArray(); 164 | jsonArray.add(30); 165 | jsonArray.add(true); 166 | jsonArray.add("CHLEN"); 167 | jsonObject.put("array", jsonArray); 168 | 169 | mObjectArrayList.add(jsonObject); 170 | Thread.sleep(1000); 171 | mClusterWS.send("String", mObjectArrayList.toString()); 172 | Thread.sleep(1000); 173 | 174 | assertTrue("Did not get the data", gotTheData); 175 | assertEquals("Data send and data received are not the same", mObjectArrayList.toString(), receivedData.toString()); 176 | } 177 | 178 | @Test 179 | public void testDisconnect() throws Exception { 180 | mClusterWS.connect(); 181 | Thread.sleep(1000); 182 | mClusterWS.disconnect(null, null); 183 | Thread.sleep(1000); 184 | assertEquals(WebSocket.READYSTATE.CLOSED, mClusterWS.getState()); 185 | } 186 | 187 | @Test 188 | public void testAllGetStates() throws Exception { 189 | final com.clusterws.ClusterWSStatesBool clusterWSStatesBool = new com.clusterws.ClusterWSStatesBool(); 190 | if (mClusterWS.getState() == WebSocket.READYSTATE.NOT_YET_CONNECTED) { 191 | clusterWSStatesBool.setNotYetConnected(true); 192 | } 193 | 194 | mClusterWS.setClusterWSListener(new IClusterWSListener() { 195 | @Override 196 | public void onConnected() { 197 | if (mClusterWS.getState() == WebSocket.READYSTATE.OPEN) { 198 | clusterWSStatesBool.setOpen(true); 199 | } 200 | } 201 | 202 | @Override 203 | public void onError(Exception exception) { 204 | 205 | } 206 | 207 | @Override 208 | public void onDisconnected(int code, String reason) { 209 | 210 | } 211 | }); 212 | 213 | mClusterWS.connect(); 214 | if (mClusterWS.getState() == WebSocket.READYSTATE.CONNECTING) { 215 | clusterWSStatesBool.setConnecting(true); 216 | } 217 | Thread.sleep(1000); 218 | mClusterWS.disconnect(1000, "Test"); 219 | if (mClusterWS.getState() == WebSocket.READYSTATE.CLOSING) { 220 | clusterWSStatesBool.setClosing(true); 221 | } 222 | Thread.sleep(1000); 223 | if (mClusterWS.getState() == WebSocket.READYSTATE.CLOSED) { 224 | clusterWSStatesBool.setClosed(true); 225 | } 226 | 227 | assertTrue("State CREATED not working", clusterWSStatesBool.isNotYetConnected()); 228 | //TODO Сложно поймать этот state 229 | // assertTrue("State CONNECTING not working",clusterWSStatesBool.isConnecting()); 230 | assertTrue("State OPEN not working", clusterWSStatesBool.isOpen()); 231 | assertTrue("State CLOSING not working", clusterWSStatesBool.isClosing()); 232 | assertTrue("State CLOSED not working", clusterWSStatesBool.isClosed()); 233 | } 234 | 235 | @Test 236 | public void testReconnection() throws Exception { 237 | mClusterWS.setReconnection(true, 1000, 2000, null); 238 | mClusterWS.connect(); 239 | Thread.sleep(1000); 240 | mClusterWS.disconnect(3002, "test"); 241 | Thread.sleep(4000); 242 | assertEquals("Did not reconnect", WebSocket.READYSTATE.OPEN, mClusterWS.getState()); 243 | } 244 | 245 | @Test 246 | public void testPingPong() throws Exception { 247 | mClusterWS.connect(); 248 | Thread.sleep(1900); 249 | assertEquals("Websocket disconnected", WebSocket.READYSTATE.OPEN, mClusterWS.getState()); 250 | } 251 | 252 | @Test(expected = NullPointerException.class) 253 | public void testNullUrl() throws Exception { 254 | mClusterWS = new ClusterWS(null); 255 | } 256 | 257 | } -------------------------------------------------------------------------------- /src/test/java/com/clusterws/EmitterTest.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | public class EmitterTest { 10 | private boolean gotMessage; 11 | private Emitter mEmitter; 12 | 13 | @Before 14 | public void init(){ 15 | mEmitter = new Emitter(); 16 | gotMessage = false; 17 | } 18 | 19 | @After 20 | public void deleteEmitter(){ 21 | mEmitter = null; 22 | } 23 | 24 | @Test 25 | public void addEventListenerAndEmit() throws Exception { 26 | mEmitter.addEventListener("test", new IEmitterListener() { 27 | @Override 28 | public void onDataReceived(Object data) { 29 | gotMessage = true; 30 | } 31 | }); 32 | mEmitter.emit("test","HUI"); 33 | assertTrue(gotMessage); 34 | } 35 | 36 | @Test 37 | public void addDifferentEventListenersWithTheSameEventName() throws Exception{ 38 | mEmitter.addEventListener("test", new IEmitterListener() { 39 | @Override 40 | public void onDataReceived(Object data) { 41 | gotMessage = false; 42 | } 43 | }); 44 | mEmitter.addEventListener("test", new IEmitterListener() { 45 | @Override 46 | public void onDataReceived(Object data) { 47 | gotMessage = true; 48 | } 49 | }); 50 | mEmitter.emit("test","HUI"); 51 | assertTrue(gotMessage); 52 | } 53 | 54 | @Test 55 | public void removeAllEvents() throws Exception { 56 | mEmitter.addEventListener("test", new IEmitterListener() { 57 | @Override 58 | public void onDataReceived(Object data) { 59 | gotMessage = true; 60 | } 61 | }); 62 | mEmitter.removeAllEvents(); 63 | mEmitter.emit("test","HUI"); 64 | assertFalse(gotMessage); 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /src/test/java/com/clusterws/MessageHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.util.ArrayList; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | public class MessageHandlerTest { 13 | private MessageHandler mMessageHandler; 14 | private String mResult; 15 | 16 | @Before 17 | public void init() { 18 | mMessageHandler = new MessageHandler(); 19 | mResult = null; 20 | } 21 | 22 | @Test 23 | public void messageEncodeEmitString() throws Exception { 24 | mResult = mMessageHandler.messageEncode("test", "testString", "emit"); 25 | assertEquals("{\"#\":[\"e\",\"test\",\"testString\"]}", mResult); 26 | } 27 | 28 | @Test 29 | public void messageEncodeEmitInt() throws Exception { 30 | mResult = mMessageHandler.messageEncode("test", 20, "emit"); 31 | assertEquals("{\"#\":[\"e\",\"test\",20]}", mResult); 32 | } 33 | 34 | @Test 35 | public void messageEncodeEmitBoolean() throws Exception { 36 | mResult = mMessageHandler.messageEncode("test", true, "emit"); 37 | assertEquals("{\"#\":[\"e\",\"test\",true]}", mResult); 38 | } 39 | 40 | @Test 41 | public void messageEncodeEmitNull() throws Exception { 42 | mResult = mMessageHandler.messageEncode("test", null, "emit"); 43 | assertEquals("{\"#\":[\"e\",\"test\",null]}", mResult); 44 | } 45 | 46 | @Test 47 | public void messageEncodeEmitObject() throws Exception { 48 | JSONObject jsonObject = new JSONObject(); 49 | jsonObject.put("int", 30); 50 | jsonObject.put("bool", true); 51 | jsonObject.put("string", "CHLEN"); 52 | JSONArray jsonArray = new JSONArray(); 53 | jsonArray.add(30); 54 | jsonArray.add(true); 55 | jsonArray.add("CHLEN"); 56 | jsonObject.put("array", jsonArray); 57 | mResult = mMessageHandler.messageEncode("test", jsonObject, "emit"); 58 | assertEquals("{\"#\":[\"e\",\"test\",{\"bool\":true,\"string\":\"CHLEN\",\"array\":[30,true,\"CHLEN\"],\"int\":30}]}", mResult); 59 | } 60 | 61 | 62 | @Test 63 | public void messageEncodeEmitArray() throws Exception { 64 | JSONObject jsonObject = new JSONObject(); 65 | jsonObject.put("int", 30); 66 | jsonObject.put("bool", true); 67 | jsonObject.put("string", "CHLEN"); 68 | JSONArray jsonArray = new JSONArray(); 69 | jsonArray.add(30); 70 | jsonArray.add(true); 71 | jsonArray.add("CHLEN"); 72 | jsonObject.put("array", jsonArray); 73 | 74 | ArrayList arrayList = new ArrayList<>(); 75 | arrayList.add(30); 76 | arrayList.add(false); 77 | arrayList.add("Test message"); 78 | arrayList.add(jsonObject); 79 | mResult = mMessageHandler.messageEncode("test", arrayList, "emit"); 80 | assertEquals("{\"#\":[\"e\",\"test\",[30,false,\"Test message\",{\"bool\":true,\"string\":\"CHLEN\",\"array\":[30,true,\"CHLEN\"],\"int\":30}]]}", mResult); 81 | } 82 | 83 | @Test 84 | public void messageEncodeSystemSubscribe() throws Exception{ 85 | mResult = mMessageHandler.messageEncode("subscribe","channelName","system"); 86 | assertEquals("{\"#\":[\"s\",\"s\",\"channelName\"]}",mResult); 87 | } 88 | 89 | @Test 90 | public void messageEncodeSystemUnsubscribe() throws Exception{ 91 | mResult = mMessageHandler.messageEncode("unsubscribe","channelName","system"); 92 | assertEquals("{\"#\":[\"s\",\"u\",\"channelName\"]}",mResult); 93 | } 94 | @Test 95 | public void messageEncodePing() throws Exception{ 96 | mResult = mMessageHandler.messageEncode("#1",null,"ping"); 97 | assertEquals("#1",mResult); 98 | } 99 | 100 | @Test 101 | public void messageEncodePublishString(){ 102 | mResult = mMessageHandler.messageEncode("channelname", "testData", "publish"); 103 | assertEquals("{\"#\":[\"p\",\"channelname\",\"testData\"]}", mResult); 104 | } 105 | 106 | @Test 107 | public void messageEncodePublishInt() throws Exception { 108 | mResult = mMessageHandler.messageEncode("channelname", 20, "publish"); 109 | assertEquals("{\"#\":[\"p\",\"channelname\",20]}", mResult); 110 | 111 | } 112 | 113 | @Test 114 | public void messageEncodePublishBoolean() throws Exception { 115 | mResult = mMessageHandler.messageEncode("channelname", true, "publish"); 116 | assertEquals("{\"#\":[\"p\",\"channelname\",true]}", mResult); 117 | 118 | } 119 | 120 | @Test 121 | public void messageEncodePublishNull() throws Exception { 122 | mResult = mMessageHandler.messageEncode("channelname", null, "publish"); 123 | assertEquals("{\"#\":[\"p\",\"channelname\",null]}", mResult); 124 | 125 | } 126 | 127 | @Test 128 | public void messageEncodePublishObject() throws Exception { 129 | JSONObject jsonObject = new JSONObject(); 130 | jsonObject.put("int", 30); 131 | jsonObject.put("bool", true); 132 | jsonObject.put("string", "CHLEN"); 133 | JSONArray jsonArray = new JSONArray(); 134 | jsonArray.add(30); 135 | jsonArray.add(true); 136 | jsonArray.add("CHLEN"); 137 | jsonObject.put("array", jsonArray); 138 | mResult = mMessageHandler.messageEncode("channelname", jsonObject, "publish"); 139 | assertEquals("{\"#\":[\"p\",\"channelname\",{\"bool\":true,\"string\":\"CHLEN\",\"array\":[30,true,\"CHLEN\"],\"int\":30}]}", mResult); 140 | } 141 | 142 | 143 | @Test 144 | public void messageEncodePublishArray() throws Exception { 145 | JSONObject jsonObject = new JSONObject(); 146 | jsonObject.put("int", 30); 147 | jsonObject.put("bool", true); 148 | jsonObject.put("string", "CHLEN"); 149 | JSONArray jsonArray = new JSONArray(); 150 | jsonArray.add(30); 151 | jsonArray.add(true); 152 | jsonArray.add("CHLEN"); 153 | jsonObject.put("array", jsonArray); 154 | 155 | ArrayList arrayList = new ArrayList<>(); 156 | arrayList.add(30); 157 | arrayList.add(false); 158 | arrayList.add("Test message"); 159 | arrayList.add(jsonObject); 160 | mResult = mMessageHandler.messageEncode("channelname", arrayList, "publish"); 161 | assertEquals("{\"#\":[\"p\",\"channelname\",[30,false,\"Test message\",{\"bool\":true,\"string\":\"CHLEN\",\"array\":[30,true,\"CHLEN\"],\"int\":30}]]}", mResult); 162 | } 163 | } -------------------------------------------------------------------------------- /src/test/java/com/clusterws/PingHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.clusterws; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.*; 7 | 8 | public class PingHandlerTest { 9 | 10 | private PingHandler mPingHandler; 11 | 12 | @Before 13 | public void init(){ 14 | mPingHandler = new PingHandler(); 15 | } 16 | 17 | @Test 18 | public void incrementMissedPing() throws Exception { 19 | mPingHandler.incrementMissedPing(); 20 | assertEquals(1,mPingHandler.getMissedPing()); 21 | } 22 | 23 | @Test 24 | public void setMissedPingToZero() throws Exception { 25 | mPingHandler.incrementMissedPing(); 26 | mPingHandler.setMissedPingToZero(); 27 | assertEquals(0,mPingHandler.getMissedPing()); 28 | } 29 | 30 | 31 | @Test 32 | public void getMissedPing() throws Exception { 33 | assertEquals(0,mPingHandler.getMissedPing()); 34 | } 35 | 36 | } --------------------------------------------------------------------------------