├── .gitignore ├── .gradle └── 2.2 │ └── taskArtifacts │ ├── cache.properties │ ├── cache.properties.lock │ ├── fileHashes.bin │ ├── fileSnapshots.bin │ ├── outputFileStates.bin │ └── taskArtifacts.bin ├── README.en.md ├── README.md ├── build.gradle ├── build ├── publications │ └── mavenJava │ │ └── pom-default.xml └── tmp │ └── jar │ └── MANIFEST.MF ├── factories └── META-INF │ └── spring.factories ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src └── main │ └── java │ └── javagrinko │ └── spring │ ├── starter │ ├── TcpServerAutoConfiguration.java │ └── TcpServerProperties.java │ └── tcp │ ├── Connection.java │ ├── Server.java │ ├── TcpConnection.java │ ├── TcpController.java │ ├── TcpControllerBeanPostProcessor.java │ ├── TcpServer.java │ └── TcpServerAutoStarterApplicationListener.java └── version.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 2 | 3 | *.iml 4 | 5 | ## Directory-based project format: 6 | .idea/ 7 | # if you remove the above rule, at least ignore the following: 8 | 9 | # User-specific stuff: 10 | # .idea/workspace.xml 11 | # .idea/tasks.xml 12 | # .idea/dictionaries 13 | 14 | # Sensitive or high-churn files: 15 | # .idea/dataSources.ids 16 | # .idea/dataSources.xml 17 | # .idea/sqlDataSources.xml 18 | # .idea/dynamic.xml 19 | # .idea/uiDesigner.xml 20 | 21 | # Gradle: 22 | # .idea/gradle.xml 23 | # .idea/libraries 24 | 25 | # Mongo Explorer plugin: 26 | # .idea/mongoSettings.xml 27 | 28 | ## File-based project format: 29 | *.ipr 30 | *.iws 31 | 32 | ## Plugin-specific files: 33 | 34 | # IntelliJ 35 | /out/ 36 | 37 | # mpeltonen/sbt-idea plugin 38 | .idea_modules/ 39 | 40 | # JIRA plugin 41 | atlassian-ide-plugin.xml 42 | 43 | # Crashlytics plugin (for Android Studio and IntelliJ) 44 | com_crashlytics_export_strings.xml 45 | crashlytics.properties 46 | crashlytics-build.properties -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/cache.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 23 14:12:31 MSK 2015 2 | -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/cache.properties.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaGrinko/tcp-spring-boot-starter/e140741554429cc7b5502e8543b35606c2bfc182/.gradle/2.2/taskArtifacts/cache.properties.lock -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaGrinko/tcp-spring-boot-starter/e140741554429cc7b5502e8543b35606c2bfc182/.gradle/2.2/taskArtifacts/fileHashes.bin -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/fileSnapshots.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaGrinko/tcp-spring-boot-starter/e140741554429cc7b5502e8543b35606c2bfc182/.gradle/2.2/taskArtifacts/fileSnapshots.bin -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/outputFileStates.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaGrinko/tcp-spring-boot-starter/e140741554429cc7b5502e8543b35606c2bfc182/.gradle/2.2/taskArtifacts/outputFileStates.bin -------------------------------------------------------------------------------- /.gradle/2.2/taskArtifacts/taskArtifacts.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaGrinko/tcp-spring-boot-starter/e140741554429cc7b5502e8543b35606c2bfc182/.gradle/2.2/taskArtifacts/taskArtifacts.bin -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | #### [Просмотр русской версии этого файла здесь](./README.md) 2 | 3 | ## Description 4 | **tcp-spring-boot-starter** - Spring Boot library that allows you to quickly deploy a TCP server. Includes: 5 | * **@TcpController** - Annotation marking a class as a TCP controller, 6 | * **ServerUtils** - bean, with which you can manage the server. 7 | 8 | The controller can contain three types of methods: 9 | * The event-receiving method of the message. Must start with the word `receive` and have two arguments: `Connection` and `Object` (or any other, then the receive method will be typed). 10 | * New connection method-event. Must start with connect and have the `Connection` argument 11 | * Client-disconnect method-event. Must start with the word `disconnect` and have the `Connection` argument 12 | 13 | ## Examples 14 | ```java 15 | import javagrinko.spring.tcp.Connection; 16 | import javagrinko.spring.tcp.TcpController; 17 | 18 | @TcpController 19 | public class EchoController { 20 | 21 | public void receiveData(Connection connection, byte[] data) { 22 | String s = new String(data); 23 | connection.send(s.toUpperCase().getBytes()); 24 | } 25 | 26 | public void connect(Connection connection) { 27 | System.out.println("New connection " + connection.getAddress().getCanonicalHostName()); 28 | } 29 | 30 | public void disconnect(Connection connection) { 31 | System.out.println("Disconnect " + connection.getAddress().getCanonicalHostName()); 32 | } 33 | } 34 | ``` 35 | **application.properties**: 36 | ``` 37 | javagrinko.tcp-server.port = 20502 38 | javagrinko.tcp-server.auto-start = true 39 | ``` 40 | 41 | ## Installation 42 | 1) You need to add the dependency to your build.gradle: 43 | ``` 44 | repositories { 45 | maven { 46 | url "http://jcenter.bintray.com" 47 | } 48 | } 49 | 50 | dependencies { 51 | compile 'javagrinko:tcp-spring-boot-starter:1.10' 52 | ... 53 | } 54 | 55 | ``` 56 | 57 | 2) In the file `src/main/resources/application.properties` set the server settings: 58 | ``` 59 | #Server Port 60 | javagrinko.tcp-server.port = 20502 61 | #Autostart server after loading application context 62 | javagrinko.tcp-server.auto-start = true 63 | ``` 64 | 65 | If `autostart` is not set or set to **false**, you must manually start the server from the code: 66 | ```java 67 | @Component 68 | public class Starter { 69 | @Autowired 70 | ServerUtils serverUtils; 71 | 72 | @PostConstruct 73 | void setUp(){ 74 | Server server = serverUtils.getServer(); 75 | server.setPort(20502); 76 | server.start(); 77 | } 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### [View English version of this file here](./README.en.md) 2 | 3 | ## Описание 4 | 5 | **tcp-spring-boot-starter** - Spring Boot библиотека, которая позволяет быстро разворачивать TCP-сервера. Включает в себя: 6 | * **@TcpController** - аннотация, помечающая класс как TCP-контроллер, 7 | * **ServerUtils** - бин, с помощью которого можно управлять сервером. 8 | 9 | Контроллер может содержать три типа методов: 10 | * Метод-событие получения сообщения. Должен начинаться со слова receive и иметь два аргумента: Connection и Object (или любой другой, тогда метод приема будет типизирован). 11 | * Метод-событие нового подключения. Должен начинаться со слова connect и иметь аргумент Connection 12 | * Метод-событие отключения клиента. Должен начинаться со слова disconnect и иметь аргумент Connection 13 | 14 | ## Примеры 15 | ```java 16 | import javagrinko.spring.tcp.Connection; 17 | import javagrinko.spring.tcp.TcpController; 18 | 19 | @TcpController 20 | public class EchoController { 21 | 22 | public void receiveData(Connection connection, byte[] data) { 23 | String s = new String(data); 24 | connection.send(s.toUpperCase().getBytes()); 25 | } 26 | 27 | public void connect(Connection connection) { 28 | System.out.println("New connection " + connection.getAddress().getCanonicalHostName()); 29 | } 30 | 31 | public void disconnect(Connection connection) { 32 | System.out.println("Disconnect " + connection.getAddress().getCanonicalHostName()); 33 | } 34 | } 35 | ``` 36 | **application.properties**: 37 | ``` 38 | javagrinko.tcp-server.port = 20502 39 | javagrinko.tcp-server.auto-start = true 40 | ``` 41 | 42 | ## Установка 43 | 1) Необходимо добавить зависимость в build.gradle: 44 | ``` 45 | repositories { 46 | maven { 47 | url "http://jcenter.bintray.com" 48 | } 49 | } 50 | 51 | dependencies { 52 | compile 'javagrinko:tcp-spring-boot-starter:1.10' 53 | ... 54 | } 55 | 56 | ``` 57 | 58 | 2) в файле src/main/resources/application.properties задать настройки сервера: 59 | ``` 60 | #Порт сервера 61 | javagrinko.tcp-server.port = 20502 62 | #Автостарт сервера после загрузки application context 63 | javagrinko.tcp-server.auto-start = true 64 | ``` 65 | 66 | Если автостарт не задан или установлено значение **false**, то необходимо вручную запустить сервер из кода: 67 | ```java 68 | @Component 69 | public class Starter { 70 | @Autowired 71 | ServerUtils serverUtils; 72 | 73 | @PostConstruct 74 | void setUp(){ 75 | Server server = serverUtils.getServer(); 76 | server.setPort(20502); 77 | server.start(); 78 | } 79 | } 80 | ``` 81 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { url 'http://repo.spring.io/plugins-release' } 5 | } 6 | dependencies { 7 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 8 | classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.7' 9 | } 10 | } 11 | 12 | configure(allprojects) { 13 | apply plugin: 'propdeps' 14 | apply plugin: 'propdeps-maven' 15 | apply plugin: 'propdeps-idea' 16 | apply plugin: 'propdeps-eclipse' 17 | } 18 | 19 | group 'javagrinko' 20 | version '1.0-SNAPSHOT' 21 | 22 | apply plugin: 'java' 23 | apply plugin: 'maven' 24 | apply plugin: "com.jfrog.bintray" 25 | apply plugin: 'maven-publish' 26 | 27 | def props = new Properties() 28 | def file = new File("version.properties") 29 | file.withInputStream { 30 | stream -> props.load(stream) 31 | } 32 | def subver = props.getProperty("subversion", "0") as Integer 33 | def ver = props.getProperty("version", "0") as Integer 34 | //props.setProperty("subversion", (subver + 1) as String) 35 | props.setProperty("subversion", (subver) as String) 36 | props.store(file.newWriter(), null) 37 | version = ver + "." + subver 38 | 39 | repositories { 40 | mavenCentral() 41 | maven { 42 | url "http://dl.bintray.com/javagrinko/maven" 43 | } 44 | } 45 | 46 | dependencies { 47 | compile("org.springframework.boot:spring-boot-starter:1.3.2.RELEASE"); 48 | optional "org.springframework.boot:spring-boot-configuration-processor:1.3.2.RELEASE" 49 | } 50 | 51 | compileJava.dependsOn(processResources) 52 | 53 | jar { 54 | from('/factories/') { 55 | include 'META-INF/spring.factories' 56 | } 57 | } 58 | 59 | 60 | bintray { 61 | user = "javagrinko" 62 | key = "161e314ff8fea76298fcdeb51c8d939995874d11" //this usually comes from gradle.properties file in ~/.gradle 63 | publications = ['mavenJava'] 64 | dryRun = false 65 | publish = true 66 | pkg { 67 | repo = 'maven' 68 | name = 'tcp-spring-boot-starter' 69 | } 70 | } 71 | 72 | task sourcesJar(type: Jar, dependsOn: classes) { 73 | classifier = 'sources' 74 | from sourceSets.main.allSource 75 | } 76 | 77 | publishing { 78 | publications { 79 | mavenJava(MavenPublication) { 80 | from components.java 81 | artifact sourcesJar 82 | } 83 | } 84 | } 85 | 86 | task generateAndPublish{ 87 | println "Ok" 88 | } 89 | 90 | generateAndPublish.dependsOn 'bintrayUpload' 91 | generateAndPublish.dependsOn 'generatePomFileForMavenJavaPublication' 92 | generateAndPublish.dependsOn 'sourcesJar' 93 | generateAndPublish.dependsOn 'jar' 94 | -------------------------------------------------------------------------------- /build/publications/mavenJava/pom-default.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | javagrinko 6 | tcp-spring-boot-starter 7 | 1.10 8 | 9 | 10 | org.springframework.boot 11 | spring-boot-starter 12 | 1.3.2.RELEASE 13 | runtime 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-configuration-processor 18 | 1.3.2.RELEASE 19 | runtime 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /build/tmp/jar/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | 3 | -------------------------------------------------------------------------------- /factories/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=javagrinko.spring.starter.TcpServerAutoConfiguration -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JavaGrinko/tcp-spring-boot-starter/e140741554429cc7b5502e8543b35606c2bfc182/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 23 14:12:31 MSK 2015 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-2.2-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'tcp-spring-boot-starter' -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/starter/TcpServerAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.starter; 2 | 3 | import javagrinko.spring.tcp.Server; 4 | import javagrinko.spring.tcp.TcpControllerBeanPostProcessor; 5 | import javagrinko.spring.tcp.TcpServer; 6 | import javagrinko.spring.tcp.TcpServerAutoStarterApplicationListener; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | @EnableConfigurationProperties(TcpServerProperties.class) 14 | @ConditionalOnProperty(prefix = "javagrinko.tcp-server", name = {"port", "auto-start"}) 15 | public class TcpServerAutoConfiguration { 16 | 17 | @Bean 18 | TcpServerAutoStarterApplicationListener tcpServerAutoStarterApplicationListener() { 19 | return new TcpServerAutoStarterApplicationListener(); 20 | } 21 | 22 | @Bean 23 | TcpControllerBeanPostProcessor tcpControllerBeanPostProcessor() { 24 | return new TcpControllerBeanPostProcessor(); 25 | } 26 | 27 | @Bean 28 | Server server(){ 29 | return new TcpServer(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/starter/TcpServerProperties.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.starter; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(prefix = "javagrinko.tcp-server") 6 | public class TcpServerProperties { 7 | 8 | private int port; 9 | 10 | private boolean autoStart; 11 | 12 | public int getPort() { 13 | return port; 14 | } 15 | 16 | public void setPort(int port) { 17 | this.port = port; 18 | } 19 | 20 | public boolean getAutoStart() { 21 | return autoStart; 22 | } 23 | 24 | public void setAutoStart(boolean autoStart) { 25 | this.autoStart = autoStart; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/tcp/Connection.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.tcp; 2 | 3 | import java.net.InetAddress; 4 | 5 | public interface Connection { 6 | InetAddress getAddress(); 7 | void send(Object objectToSend); 8 | void addListener(Listener listener); 9 | void start(); 10 | void close(); 11 | 12 | interface Listener { 13 | void messageReceived(Connection connection, Object message); 14 | void connected(Connection connection); 15 | void disconnected(Connection connection); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/tcp/Server.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.tcp; 2 | 3 | 4 | import java.util.List; 5 | 6 | public interface Server { 7 | int getConnectionsCount(); 8 | void setPort(Integer port); 9 | void start(); 10 | void stop(); 11 | List getConnections(); 12 | void addListener(Connection.Listener listener); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/tcp/TcpConnection.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.tcp; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.InetAddress; 7 | import java.net.Socket; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | public class TcpConnection implements Connection { 13 | private InputStream inputStream; 14 | private OutputStream outputStream; 15 | private Socket socket; 16 | private List listeners = new ArrayList<>(); 17 | 18 | public TcpConnection(Socket socket) { 19 | this.socket = socket; 20 | try { 21 | inputStream = socket.getInputStream(); 22 | outputStream = socket.getOutputStream(); 23 | } catch (IOException e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | 28 | @Override 29 | public InetAddress getAddress() { 30 | return socket.getInetAddress(); 31 | } 32 | 33 | @Override 34 | public void send(Object objectToSend) { 35 | if (objectToSend instanceof byte[]) { 36 | byte[] data = (byte[]) objectToSend; 37 | try { 38 | outputStream.write(data); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | } 44 | 45 | @Override 46 | public void addListener(Listener listener) { 47 | listeners.add(listener); 48 | } 49 | 50 | @Override 51 | public void start() { 52 | new Thread(() -> { 53 | while (true) { 54 | byte buf[] = new byte[64 * 1024]; 55 | try { 56 | int count = inputStream.read(buf); 57 | if (count > 0) { 58 | byte[] bytes = Arrays.copyOf(buf, count); 59 | for (Listener listener : listeners) { 60 | listener.messageReceived(this, bytes); 61 | } 62 | } else { 63 | socket.close(); 64 | for (Listener listener : listeners) { 65 | listener.disconnected(this); 66 | } 67 | break; 68 | } 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | for (Listener listener : listeners) { 72 | listener.disconnected(this); 73 | } 74 | break; 75 | } 76 | } 77 | }).start(); 78 | } 79 | 80 | @Override 81 | public void close() { 82 | try { 83 | socket.close(); 84 | } catch (IOException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/tcp/TcpController.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.tcp; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Component 10 | public @interface TcpController { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/tcp/TcpControllerBeanPostProcessor.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.tcp; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.config.BeanPostProcessor; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | @Component 16 | public class TcpControllerBeanPostProcessor implements BeanPostProcessor { 17 | private Map map = new HashMap<>(); 18 | 19 | @Autowired 20 | private Server server; 21 | 22 | @Override 23 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 24 | Class beanClass = bean.getClass(); 25 | if (beanClass.isAnnotationPresent(TcpController.class)) { 26 | map.put(beanName, beanClass); 27 | } 28 | return bean; 29 | } 30 | 31 | @Override 32 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 33 | if (map.containsKey(beanName)) { 34 | List receiveMethods = new ArrayList<>(); 35 | List connectMethods = new ArrayList<>(); 36 | List disconnectMethods = new ArrayList<>(); 37 | Method[] methods = bean.getClass().getMethods(); 38 | for (Method method : methods) { 39 | if (method.getName().startsWith("receive") && method.getParameterCount() == 2 40 | && method.getParameterTypes()[0] == Connection.class) { 41 | receiveMethods.add(method); 42 | } else if (method.getName().startsWith("connect") && method.getParameterCount() == 1 43 | && method.getParameterTypes()[0] == Connection.class) { 44 | connectMethods.add(method); 45 | } else if (method.getName().startsWith("disconnect") && method.getParameterCount() == 1 46 | && method.getParameterTypes()[0] == Connection.class) { 47 | disconnectMethods.add(method); 48 | } 49 | } 50 | 51 | 52 | server.addListener(new Connection.Listener() { 53 | @Override 54 | public void messageReceived(Connection connection, Object message) { 55 | for (Method receiveMethod : receiveMethods) { 56 | Class aClass = receiveMethod.getParameterTypes()[1]; 57 | if (message.getClass().isAssignableFrom(aClass)) { 58 | try { 59 | receiveMethod.invoke(bean, connection, message); 60 | } catch (IllegalAccessException e) { 61 | e.printStackTrace(); 62 | } catch (InvocationTargetException e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | public void connected(Connection connection) { 71 | for (Method connectMethod : connectMethods) { 72 | try { 73 | connectMethod.invoke(bean, connection); 74 | } catch (IllegalAccessException e) { 75 | e.printStackTrace(); 76 | } catch (InvocationTargetException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | } 81 | 82 | @Override 83 | public void disconnected(Connection connection) { 84 | for (Method disconnectMethod : disconnectMethods) { 85 | try { 86 | disconnectMethod.invoke(bean, connection); 87 | } catch (IllegalAccessException e) { 88 | e.printStackTrace(); 89 | } catch (InvocationTargetException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | } 94 | }); 95 | } 96 | return bean; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/tcp/TcpServer.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.tcp; 2 | 3 | import org.apache.commons.logging.Log; 4 | import org.apache.commons.logging.LogFactory; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.io.IOException; 8 | import java.net.ServerSocket; 9 | import java.net.Socket; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | @Component 14 | public class TcpServer implements Server, Connection.Listener { 15 | private static Log logger = LogFactory.getLog(TcpServer.class); 16 | 17 | private ServerSocket serverSocket; 18 | private volatile boolean isStop; 19 | private List connections = new ArrayList<>(); 20 | private List listeners = new ArrayList<>(); 21 | 22 | public void setPort(Integer port) { 23 | try { 24 | if (port == null) { 25 | logger.info("Property tcp.server.port not found. Use default port 1234"); 26 | port = 1234; 27 | } 28 | serverSocket = new ServerSocket(port); 29 | logger.info("Server start at port " + port); 30 | } catch (IOException e) { 31 | e.printStackTrace(); 32 | logger.error("May be port " + port + " busy."); 33 | } 34 | } 35 | 36 | @Override 37 | public int getConnectionsCount() { 38 | return connections.size(); 39 | } 40 | 41 | @Override 42 | public void start() { 43 | new Thread(() -> { 44 | while (!isStop) { 45 | try { 46 | Socket socket = serverSocket.accept(); 47 | if (socket.isConnected()) { 48 | TcpConnection tcpConnection = new TcpConnection(socket); 49 | tcpConnection.start(); 50 | tcpConnection.addListener(this); 51 | connected(tcpConnection); 52 | } 53 | } catch (IOException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | }).start(); 58 | } 59 | 60 | @Override 61 | public void stop() { 62 | isStop = true; 63 | } 64 | 65 | @Override 66 | public List getConnections() { 67 | return connections; 68 | } 69 | 70 | @Override 71 | public void addListener(Connection.Listener listener) { 72 | listeners.add(listener); 73 | } 74 | 75 | @Override 76 | public void messageReceived(Connection connection, Object message) { 77 | logger.trace("Received new message from " + connection.getAddress().getCanonicalHostName()); 78 | logger.trace("Class name: " + message.getClass().getCanonicalName() + ", toString: " + message.toString()); 79 | for (Connection.Listener listener : listeners) { 80 | listener.messageReceived(connection, message); 81 | } 82 | } 83 | 84 | @Override 85 | public void connected(Connection connection) { 86 | logger.info("New connection! Ip: " + connection.getAddress().getCanonicalHostName() + "."); 87 | connections.add(connection); 88 | logger.info("Current connections count: " + connections.size()); 89 | for (Connection.Listener listener : listeners) { 90 | listener.connected(connection); 91 | } 92 | } 93 | 94 | @Override 95 | public void disconnected(Connection connection) { 96 | logger.info("Disconnect! Ip: " + connection.getAddress().getCanonicalHostName() + "."); 97 | connections.remove(connection); 98 | logger.info("Current connections count: " + connections.size()); 99 | for (Connection.Listener listener : listeners) { 100 | listener.disconnected(connection); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/javagrinko/spring/tcp/TcpServerAutoStarterApplicationListener.java: -------------------------------------------------------------------------------- 1 | package javagrinko.spring.tcp; 2 | 3 | import javagrinko.spring.starter.TcpServerProperties; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.ApplicationListener; 6 | import org.springframework.context.event.ContextRefreshedEvent; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class TcpServerAutoStarterApplicationListener implements ApplicationListener { 11 | 12 | @Autowired 13 | private TcpServerProperties properties; 14 | 15 | @Autowired 16 | private Server server; 17 | 18 | @Override 19 | public void onApplicationEvent(ContextRefreshedEvent event) { 20 | boolean autoStart = properties.getAutoStart(); 21 | if (autoStart){ 22 | server.setPort(properties.getPort()); 23 | server.start(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /version.properties: -------------------------------------------------------------------------------- 1 | #Mon Mar 12 15:36:17 PDT 2018 2 | version=1 3 | subversion=10 4 | --------------------------------------------------------------------------------