├── .gitignore ├── .gitreview ├── .travis.yml ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle ├── .gradle │ └── 2.1 │ │ └── taskArtifacts │ │ ├── cache.properties │ │ ├── cache.properties.lock │ │ ├── fileHashes.bin │ │ ├── fileSnapshots.bin │ │ ├── outputFileStates.bin │ │ └── taskArtifacts.bin ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sona.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew └── src ├── main └── java │ └── com │ └── cyngn │ └── vertx │ ├── async │ ├── Action.java │ ├── Latch.java │ ├── ResultContext.java │ └── promise │ │ ├── Promise.java │ │ ├── PromiseAction.java │ │ ├── PromiseFactory.java │ │ └── PromiseImpl.java │ ├── client │ ├── ServiceClient.java │ └── ServiceRequest.java │ ├── eventbus │ └── EventBusTools.java │ ├── validation │ └── ValidationResult.java │ └── web │ ├── HttpHelper.java │ ├── JsonUtil.java │ ├── RestApi.java │ ├── RouterTools.java │ └── handler │ └── RequestIdResponseHandler.java └── test └── java └── com └── cyngn └── vertx ├── async ├── LatchTests.java └── promise │ └── PromiseImplTests.java ├── client └── ServiceClientTest.java ├── eventbus └── EventBusToolsTest.java └── web ├── HttpHelperTest.java ├── JsonUtilTest.java ├── RouterToolsTest.java └── handler └── RequestIdResponseHandlerTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | out 3 | atlassian-ide-plugin.xml 4 | build 5 | *.iml 6 | *.ipr 7 | *.iws 8 | *.DS_Store 9 | /bin/ 10 | .classpath 11 | .project 12 | .settings/ 13 | .idea 14 | gradle.properties 15 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.cyanogenmod.org 3 | port=29418 4 | project=cyngn/vertx-util 5 | defaultbranch=master 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 Cyanogen Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/cyngn/vertx-util.svg?branch=master)](https://travis-ci.org/jtruelove/vertx-util) 2 | 3 | # vertx-util 4 | 5 | General purpose utils & apis for interacting with vert.x 6 | 7 | ## Getting Started 8 | 9 | Add a dependency to vertx-util: 10 | 11 | ```xml 12 | 13 | com.cyngn.vertx 14 | vertx-util 15 | 3.3.0-SNAPSHOT 16 | 17 | ``` 18 | 19 | | vertx-util | vert.x Version | 20 | | ------- | --------------:| 21 | | 3.3.0-SNAPSHOT | 3.3.0-SNAPSHOT | 22 | | 0.6.0 | 3.2.0 | 23 | 24 | ## Promises 25 | 26 | Light weight promises that run on the vertx event loop. This allows developers to easily coordinate running a number of callbacks in parallel or serially while getting notifications of results or exceptions. Also there is a JsonObject that is supplied to enable passing of information between callbacks as well as to the exception or done handlers. 27 | 28 | ### Basic Example 29 | 30 | ```java 31 | // pass it the ref to your current vertx event loop. 32 | Promise.newInstance(vertx) 33 | .then((context, onResult) -> { 34 | // do some stuff 35 | context.put("result", "some text to share"); 36 | onResult.accept(true); 37 | }) 38 | .then((context, onResult) -> onResult.accept(context.containsKey("result"))) 39 | // optional exception handler, when a promise calls onResult.accept(false) or a callback throws an exception 40 | .except((context) -> System.out.println("Failure: " + context.encode())) 41 | // optional completion handler called when all callbacks have run and succeeded 42 | .done((context) -> System.out.println("Success: " + context.encode())) 43 | // optionally set a timeout in ms for the callback chain to complete in 44 | .timeout(3000) 45 | // you are required to call this once and only once to make the promise chain begin to evaluate 46 | .eval(); 47 | ``` 48 | 49 | ### Callbacks in Parallel 50 | 51 | ```java 52 | // pass it the ref to your current vertx event loop, 53 | Promise.newInstance(vertx) 54 | // these can complete in a different order than they are added 55 | .all((context, onResult) -> { 56 | System.out.println("Also 'all' call 1"); 57 | onResult.accept(true); 58 | }, 59 | (context, onResult) -> { 60 | System.out.println("Also 'all' call 2"); 61 | onResult.accept(true); 62 | }) 63 | .done((context) -> System.out.println("Success")) 64 | // you are required to call this once and only once to make the promise chain begin to evaluate 65 | .eval(); 66 | ``` 67 | 68 | ### Callbacks Serially then in Parallel 69 | 70 | ```java 71 | // pass it the ref to your current vertx event loop, 72 | Promise.newInstance(vertx) 73 | .then((context, onResult) -> { 74 | System.out.println("Start here"); 75 | onResult.accept(true); 76 | }) 77 | .then((context, onResult) -> { 78 | System.out.println("Continue here"); 79 | onResult.accept(true); 80 | }) 81 | .all((context, onResult) -> { 82 | System.out.println("Starting something else"); 83 | vertx.executeBlocking(future -> { 84 | try { 85 | Thread.sleep(1000); 86 | } catch (Exception ex) {} 87 | future.complete(); 88 | }, asyncResult -> { 89 | System.out.println("'all' call 1"); 90 | onResult.accept(true); 91 | }); 92 | }, (context, onResult) -> { 93 | System.out.println("'all' call 2"); 94 | onResult.accept(true); 95 | }) 96 | .done((context) -> System.out.println("Success")) 97 | // you are required to call this once and only once to make the promise chain begin to evaluate 98 | .eval(); 99 | ``` 100 | 101 | ### Promise Factory 102 | 103 | There's a promise factory supplied that allows you to set the vertx instance once and generate Promises on demand without having to keep your vertx reference around. 104 | 105 | ```java 106 | PromiseFactory factory = new PromiseFactory(vertx); 107 | // Promise 1 108 | factory.create().then((context, onResult) -> { 109 | System.out.println("a new promise"); 110 | onResult.accept(true); 111 | }).eval(); 112 | 113 | // Promise 2 114 | factory.createParallel((context, onResult) -> { 115 | System.out.println("a test"); 116 | onResult.accept(true); 117 | },(context, onResult) -> { 118 | System.out.println("a test 2"); 119 | onResult.accept(true); 120 | }).eval(); 121 | ``` 122 | 123 | ### Things to Remember 124 | 125 | * you must call `eval()` after creating your chain 126 | * you must call the `onResult` callback with true or false to continue the chain processing 127 | * in the case of a timeout the exception handler is called 128 | 129 | ## Latches 130 | These offer a way to coordinate an action after `N` events have completed just using the vert.x event loop and no additional threads. 131 | 132 | ```java 133 | // this callback will fire after complete has been called on the latch twice 134 | Latch latch = new Latch(2, () -> System.out.println("I'm all done now")); 135 | 136 | // call #1 137 | vertx.executeBlocking(future -> { 138 | // something expensive like a DB call 139 | future.complete(); 140 | }, result -> latch.complete()); 141 | 142 | vertx.setTimer(2000, (aTimerId) -> latch.complete()); 143 | ``` 144 | 145 | ## Event Bus Tools 146 | There are a number of event bus functions including to assist in consuming messages one or `N` times. 147 | 148 | ```java 149 | // ie one shot consumers of events 150 | EventBusTools.oneShotConsumer(bus, "SOME_ADDRESS", event -> { 151 | System.out.println("Got an event: " + event); 152 | }); 153 | ``` 154 | 155 | ## Service Client 156 | Service Client is wrapper over vertx http client. It supports 157 | 158 | * Creating http client from Json Configuration and builder. 159 | * Specification of timeout for apis. 160 | * In-built retry handler ( coming later) 161 | 162 | ```json 163 | { 164 | "host" : "localhost", 165 | "port" : 8080, 166 | "num_connections" : 10, 167 | "apis" :[ 168 | { 169 | "name" : "put", 170 | "timeout" : 1000 171 | }, 172 | { 173 | "name" : "remove", 174 | "timeout" : 1000 175 | } 176 | ] 177 | } 178 | ``` 179 | 180 | Field breakdown: 181 | * `host` server host or endpoint to connect to 182 | * `port` server port to connect to 183 | * `num_connections` number of connections in connection pool for Vertx http client 184 | * `apis` timeouts for apis (extensible to other attributes in future) 185 | 186 | Configuration Example: 187 | ```java 188 | JsonObject config = new JsonObject(); 189 | config.put(ServiceClient.HOST, "localhost"); 190 | config.put(ServiceClient.PORT, 8080); 191 | ServiceClient.create(vertx, config); 192 | ``` 193 | 194 | Builder Example: 195 | 196 | ```java 197 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 198 | builder.withHost("localhost").withPort(8080); 199 | builder.addApiTimeout("put", 1000L); 200 | builder.addApiTimeout("remove", 1000L); 201 | ServiceClient serviceClient = builder.build(); 202 | ``` 203 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | // Uncomment below when doing official builds 3 | // apply from: "gradle/sona.gradle" 4 | 5 | version = '3.3.0' 6 | group = "com.cyngn.vertx" 7 | archivesBaseName = "vertx-util" 8 | 9 | if (!JavaVersion.current().java8Compatible) { 10 | throw new IllegalStateException('''A Haiku: 11 | | This needs Java 8, 12 | | You are using something else, 13 | | Refresh. Try again.'''.stripMargin()) 14 | } 15 | 16 | repositories { 17 | mavenCentral() 18 | maven { 19 | url = 'http://oss.sonatype.org/content/repositories/snapshots/' 20 | } 21 | } 22 | 23 | dependencies { 24 | compile 'io.vertx:vertx-core:3.3.0-SNAPSHOT' 25 | compile 'io.vertx:vertx-web:3.3.0-SNAPSHOT' 26 | compile "io.vertx:vertx-codegen:3.3.0-SNAPSHOT" 27 | compile 'org.slf4j:slf4j-api:1.7.7' 28 | compile 'commons-lang:commons-lang:2.6' 29 | compile 'javax.ws.rs:javax.ws.rs-api:2.0.1' 30 | compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.1" 31 | testCompile "junit:junit:4.11" 32 | testCompile "io.vertx:vertx-unit:3.3.0-SNAPSHOT" 33 | testCompile 'io.vertx:vertx-web:3.3.0-SNAPSHOT:tests' 34 | testCompile 'io.vertx:vertx-core:3.3.0-SNAPSHOT:tests' 35 | } 36 | 37 | task wrapper(type: Wrapper) { 38 | gradleVersion = '2.12' 39 | } 40 | 41 | task release() << {} 42 | 43 | gradle.taskGraph.whenReady {taskGraph -> 44 | if (!taskGraph.hasTask(release)) { 45 | version += '-SNAPSHOT' 46 | } 47 | } 48 | 49 | task javadocJar(type: Jar) { 50 | classifier = 'javadoc' 51 | from javadoc 52 | } 53 | 54 | task sourcesJar(type: Jar) { 55 | classifier = 'sources' 56 | from sourceSets.main.allSource 57 | } 58 | 59 | artifacts { 60 | archives javadocJar, sourcesJar 61 | } 62 | -------------------------------------------------------------------------------- /gradle/.gradle/2.1/taskArtifacts/cache.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 17 22:29:02 PDT 2015 2 | -------------------------------------------------------------------------------- /gradle/.gradle/2.1/taskArtifacts/cache.properties.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyngn/vertx-util/ad6ec9eda216dd3aedfc06861e955ea615f4a33b/gradle/.gradle/2.1/taskArtifacts/cache.properties.lock -------------------------------------------------------------------------------- /gradle/.gradle/2.1/taskArtifacts/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyngn/vertx-util/ad6ec9eda216dd3aedfc06861e955ea615f4a33b/gradle/.gradle/2.1/taskArtifacts/fileHashes.bin -------------------------------------------------------------------------------- /gradle/.gradle/2.1/taskArtifacts/fileSnapshots.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyngn/vertx-util/ad6ec9eda216dd3aedfc06861e955ea615f4a33b/gradle/.gradle/2.1/taskArtifacts/fileSnapshots.bin -------------------------------------------------------------------------------- /gradle/.gradle/2.1/taskArtifacts/outputFileStates.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyngn/vertx-util/ad6ec9eda216dd3aedfc06861e955ea615f4a33b/gradle/.gradle/2.1/taskArtifacts/outputFileStates.bin -------------------------------------------------------------------------------- /gradle/.gradle/2.1/taskArtifacts/taskArtifacts.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyngn/vertx-util/ad6ec9eda216dd3aedfc06861e955ea615f4a33b/gradle/.gradle/2.1/taskArtifacts/taskArtifacts.bin -------------------------------------------------------------------------------- /gradle/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyngn/vertx-util/ad6ec9eda216dd3aedfc06861e955ea615f4a33b/gradle/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Apr 17 22:29:02 PDT 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.1-bin.zip 7 | -------------------------------------------------------------------------------- /gradle/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 | -------------------------------------------------------------------------------- /gradle/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 | -------------------------------------------------------------------------------- /gradle/sona.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'signing' 2 | apply plugin: 'maven' 3 | 4 | signing { 5 | required { 6 | gradle.taskGraph.hasTask("uploadArchives") 7 | } 8 | sign configurations.archives 9 | } 10 | 11 | uploadArchives { 12 | repositories { 13 | mavenDeployer { 14 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 15 | 16 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 17 | authentication(userName: sonaUsername, password: sonaPassword) 18 | } 19 | 20 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 21 | authentication(userName: sonaUsername, password: sonaPassword) 22 | } 23 | 24 | pom.project { 25 | name 'vertx-opentsdb' 26 | packaging 'jar' 27 | description 'A library for using opentsdb with vert.x' 28 | url 'https://github.com/cyngn/vertx-opentsdb' 29 | 30 | scm { 31 | url 'https://github.com/cyngn/vertx-opentsdb' 32 | } 33 | 34 | licenses { 35 | license { 36 | name 'The Apache License, Version 2.0' 37 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 38 | } 39 | } 40 | 41 | developers { 42 | developer { 43 | id 'jtruelove' 44 | name 'Jeremy truelove' 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyngn/vertx-util/ad6ec9eda216dd3aedfc06861e955ea615f4a33b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Sep 25 11:30:31 BST 2014 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.0-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 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/async/Action.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async; 2 | 3 | /** 4 | * Represents an action to perform. 5 | * 6 | * @author truelove@cyngn.com (Jeremy Truelove) 10/15/14 7 | */ 8 | public interface Action { 9 | /** 10 | * Something you want executed 11 | */ 12 | void callback(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/async/Latch.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async; 2 | 3 | /** 4 | * A helper class for coordinating events asynchronously. 5 | * 6 | * @author truelove@cyngn.com (Jeremy Truelove) 10/15/14 7 | */ 8 | public class Latch { 9 | private Action onComplete; 10 | private int count; 11 | private int currentCount; 12 | 13 | /** 14 | * @param count the number of events to complete before callback is called 15 | * @param onComplete the action to take when the latch has been completed 16 | */ 17 | public Latch(int count, Action onComplete) { 18 | validateParams(count, onComplete); 19 | 20 | this.count = count; 21 | currentCount = 0; 22 | this.onComplete = onComplete; 23 | } 24 | 25 | /** 26 | * Called to signal to the latch an event has completed. 27 | */ 28 | public void complete() { 29 | if (currentCount == count) { 30 | throw new IllegalStateException("Latch has already been completed."); 31 | } 32 | 33 | currentCount++; 34 | 35 | if (currentCount == count) { 36 | onComplete.callback(); 37 | } 38 | } 39 | 40 | /** 41 | * Resets the latch using the current settings. 42 | */ 43 | public void reset() { 44 | reset(count, onComplete); 45 | } 46 | 47 | /** 48 | * Resets the latch using a new count. 49 | * 50 | * @param count the new count to use 51 | */ 52 | public void reset(int count) { 53 | reset(count, onComplete); 54 | } 55 | 56 | /** 57 | * Resets the latch to a new count and new action 58 | * 59 | * @param count the new count to use 60 | * @param onComplete the new Action to call on latch completion 61 | */ 62 | public void reset(int count, Action onComplete) { 63 | validateParams(count, onComplete); 64 | 65 | currentCount = 0; 66 | this.count = count; 67 | this.onComplete = onComplete; 68 | } 69 | 70 | private void validateParams(int count, Action onComplete) { 71 | if (count < 1) { 72 | throw new IllegalArgumentException("Count must be greater than 0"); 73 | } 74 | 75 | if (onComplete == null) { 76 | throw new IllegalArgumentException("Cannot set a null callback for complete"); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/async/ResultContext.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async; 2 | 3 | /** 4 | * Handles wrapping what occurred as a result of an async operation. 5 | * 6 | * @author truelove@cyngn.com (Jeremy Truelove) 9/3/15 7 | */ 8 | public class ResultContext { 9 | public final boolean succeeded; 10 | public final Throwable error; 11 | public final String errorMessage; 12 | public final T value; 13 | 14 | /** 15 | * Creates a new result context that represents an operation that completed without error. 16 | * 17 | * @param succeeded True if the operation was successful, false otherwise. 18 | */ 19 | public ResultContext(boolean succeeded) { 20 | this.succeeded = succeeded; 21 | this.value = null; 22 | this.error = null; 23 | this.errorMessage = null; 24 | } 25 | 26 | /** 27 | * Creates a new result context that represents an operation that completed without error. 28 | * 29 | * @param succeeded True if the operation was successful, false otherwise. 30 | * @param value The resulting value. 31 | */ 32 | public ResultContext(boolean succeeded, T value) { 33 | this.succeeded = succeeded; 34 | this.value = value; 35 | this.error = null; 36 | this.errorMessage = null; 37 | } 38 | 39 | /** 40 | * Creates a new result context that represents a failed async operation. 41 | * 42 | * @param error The {@link Throwable} error that was the result of the operation. 43 | * @param errorMessage The error message. 44 | */ 45 | public ResultContext(Throwable error, String errorMessage) { 46 | this.succeeded = false; 47 | this.value = null; 48 | this.error = error; 49 | this.errorMessage = errorMessage; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/async/promise/Promise.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async.promise; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.json.JsonObject; 5 | 6 | import java.util.function.Consumer; 7 | 8 | /** 9 | * Represents a set of one or more asynchronous actions. 10 | * 11 | * @author truelove@cyngn.com (Jeremy Truelove) 7/30/15 12 | */ 13 | public interface Promise { 14 | 15 | String CONTEXT_FAILURE_KEY = "failure"; 16 | 17 | /** 18 | * Executes all actions in parallel 19 | * 20 | * @param actions the actions to execute 21 | * @return the promise representing the actions 22 | */ 23 | Promise all(PromiseAction ... actions); 24 | 25 | /** 26 | * Executes all actions serially 27 | * 28 | * @param actions the actions to execute 29 | * @return the promise representing the actions 30 | */ 31 | Promise allInOrder(PromiseAction ... actions); 32 | 33 | /** 34 | * Add an action to execute in the chain. 35 | * 36 | * @param action the action to execute 37 | * @return the promise representing the actions 38 | */ 39 | Promise then(PromiseAction action); 40 | 41 | /** 42 | * Add an exception handler to be called in the event something goes wrong. 43 | * 44 | * @param onFailure the callback to call on failure 45 | * @return the promise representing the actions 46 | */ 47 | Promise except(Consumer onFailure); 48 | 49 | /** 50 | * The callback to call when all promise actions are done. This will only be called if there are no failures. 51 | * @param onComplete the callback to hit when the promise is complete 52 | * @return the promise representing the actions 53 | */ 54 | Promise done(Consumer onComplete); 55 | 56 | /** 57 | * A timeout to set on the promise. 58 | * @param time the delay in milliseconds that the promise needs to complete in 59 | * @return the promise representing the actions 60 | */ 61 | Promise timeout(long time); 62 | 63 | /** 64 | * Has the promise succeeded? Will return false while still executing. 65 | * 66 | * @return true if the promise has succeeded false otherwise 67 | */ 68 | boolean succeeded(); 69 | 70 | /** 71 | * Has the promise completed yet? Either by completing all tasks or failing to. 72 | * 73 | * @return true if all actions or done completing or the promise has failed. 74 | */ 75 | boolean completed(); 76 | 77 | /** 78 | * Called when you are ready to begin resolution of the promise chain. 79 | * 80 | * @return the promise you are evaluating 81 | */ 82 | Promise eval(); 83 | 84 | /** 85 | * If the promise has no actions in it 86 | * 87 | * @return true if the promise has no actions, false otherwise 88 | */ 89 | boolean isEmpty(); 90 | 91 | /** 92 | * Create a new promise. 93 | * @param vertx the vertx instance to run it on 94 | * @return the newly created Promise 95 | */ 96 | static Promise newInstance(Vertx vertx) { 97 | return new PromiseImpl(vertx); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/async/promise/PromiseAction.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async.promise; 2 | 3 | import io.vertx.core.json.JsonObject; 4 | 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * The contract of a discrete action to be executed in a Promise 9 | * 10 | * @author truelove@cyngn.com (Jeremy Truelove) 7/30/15 11 | */ 12 | public interface PromiseAction { 13 | /** 14 | * The action to execute. 15 | * 16 | * @param context general purpose object for populating with data that can be used by other actions in a promise or 17 | * for communicating the result 18 | * @param onResult the callback that collects the result of any given PromiseAction necessary for 19 | * continuing or completing the chain of actions in a promise. 20 | */ 21 | void execute(JsonObject context, Consumer onResult); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/async/promise/PromiseFactory.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async.promise; 2 | 3 | import io.vertx.core.Vertx; 4 | 5 | /** 6 | * Handles generating promises that can be executed on the Vert.x eventloop, allows you to not keep track of vert.x 7 | * instance. 8 | * 9 | * @author truelove@cyngn.com (Jeremy Truelove) 7/30/15 10 | */ 11 | public class PromiseFactory { 12 | 13 | private final Vertx vertx; 14 | 15 | /** 16 | * Initialize a promise factory with a reference to your vertx event loop 17 | * @param vertx the vertx event loop to run your promises on 18 | */ 19 | public PromiseFactory(Vertx vertx) { 20 | this.vertx = vertx; 21 | } 22 | 23 | /** 24 | * Create an empty promise. 25 | * 26 | * @return a new empty promise 27 | */ 28 | public Promise create() { 29 | return Promise.newInstance(vertx); 30 | } 31 | 32 | /** 33 | * Create a promise with a list of actions to be executed serially. 34 | * 35 | * @param actions the actions to execute 36 | * @return the promise representing the actions 37 | */ 38 | public Promise createSerial(PromiseAction ... actions) { 39 | return Promise.newInstance(vertx).allInOrder(actions); 40 | } 41 | 42 | /** 43 | * Create a promise with a list of actions to be executed in parallel. 44 | * 45 | * @param actions the actions to execute 46 | * @return the promise representing the actions 47 | */ 48 | public Promise createParallel(PromiseAction ... actions) { 49 | return Promise.newInstance(vertx).all(actions); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/async/promise/PromiseImpl.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async.promise; 2 | 3 | import com.cyngn.vertx.async.Latch; 4 | import io.vertx.core.Vertx; 5 | import io.vertx.core.json.JsonObject; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | import java.util.function.Consumer; 11 | 12 | /** 13 | * Implementation of Promise interface. 14 | * 15 | * @author truelove@cyngn.com (Jeremy Truelove) 7/30/15 16 | */ 17 | public class PromiseImpl implements Promise { 18 | 19 | private List actions; 20 | 21 | private int pos; 22 | private boolean done; 23 | private boolean failed; 24 | private Vertx vertx; 25 | private Consumer onFailure; 26 | private Consumer onComplete; 27 | private JsonObject context; 28 | private Long timerId; 29 | private AtomicBoolean evaluated; 30 | 31 | // scope to the package 32 | PromiseImpl(Vertx vertx) { 33 | this.vertx = vertx; 34 | pos = 0; 35 | done = failed = false; 36 | context = new JsonObject(); 37 | actions = new ArrayList<>(); 38 | evaluated = new AtomicBoolean(false); 39 | } 40 | 41 | @Override 42 | public Promise eval(){ 43 | if(actions.size() < 1) { 44 | throw new IllegalStateException("cannot eval an empty promise"); 45 | } 46 | 47 | if(evaluated.compareAndSet(false, true)) { 48 | vertx.runOnContext(this::internalEval); 49 | } else { 50 | throw new IllegalStateException("You cannot eval a promise chain more than once"); 51 | } 52 | return this; 53 | } 54 | 55 | @Override 56 | public boolean isEmpty() { 57 | return actions.size() == 0; 58 | } 59 | 60 | /** 61 | * Move the promise chain to the next step in the process. 62 | */ 63 | private void internalEval(Void aVoid) { 64 | if (!done && pos < actions.size() && !failed) { 65 | PromiseAction action = actions.get(pos); 66 | pos++; 67 | try { 68 | action.execute(context, (success) -> { 69 | if (failed || done) { return; } 70 | 71 | if (!success) { 72 | fail(); 73 | } else { 74 | done = pos == actions.size(); 75 | } 76 | 77 | // scheduled the next action 78 | if (!done && !failed) { 79 | vertx.runOnContext(this::internalEval); 80 | } 81 | 82 | if (done && !failed) { 83 | cleanUp(); 84 | // ultimate success case 85 | if(onComplete != null) { onComplete.accept(context); } 86 | } 87 | }); 88 | } catch (Exception ex) { 89 | context.put(CONTEXT_FAILURE_KEY, ex.toString()); 90 | fail(); 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * End the processing chain due to an error condition 97 | */ 98 | private void fail() { 99 | failed = true; 100 | done = true; 101 | cleanUp(); 102 | if(onFailure != null) { 103 | onFailure.accept(context); 104 | } 105 | } 106 | 107 | /** 108 | * Clear local objects no longer needed 109 | */ 110 | private void cleanUp() { 111 | cancelTimer(); 112 | actions.clear(); 113 | } 114 | 115 | @Override 116 | public Promise all(PromiseAction ... theActions) { 117 | return then((context, onResult) -> { 118 | // track the results, but execute them all in parallel vs serially 119 | Latch latch = new Latch(theActions.length, () -> onResult.accept(true)); 120 | for(PromiseAction action : theActions) { 121 | action.execute(context, (success) -> { 122 | if(!success) { 123 | onResult.accept(false); 124 | } else { 125 | latch.complete(); 126 | } 127 | }); 128 | } 129 | }); 130 | } 131 | 132 | @Override 133 | public Promise allInOrder(PromiseAction... actions) { 134 | for (PromiseAction action : actions) { then(action); } 135 | return this; 136 | } 137 | 138 | @Override 139 | public Promise then(PromiseAction action) { 140 | if (done) { throw new IllegalArgumentException("can't add actions to a completed chain"); } 141 | 142 | actions.add(action); 143 | return this; 144 | } 145 | 146 | @Override 147 | public Promise done(Consumer action) { 148 | onComplete = action; 149 | return this; 150 | } 151 | 152 | @Override 153 | public Promise timeout(long time) { 154 | if(done) { throw new IllegalArgumentException("Can't set timer on a completed promise"); } 155 | 156 | if(timerId != null) { 157 | // if you are able to cancel it schedule another 158 | if(vertx.cancelTimer(timerId)) { 159 | timerId = vertx.setTimer(time, theTimerId -> cancel()); 160 | } 161 | } else { 162 | timerId = vertx.setTimer(time, theTimerId -> cancel()); 163 | } 164 | 165 | return this; 166 | } 167 | 168 | /** 169 | * Get rid of a timer that has not been fired yet. 170 | */ 171 | private void cancelTimer(){ 172 | if(timerId != null) { 173 | vertx.cancelTimer(timerId); 174 | } 175 | } 176 | 177 | /** 178 | * Function called when a timer is expired but the chain is not yet complete. 179 | */ 180 | private void cancel() { 181 | timerId = null; 182 | if(!done) { 183 | context.put(CONTEXT_FAILURE_KEY, "promise timed out"); 184 | fail(); 185 | } 186 | } 187 | 188 | @Override 189 | public boolean succeeded() { 190 | return !failed; 191 | } 192 | 193 | @Override 194 | public boolean completed() { 195 | return done; 196 | } 197 | 198 | @Override 199 | public Promise except(Consumer onFailure) { 200 | this.onFailure = onFailure; 201 | return this; 202 | } 203 | } -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/client/ServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.client; 2 | 3 | import io.vertx.core.Handler; 4 | import io.vertx.core.Vertx; 5 | import io.vertx.core.buffer.Buffer; 6 | import io.vertx.core.http.HttpClient; 7 | import io.vertx.core.http.HttpClientOptions; 8 | import io.vertx.core.http.HttpClientRequest; 9 | import io.vertx.core.http.HttpClientResponse; 10 | import io.vertx.core.http.HttpHeaders; 11 | import io.vertx.core.http.HttpMethod; 12 | import io.vertx.core.json.JsonArray; 13 | import io.vertx.core.json.JsonObject; 14 | import org.apache.commons.lang.StringUtils; 15 | 16 | import javax.ws.rs.core.MediaType; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * Service client for vertx services. 22 | 23 | * Uses {@link HttpClient} as underlying client 24 | * 25 | * @author asarda@cyngn.com (Ajay Sarda) on 8/25/15. 26 | */ 27 | public class ServiceClient { 28 | 29 | // configuration key constants 30 | public static final String HOST = "host"; 31 | public static final String PORT = "port"; 32 | public static final String NUM_CONNECTIONS = "num_connections"; 33 | public static final String COMPRESSION = "compression"; 34 | public static final String APIS = "apis"; 35 | public static final String API_NAME = "name"; 36 | public static final String TIMEOUT = "timeout"; 37 | public static final String SSL = "ssl"; 38 | public static final String HEADERS = "headers"; 39 | private static final long NO_TIMEOUT = 0L; 40 | 41 | // empty request. 42 | public static final String EMPTY_REQUEST = ""; 43 | 44 | private Map apiTimeouts = new HashMap<>(); 45 | 46 | // http client delegate 47 | private HttpClient client; 48 | 49 | // saving host and port for consumers 50 | private String host; 51 | private Integer port; 52 | private long timeout; 53 | private Map headers; 54 | 55 | // private constructor to prohibit creating instances using constructor 56 | private ServiceClient() {} 57 | 58 | /** 59 | * Creates instance of {@link ServiceClient} from json configuration 60 | * 61 | * @param vertx - reference to vertx instance. 62 | * @param config - Json configuration. 63 | * @return {@link ServiceClient} object 64 | */ 65 | public static ServiceClient create(Vertx vertx, JsonObject config) { 66 | Builder builder = new Builder(vertx); 67 | 68 | if (config.containsKey(HOST)) { 69 | builder.withHost(config.getString(HOST)); 70 | } else { 71 | throw new IllegalArgumentException("No host key defined in service client configuration"); 72 | } 73 | 74 | if (config.containsKey(PORT)) { 75 | builder.withPort(config.getInteger(PORT)); 76 | } else { 77 | throw new IllegalArgumentException("No port key defined in service client configuration"); 78 | } 79 | 80 | if (config.containsKey(NUM_CONNECTIONS)) { 81 | builder.withNumConnections(config.getInteger(NUM_CONNECTIONS)); 82 | } 83 | 84 | if (config.containsKey(COMPRESSION)) { 85 | builder.withCompression(config.getBoolean(COMPRESSION)); 86 | } 87 | 88 | if (config.containsKey(TIMEOUT)) { 89 | builder.withTimeout(config.getLong(TIMEOUT)); 90 | } 91 | 92 | if (config.containsKey(SSL)) { 93 | builder.withSsl(config.getBoolean(SSL)); 94 | } 95 | 96 | if (config.containsKey(HEADERS)) { 97 | JsonObject headerConfig = config.getJsonObject(HEADERS, null); 98 | 99 | if (headerConfig != null) { 100 | Map headers = new HashMap<>(); 101 | headerConfig.forEach(entry -> { 102 | headers.put(entry.getKey(), (String) entry.getValue()); 103 | }); 104 | builder.withHeaders(headers); 105 | } 106 | } 107 | 108 | if (config.containsKey(APIS)) { 109 | JsonArray apiArray = config.getJsonArray(APIS); 110 | 111 | for (int pos = 0; pos < apiArray.size(); pos++) { 112 | JsonObject apiObject = apiArray.getJsonObject(pos); 113 | String name = apiObject.getString(API_NAME); 114 | long timeout = apiObject.getLong(TIMEOUT, NO_TIMEOUT); 115 | builder.addApiTimeout(name, timeout); 116 | } 117 | } 118 | 119 | return builder.build(); 120 | } 121 | 122 | private ServiceClient(HttpClient client, Map apiTimeouts, String host, Integer port, long timeout, 123 | Map headers) { 124 | this.client = client; 125 | this.apiTimeouts = apiTimeouts; 126 | this.host = host; 127 | this.port = port; 128 | this.timeout = timeout; 129 | this.headers = headers; 130 | } 131 | 132 | /** 133 | * Gets the host name for which the service client is created. 134 | * 135 | * @return host name 136 | */ 137 | public String getHost() { 138 | return host; 139 | } 140 | 141 | /** 142 | * Gets the port for which the service client is created 143 | * 144 | * @return port 145 | */ 146 | public Integer getPort() { 147 | return port; 148 | } 149 | 150 | /** 151 | * Fluent Builder class to create objects of {@link ServiceClient} 152 | */ 153 | public static class Builder { 154 | private String host; 155 | private int port = 0; 156 | private boolean compression = HttpClientOptions.DEFAULT_TRY_USE_COMPRESSION; 157 | private int numConnections = HttpClientOptions.DEFAULT_MAX_POOL_SIZE; 158 | private long timeout = 0L; 159 | private final Vertx vertx; 160 | private boolean ssl; 161 | private Map apiTimeouts = new HashMap<>(); 162 | private Map headers; 163 | 164 | public Builder(Vertx vertx) { 165 | this.vertx = vertx; 166 | } 167 | 168 | /** 169 | * Builds the {@link ServiceClient} with specified parameters 170 | * 171 | * @return - instance of ServiceClient. 172 | */ 173 | public ServiceClient build() { 174 | HttpClientOptions options = new HttpClientOptions(); 175 | 176 | if (StringUtils.isNotBlank(host)) { 177 | options.setDefaultHost(host); 178 | } else { 179 | throw new IllegalArgumentException("missing host parameter"); 180 | } 181 | 182 | if (port != 0) { 183 | options.setDefaultPort(port); 184 | } else { 185 | throw new IllegalArgumentException("missing port parameter"); 186 | } 187 | 188 | options.setTryUseCompression(compression); 189 | options.setMaxPoolSize(numConnections); 190 | options.setSsl(ssl); 191 | 192 | // create the http client; 193 | HttpClient client = vertx.createHttpClient(options); 194 | 195 | return new ServiceClient(client, apiTimeouts, host, port, timeout, headers); 196 | } 197 | 198 | /** 199 | * Sets the hostname 200 | * 201 | * @param host - hostname associated with client. 202 | * @return - reference to Builder object 203 | */ 204 | public Builder withHost(String host) { 205 | this.host = host; 206 | return this; 207 | } 208 | 209 | /** 210 | * Sets the port 211 | * 212 | * @param port - port associated with client 213 | * @return - reference to Builder object. 214 | */ 215 | public Builder withPort(int port) { 216 | this.port = port; 217 | return this; 218 | } 219 | 220 | /** 221 | * Sets the compression 222 | * 223 | * @param compression - if compression is set 224 | * @return - reference to Builder object. 225 | */ 226 | public Builder withCompression(boolean compression) { 227 | this.compression = compression; 228 | return this; 229 | } 230 | 231 | /** 232 | * Sets the number of connections in connection pool for the client 233 | * 234 | * @param numConnections - connection pool size. 235 | * @return - reference to Builder object. 236 | */ 237 | public Builder withNumConnections(int numConnections) { 238 | this.numConnections = numConnections; 239 | return this; 240 | } 241 | 242 | /** 243 | * Sets the timeout for all api calls from client. 244 | * @param timeout - timeout in milliseconds. timeout with value 0 means no timeout. 245 | * @return - reference to Builder object. 246 | */ 247 | public Builder withTimeout(long timeout) { 248 | if (timeout < 0L) { 249 | throw new IllegalArgumentException("Invalid timeout value: " + timeout); 250 | } 251 | this.timeout = timeout; 252 | return this; 253 | } 254 | 255 | /** 256 | * Sets the ssl on client. 257 | * 258 | * Any request to secured http endpoint should have ssl set 259 | * 260 | * @param ssl - ssl enabled? 261 | * @return - reference to Builder object. 262 | */ 263 | public Builder withSsl(boolean ssl) { 264 | this.ssl = ssl; 265 | return this; 266 | } 267 | 268 | /** 269 | * Sets the headers to be sent on every api call from client. 270 | * 271 | * @param headers - map of string key, string value pairs. 272 | * @return - reference to Builder object. 273 | */ 274 | public Builder withHeaders(Map headers) { 275 | this.headers = headers; 276 | return this; 277 | } 278 | 279 | /** 280 | * Adds the api to the builder 281 | * 282 | * @param name - api name used for subsequent usage to call api. 283 | * @param timeout - timeout in milliseconds. timeout with value 0 means no timeout. 284 | * @return - reference to Builder object. 285 | */ 286 | public Builder addApiTimeout(String name, long timeout) { 287 | if (timeout < 0L) { 288 | throw new IllegalArgumentException("Invalid timeout value: " + timeout + " for api: " + name); 289 | } 290 | 291 | if (apiTimeouts.containsKey(name)) { 292 | throw new IllegalArgumentException("api by name " + name + " already added to builder"); 293 | } 294 | 295 | apiTimeouts.put(name, timeout); 296 | return this; 297 | } 298 | } 299 | 300 | /** 301 | * Calls the service api 302 | * 303 | * @param httpMethod - HTTP method for the request 304 | * @param path - the absolute URI path 305 | * @param payload - payload sent in the call. 306 | * @param timeout - timeout in millis 307 | * @param responseHandler - response handler 308 | * @param exceptionHandler - exception handler 309 | */ 310 | public void call(HttpMethod httpMethod, String path, byte[] payload, long timeout, Handler responseHandler, 311 | Handler exceptionHandler) { 312 | HttpClientRequest request = client.request(httpMethod, path, responseHandler) 313 | .exceptionHandler(exceptionHandler) 314 | .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) 315 | .putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(payload.length)) 316 | .write(Buffer.buffer(payload)) 317 | .setTimeout(timeout); 318 | 319 | if (headers != null && headers.size() > 0) { 320 | headers.forEach((key, value) -> request.putHeader(key, value)); 321 | } 322 | 323 | request.end(); 324 | } 325 | 326 | /** 327 | * Calls the service api 328 | * 329 | * @param httpMethod - HTTP method for the request 330 | * @param path - the absolute URI path 331 | * @param serviceRequest - service request object 332 | * @param responseHandler - response handler 333 | * @param exceptionHandler - exception handler 334 | */ 335 | public void call(HttpMethod httpMethod, String path, ServiceRequest serviceRequest, Handler responseHandler, 336 | Handler exceptionHandler) { 337 | 338 | HttpClientRequest request = client.request(httpMethod, path, responseHandler) 339 | .exceptionHandler(exceptionHandler) 340 | .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) 341 | .putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf( 342 | serviceRequest.hasPayload() ? serviceRequest.getPayload().length : EMPTY_REQUEST.length())); 343 | 344 | 345 | if (serviceRequest.hasTimeout()) { 346 | request.setTimeout(serviceRequest.getTimeout()); 347 | } 348 | 349 | if (headers != null && headers.size() > 0) { 350 | headers.forEach((key, value) -> request.putHeader(key, value)); 351 | } 352 | 353 | if (serviceRequest.hasHeaders()) { 354 | serviceRequest.getHeaders().forEach((key, value) -> request.putHeader(key, value)); 355 | } 356 | 357 | request.end(); 358 | } 359 | 360 | /** 361 | * Calls the service api 362 | * 363 | * @param httpMethod - HTTP method for the request 364 | * @param path - the absolute URI path 365 | * @param payload - payload sent in the call. 366 | * @param responseHandler - response handler 367 | * @param exceptionHandler - exception handler 368 | */ 369 | public void call(HttpMethod httpMethod, String path, byte[] payload, Handler responseHandler, 370 | Handler exceptionHandler) { 371 | HttpClientRequest request = client.request(httpMethod, path, responseHandler) 372 | .exceptionHandler(exceptionHandler) 373 | .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) 374 | .putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(payload.length)) 375 | .write(Buffer.buffer(payload)); 376 | 377 | if (headers != null && headers.size() > 0) { 378 | headers.forEach((key, value) -> request.putHeader(key, value)); 379 | } 380 | 381 | request.end(); 382 | } 383 | 384 | /** 385 | * Calls the service api 386 | * 387 | * @param httpMethod - HTTP method for the request 388 | * @param path - the absolute URI path 389 | * @param responseHandler - response handler 390 | * @param exceptionHandler - exception handler 391 | */ 392 | public void call(HttpMethod httpMethod, String path, Handler responseHandler, 393 | Handler exceptionHandler) { 394 | call(httpMethod, path, EMPTY_REQUEST.getBytes(), responseHandler, exceptionHandler); 395 | } 396 | 397 | /** 398 | * Calls the service api 399 | * 400 | * @param httpMethod - HTTP method for the request 401 | * @param path - the absolute URI path 402 | * @param timeout - timeout in millis 403 | * @param responseHandler - response handler 404 | * @param exceptionHandler - exception handler 405 | */ 406 | public void call(HttpMethod httpMethod, String path, long timeout, Handler responseHandler, 407 | Handler exceptionHandler) { 408 | call(httpMethod, path, EMPTY_REQUEST.getBytes(), timeout, responseHandler, exceptionHandler); 409 | } 410 | 411 | /** 412 | * Close the client. Closing will close down any pooled connections. 413 | * Clients should always be closed after use. 414 | */ 415 | public void close() { 416 | client.close(); 417 | } 418 | 419 | /** 420 | * Get timeout for the API. 421 | * 422 | * @param apiName - api name 423 | * @return timeout value 424 | */ 425 | public Long getTimeout(String apiName) { 426 | Long timeout = apiTimeouts.get(apiName); 427 | 428 | return timeout == null ? this.timeout : timeout; 429 | } 430 | 431 | } 432 | 433 | 434 | 435 | 436 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/client/ServiceRequest.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.client; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * Abstraction for call request object for {@link ServiceClient} 7 | * 8 | * @author asarda@cyngn.com (Ajay Sarda) 11/17/15. 9 | */ 10 | public class ServiceRequest { 11 | 12 | private byte[] payload; 13 | private long timeout = 0L; 14 | private Map headers; 15 | 16 | public ServiceRequest() {} 17 | 18 | /* 19 | * Using setters to set the state of object. 20 | * NOTE: 21 | * Builder objects are expensive on heap, so please 22 | * do not add builder object here in future 23 | */ 24 | 25 | /** 26 | * Gets the payload associated with the request 27 | * 28 | * @return - byte array payload 29 | */ 30 | public byte[] getPayload() { 31 | return payload; 32 | } 33 | 34 | /** 35 | * Sets the payload to be associated with the request 36 | * 37 | * @param payload - byte array 38 | */ 39 | public void setPayload(byte[] payload) { 40 | this.payload = payload; 41 | } 42 | 43 | /** 44 | * Gets the timeout associated with the request. 45 | * 46 | * @return - timeout in milliseconds. 47 | */ 48 | public long getTimeout() { 49 | return timeout; 50 | } 51 | 52 | /** 53 | * Sets the timeout to be associated with the request. 54 | * 55 | * @param timeout - timeout value in milliseconds. 56 | */ 57 | public void setTimeout(long timeout) { 58 | this.timeout = timeout; 59 | } 60 | 61 | /** 62 | * Gets the http headers associated with the request 63 | * 64 | * @return - map of string key, string value request headers. 65 | */ 66 | public Map getHeaders() { 67 | return headers; 68 | } 69 | 70 | /** 71 | * Sets the http headers to be associated with the request 72 | * 73 | * @param headers - map of string key, string value request headers. 74 | */ 75 | public void setHeaders(Map headers) { 76 | this.headers = headers; 77 | } 78 | 79 | /** 80 | * Checks if the {@link ServiceRequest} has timeout value set 81 | * 82 | * @return - true if timeout is set, false otherwise. 83 | */ 84 | public boolean hasTimeout() { 85 | return timeout > 0L; 86 | } 87 | 88 | /** 89 | * Checks if the {@link ServiceRequest} has payload set. 90 | * 91 | * @return - true if payload is set, false otherwise. 92 | */ 93 | public boolean hasPayload() { 94 | return payload != null; 95 | } 96 | 97 | /** 98 | * Checks if the {@link ServiceRequest} has headers set. 99 | * 100 | * @return - true if headers are set, false otherwise. 101 | */ 102 | public boolean hasHeaders() { 103 | return headers != null && headers.size() > 0; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/eventbus/EventBusTools.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.eventbus; 2 | 3 | import io.vertx.core.Handler; 4 | import io.vertx.core.eventbus.EventBus; 5 | import io.vertx.core.eventbus.Message; 6 | import io.vertx.core.eventbus.MessageConsumer; 7 | 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | /** 11 | * General functions for interacting with the event bus. 12 | * 13 | * @author truelove@cyngn.com (Jeremy Truelove) 4/24/15 14 | */ 15 | public class EventBusTools { 16 | 17 | /** 18 | * Listen to a message just once 19 | * 20 | * @param bus the event bus to listen on 21 | * @param address the address to listen for 22 | * @param handler callback on message received 23 | * @param the type of object getting passed via the event bus 24 | * @return the consumer created 25 | */ 26 | public static MessageConsumer oneShotConsumer(EventBus bus, String address, Handler> handler) { 27 | return consumeNTimes(bus, address, handler, 1, false); 28 | } 29 | 30 | /** 31 | * Listen to a message just once 32 | * 33 | * @param bus the event bus to listen on 34 | * @param address the address to listen for 35 | * @param handler callback on message received 36 | * @param the type of object getting passed via the event bus 37 | * @return the consumer created 38 | */ 39 | public static MessageConsumer oneShotLocalConsumer(EventBus bus, String address, Handler> handler) { 40 | return consumeNTimes(bus, address, handler, 1, true); 41 | } 42 | 43 | /** 44 | * Listen for a message N times 45 | * 46 | * @param bus the event bus to listen on 47 | * @param address the address to listen for 48 | * @param handler callback on message(s) received 49 | * @param timesToConsume the number of times to listen for a message 50 | * @param isLocalOnly should you consume just on the local event bus or everywhere 51 | * @param the type of object getting passed via the event bus 52 | * @return the consumer created 53 | */ 54 | public static MessageConsumer consumeNTimes(EventBus bus, String address, Handler> handler, 55 | int timesToConsume, boolean isLocalOnly) { 56 | if(timesToConsume <= 0) {return null;} 57 | 58 | MessageConsumer consumer = isLocalOnly ? bus.localConsumer(address) : bus.consumer(address); 59 | AtomicInteger count = new AtomicInteger(0); 60 | consumer.handler(msg -> { 61 | try { 62 | handler.handle(msg); 63 | count.incrementAndGet(); 64 | } finally { 65 | if (count.get() == timesToConsume) { 66 | consumer.unregister(); 67 | } 68 | } 69 | }); 70 | return consumer; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/validation/ValidationResult.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.validation; 2 | 3 | /** 4 | * Validation Result object for validate method for request objects. 5 | * 6 | * @author asarda@cyngn.com (Ajay Sarda) 9/8/15. 7 | */ 8 | public class ValidationResult { 9 | public static ValidationResult SUCCESS = new ValidationResult(true, null); 10 | 11 | public boolean valid; 12 | public String errorMsg; 13 | 14 | public ValidationResult(boolean valid, String errorMsg) { 15 | this.valid = valid; 16 | this.errorMsg = errorMsg; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/web/HttpHelper.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web; 2 | 3 | import io.netty.handler.codec.http.HttpResponseStatus; 4 | import io.vertx.core.buffer.Buffer; 5 | import io.vertx.core.http.HttpHeaders; 6 | import io.vertx.core.http.HttpServerResponse; 7 | import io.vertx.core.json.JsonObject; 8 | 9 | import javax.ws.rs.core.MediaType; 10 | 11 | /** 12 | * General Utility functions for dealing with HTTP requests/responses in Vert.x 13 | * 14 | * @author truelove@cyngn.com (Jeremy Truelove) 4/3/15 15 | */ 16 | public class HttpHelper { 17 | 18 | /** 19 | * Send a JSON error response with the specified error and http code. 20 | * 21 | * @param error the error that occurred 22 | * @param response the response being replied to 23 | * @param code the HTTP code 24 | */ 25 | public static void processErrorResponse(String error, HttpServerResponse response, int code) { 26 | processResponse(Buffer.buffer(new JsonObject().put("error", error).encode()), response, code, 27 | MediaType.APPLICATION_JSON); 28 | } 29 | 30 | /** 31 | * Send a plain text HTTP 200 response. 32 | * 33 | * @param response the response being replied to 34 | */ 35 | public static void processResponse(HttpServerResponse response) { 36 | processResponse(response, HttpResponseStatus.OK.code()); 37 | } 38 | 39 | /** 40 | * Send a plain text HTTP 200 response. 41 | * 42 | * @param response the response being replied to 43 | * @param code the HTTP code 44 | */ 45 | public static void processResponse(HttpServerResponse response, int code) { 46 | processResponse(Buffer.buffer(""), response, code, MediaType.TEXT_PLAIN); 47 | } 48 | 49 | /** 50 | * Send a HTTP 200 response with generic object as JSON. 51 | * 52 | * @param value the object to serialize to json 53 | * @param response the response being replied to 54 | * @param object type to serialize 55 | */ 56 | public static void processResponse(T value, HttpServerResponse response) { 57 | processResponse(value, response, HttpResponseStatus.OK.code()); 58 | } 59 | 60 | /** 61 | * Send a HTTP 200 response with JSON object. 62 | * 63 | * @param obj the json object to send 64 | * @param response the response being replied to 65 | */ 66 | public static void processResponse(JsonObject obj, HttpServerResponse response) { 67 | processResponse(obj, response, HttpResponseStatus.OK.code()); 68 | } 69 | 70 | /** 71 | * Send a HTTP 200 response with a byte array as an octet stream. 72 | * 73 | * @param byteArray the data send 74 | * @param response the response being replied to 75 | */ 76 | public static void processResponse(byte[] byteArray, HttpServerResponse response) { 77 | processResponse(byteArray, response, HttpResponseStatus.OK.code()); 78 | } 79 | 80 | /** 81 | * Send a HTTP response with JSON object. 82 | * 83 | * @param obj the json object to send 84 | * @param response the response being replied to 85 | * @param code the HTTP status code to reply with 86 | */ 87 | public static void processResponse(JsonObject obj, HttpServerResponse response, int code) { 88 | processResponse(Buffer.buffer(obj.encode()), response, code, MediaType.APPLICATION_JSON); 89 | } 90 | 91 | /** 92 | * Send a HTTP response with generic object as JSON. 93 | * 94 | * @param value the object to serialize to json 95 | * @param response the response being replied to 96 | * @param code the HTTP status code to reply with 97 | * @param object type to serialize 98 | */ 99 | public static void processResponse(T value, HttpServerResponse response, int code) { 100 | processResponse(Buffer.buffer(JsonUtil.getJsonForObject(value)), response, code, MediaType.APPLICATION_JSON); 101 | } 102 | 103 | /** 104 | * Send a HTTP response with a byte array as an octet stream. 105 | * 106 | * @param byteArray the data send 107 | * @param response the response being replied to 108 | * @param code the HTTP status code to reply with 109 | */ 110 | public static void processResponse(byte[] byteArray, HttpServerResponse response, int code) { 111 | processResponse(Buffer.buffer(byteArray), response, code, MediaType.APPLICATION_OCTET_STREAM); 112 | } 113 | 114 | /** 115 | * Send a HTTP response with a byte array as an octet stream. 116 | * 117 | * @param buffer the data send 118 | * @param response the response being replied to 119 | * @param code the HTTP status code to reply with 120 | * @param contentType the CONTENT_TYPE of the response 121 | */ 122 | public static void processResponse(Buffer buffer, HttpServerResponse response, int code, String contentType) { 123 | response.putHeader(HttpHeaders.CONTENT_TYPE, contentType); 124 | response.setStatusCode(code); 125 | response.putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(buffer.length())).write(buffer).end(); 126 | } 127 | 128 | /** 129 | * Attempts to take a json request body string and parse that to the specified class type. If it succeeds it returns 130 | * that object. If it fails it responds to the request with a HttpResponseStatus.BAD_REQUEST. 131 | * 132 | * @param json the http body to attempt to parse 133 | * @param clazz the class type to hydrate 134 | * @param response the associated HTTP response object 135 | * @param the desired class type 136 | * @return the object successfully parsed or null in the case where the parsing fails. 137 | */ 138 | public static T attemptToParse(String json, Class clazz, HttpServerResponse response){ 139 | T result = JsonUtil.parseJsonToObject(json, clazz); 140 | if (result == null) { 141 | HttpHelper.processErrorResponse("Failed to parse JSon to create request", response, 142 | HttpResponseStatus.BAD_REQUEST.code()); 143 | } 144 | 145 | return result; 146 | } 147 | 148 | /** 149 | * Does the response code represent a non-2XX code 150 | * 151 | * @param code the code to check 152 | * @return true if a 2XX code, false otherwise 153 | */ 154 | public static boolean isHttp2XXResponse(int code) { 155 | return code >= 200 && code < 300; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/web/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | import java.util.TimeZone; 11 | 12 | /** 13 | * Collection of utils for parsing Json and interacting with it. 14 | * 15 | * @author truelove@cyngn.com (Jeremy Truelove) 10/15/14 16 | */ 17 | public class JsonUtil { 18 | private final static Logger logger = LoggerFactory.getLogger(JsonUtil.class); 19 | 20 | public final static ObjectMapper mapper = new ObjectMapper() 21 | .registerModule(new JavaTimeModule())//handle Java 8 time objects 22 | .setTimeZone(TimeZone.getTimeZone("UTC"));//override default of "GMT" 23 | 24 | /** 25 | * Parses raw json into a concrete impl of your choosing 26 | * 27 | * @param data the json raw data 28 | * @param clazz the class to parse the json into 29 | * @param the type of class parameterizing this method 30 | * @return the new instance object generated from json or null on failure 31 | */ 32 | public static T parseJsonToObject(String data, Class clazz) { 33 | if (data == null || "".equals(data)) { return null; } 34 | 35 | T obj = null; 36 | try { obj = mapper.readValue(data, clazz); } 37 | catch (IOException e) { 38 | logger.error("Error parsing class: {} error: ", clazz, e); 39 | } 40 | return obj; 41 | } 42 | 43 | /** 44 | * Parses raw json bytes into a concrete impl of your choosing. If there is an error parsing then the 45 | * exception is caught and the result will be null. 46 | * 47 | * @param data the json raw data 48 | * @param clazz the class to parse the json into 49 | * @param the type of class parameterizing this method 50 | * @return the new instance object generated from json or null on failure 51 | */ 52 | public static T parseJsonToObject(byte[] data, Class clazz) { 53 | if (data == null) { 54 | return null; 55 | } 56 | 57 | T obj = null; 58 | try { 59 | obj = mapper.readValue(data, clazz); 60 | } catch (IOException e) { 61 | logger.error("Error parsing class: {} error: ", clazz, e); 62 | } 63 | return obj; 64 | } 65 | 66 | /** 67 | * Serializes object to raw json 68 | * 69 | * @param object the object to create Json from 70 | * @return the Json representing the object passed in or null if we fail to be able to generate it 71 | */ 72 | public static String getJsonForObject(Object object) { 73 | if (object == null) { throw new IllegalArgumentException("Can't serialize a null object to Json."); } 74 | 75 | String jsonString = null; 76 | try { jsonString = mapper.writeValueAsString(object); } 77 | catch (JsonProcessingException e) { 78 | logger.error("Error generating JSON class: {} error: ", object.getClass().getName(), e); 79 | } 80 | return jsonString; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/web/RestApi.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web; 2 | 3 | import io.vertx.core.Handler; 4 | import io.vertx.core.http.HttpMethod; 5 | import io.vertx.ext.web.Router; 6 | import io.vertx.ext.web.RoutingContext; 7 | import org.slf4j.Logger; 8 | 9 | /** 10 | * Basic Rest interface to use in Vert.x 11 | * 12 | * @author truelove@cyngn.com (Jeremy Truelove) 10/15/14 13 | */ 14 | public interface RestApi { 15 | 16 | /** 17 | * A header that contains a unique request id per request 18 | */ 19 | String X_REQUEST_ID = "x-request-id"; 20 | 21 | /** 22 | * The actual ip of the client 23 | */ 24 | String X_REAL_IP = "x-real-ip"; 25 | 26 | /** 27 | * Handle adding your APIs to the server's router 28 | * 29 | * @param router the object that does routing of requests to endpoint handlers 30 | * @return the initialized rest api 31 | */ 32 | default RestApi init(Router router) { 33 | if (supportedApi() != null) { 34 | for (RestApiDescriptor api : supportedApi()) { 35 | router.route(api.method, api.uri).handler(api.handler); 36 | } 37 | } 38 | 39 | return this; 40 | } 41 | 42 | /** 43 | * What APIs are currently being exposed by the implementation 44 | * 45 | * @return the API list 46 | */ 47 | RestApiDescriptor [] supportedApi(); 48 | 49 | /** 50 | * Dump the supported API 51 | * 52 | * @param logger the logger to use for output 53 | */ 54 | default void outputApi(Logger logger) { 55 | for (RestApiDescriptor anApi : supportedApi() ) { 56 | logger.info("{} - {}", anApi.method, anApi.uri); 57 | } 58 | } 59 | 60 | /** 61 | * A way to describe an API 62 | */ 63 | class RestApiDescriptor { 64 | public final HttpMethod method; 65 | public final String uri; 66 | public final Handler handler; 67 | 68 | public RestApiDescriptor(HttpMethod method, String uri, Handler handler) { 69 | this.method = method; 70 | this.uri = uri; 71 | this.handler = handler; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/web/RouterTools.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web; 2 | 3 | import io.vertx.core.Handler; 4 | import io.vertx.ext.web.Route; 5 | import io.vertx.ext.web.Router; 6 | import io.vertx.ext.web.RoutingContext; 7 | 8 | /** 9 | * A collection of helper functions for working with vertx-web. 10 | * 11 | * @author truelove@cyngn.com (Jeremy Truelove) 7/15/15 12 | */ 13 | public class RouterTools { 14 | 15 | /** 16 | * Register a handler on the base router for things that match nothing. 17 | * 18 | * @param router the router to add the noMatch routine on 19 | * @param noMatchHandler what to call if nothing matches 20 | * @return the added no match Route 21 | */ 22 | public static Route noMatch(Router router, Handler noMatchHandler) { 23 | return router.route().last().handler(noMatchHandler); 24 | } 25 | 26 | /** 27 | * Handles adding a number of handlers on the base route, aka match anything route the 'null' route 28 | * 29 | * @param router the router to add handlers too 30 | * @param handlers the handlers to add 31 | */ 32 | @SafeVarargs 33 | public static void registerRootHandlers(Router router, Handler ... handlers) { 34 | for (Handler handler : handlers) { 35 | router.route().handler(handler); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/cyngn/vertx/web/handler/RequestIdResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web.handler; 2 | 3 | import com.cyngn.vertx.web.RestApi; 4 | import io.vertx.core.Handler; 5 | import io.vertx.ext.web.RoutingContext; 6 | import org.apache.commons.lang.StringUtils; 7 | 8 | /** 9 | * Tags a response with the request id we associated to the request if it had the header present. 10 | * 11 | * @author truelove@cyngn.com (Jeremy Truelove) 09/05/15 12 | */ 13 | public class RequestIdResponseHandler implements Handler { 14 | 15 | private RequestIdResponseHandler(){} 16 | 17 | /** 18 | * Get a RequestIdResponseHandler 19 | * 20 | * @return reference to created handler 21 | */ 22 | public static RequestIdResponseHandler create() { return new RequestIdResponseHandler(); } 23 | 24 | @Override 25 | public void handle(RoutingContext ctx) { 26 | ctx.addHeadersEndHandler(aVoid -> { 27 | // grab the header from request and add to response if present 28 | String requestId = ctx.request().getHeader(RestApi.X_REQUEST_ID); 29 | if (StringUtils.isNotEmpty(requestId)) { 30 | ctx.response().putHeader(RestApi.X_REQUEST_ID, requestId); 31 | } 32 | }); 33 | ctx.next(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/async/LatchTests.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static junit.framework.Assert.assertTrue; 9 | 10 | /** 11 | * @author truelove@cyngn.com (Jeremy Truelove) 10/15/14 12 | */ 13 | public class LatchTests { 14 | 15 | @Test 16 | public void countdownTest() { 17 | final List results = new ArrayList<>(); 18 | Latch latch = new Latch(2, () -> results.add(true)); 19 | latch.complete(); 20 | latch.complete(); 21 | 22 | assertTrue(results.size() == 1); 23 | assertTrue(results.get(0)); 24 | 25 | latch.reset(3); 26 | results.clear(); 27 | latch.complete(); 28 | latch.complete(); 29 | 30 | assertTrue(results.size() == 0); 31 | } 32 | 33 | @Test(expected = IllegalStateException.class) 34 | public void failCompleteCallsAfterCompletedTest() { 35 | final List results = new ArrayList<>(); 36 | Latch latch = new Latch(2, () -> results.add(true)); 37 | latch.complete(); 38 | latch.complete(); 39 | 40 | assertTrue(results.size() == 1); 41 | assertTrue(results.get(0)); 42 | 43 | latch.complete(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/async/promise/PromiseImplTests.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.async.promise; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.ext.unit.Async; 5 | import io.vertx.ext.unit.TestContext; 6 | import io.vertx.ext.unit.junit.VertxUnitRunner; 7 | import org.junit.After; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | /** 17 | * Tests for PromisesImpl 18 | * 19 | * @author truelove@cyngn.com (Jeremy Truelove) 7/30/15 20 | */ 21 | @RunWith(VertxUnitRunner.class) 22 | public class PromiseImplTests { 23 | 24 | private Vertx vertx; 25 | 26 | @Before 27 | public void before(TestContext context) { 28 | vertx = Vertx.vertx(); 29 | } 30 | 31 | @After 32 | public void after(TestContext context) { 33 | vertx.close(); 34 | } 35 | 36 | @Test 37 | public void testBasic(TestContext context) { 38 | PromiseFactory factory = new PromiseFactory(vertx); 39 | 40 | Async async = context.async(); 41 | 42 | List foo = new ArrayList<>(); 43 | 44 | factory.createSerial((taskContext, onComplete) -> { 45 | foo.add(1); 46 | onComplete.accept(true); 47 | }).then((taskContext, onComplete) -> { 48 | foo.add(2); 49 | onComplete.accept(true); 50 | }).then((taskContext, onComplete) -> { 51 | foo.add(5); 52 | taskContext.put("data", foo); 53 | onComplete.accept(true); 54 | }).done((taskContext) -> { 55 | context.assertTrue(taskContext != null); 56 | context.assertTrue(taskContext.containsKey("data")); 57 | context.assertEquals(3, taskContext.getJsonArray("data").size()); 58 | context.assertEquals(1, taskContext.getJsonArray("data").getInteger(0)); 59 | context.assertEquals(5, taskContext.getJsonArray("data").getInteger(2)); 60 | async.complete(); 61 | }).eval(); 62 | } 63 | 64 | @Test(expected = IllegalStateException.class) 65 | public void testDoubleEval(TestContext context) { 66 | PromiseFactory factory = new PromiseFactory(vertx); 67 | factory.createSerial((taskContext, onComplete) -> onComplete.accept(true)).eval().eval(); 68 | } 69 | 70 | @Test(expected = IllegalStateException.class) 71 | public void testEvalOnEmptyPromise(TestContext context) { 72 | Promise.newInstance(vertx).eval(); 73 | } 74 | 75 | @Test 76 | public void testParallel(TestContext context) { 77 | PromiseFactory factory = new PromiseFactory(vertx); 78 | 79 | Async async = context.async(); 80 | 81 | List foo = new ArrayList<>(); 82 | 83 | factory.createParallel((taskContext, onComplete) -> { 84 | 85 | vertx.executeBlocking((future) -> { 86 | try { 87 | Thread.sleep(1000); 88 | } catch (Exception ex) { 89 | } 90 | future.complete(); 91 | }, asyncResult -> { 92 | foo.add(1); 93 | onComplete.accept(true); 94 | }); 95 | }, (taskContext, onComplete) -> { 96 | foo.add(2); 97 | taskContext.put("data", foo); 98 | onComplete.accept(true); 99 | }).done((taskContext) -> { 100 | context.assertTrue(taskContext != null); 101 | context.assertTrue(taskContext.containsKey("data")); 102 | context.assertEquals(2, taskContext.getJsonArray("data").size()); 103 | context.assertEquals(2, taskContext.getJsonArray("data").getInteger(0)); 104 | context.assertEquals(1, taskContext.getJsonArray("data").getInteger(1)); 105 | async.complete(); 106 | }).eval(); 107 | } 108 | 109 | @Test 110 | public void testAllInOrder(TestContext context) { 111 | PromiseFactory factory = new PromiseFactory(vertx); 112 | 113 | Async async = context.async(); 114 | 115 | List foo = new ArrayList<>(); 116 | 117 | factory.createParallel((taskContext, onComplete) -> { 118 | 119 | vertx.executeBlocking((future) -> { 120 | try { 121 | Thread.sleep(500); 122 | } catch (Exception ex) { 123 | } 124 | future.complete(); 125 | }, asyncResult -> { 126 | foo.add(1); 127 | onComplete.accept(true); 128 | }); 129 | }).allInOrder((taskContext, onComplete) -> { 130 | vertx.executeBlocking((future) -> { 131 | try { 132 | Thread.sleep(500); 133 | } catch (Exception ex) { 134 | } 135 | future.complete(); 136 | }, asyncResult -> { 137 | foo.add(3); 138 | onComplete.accept(true); 139 | }); 140 | }, (taskContext, onComplete) -> { 141 | foo.add(2); 142 | taskContext.put("data", foo); 143 | onComplete.accept(true); 144 | }).done((taskContext) -> { 145 | context.assertTrue(taskContext != null); 146 | context.assertTrue(taskContext.containsKey("data")); 147 | context.assertEquals(3, taskContext.getJsonArray("data").size()); 148 | context.assertEquals(1, taskContext.getJsonArray("data").getInteger(0)); 149 | context.assertEquals(3, taskContext.getJsonArray("data").getInteger(1)); 150 | context.assertEquals(2, taskContext.getJsonArray("data").getInteger(2)); 151 | async.complete(); 152 | }).eval(); 153 | } 154 | 155 | @Test 156 | public void testExcept(TestContext context) { 157 | PromiseFactory factory = new PromiseFactory(vertx); 158 | 159 | Async async = context.async(); 160 | 161 | factory.createSerial((taskContext, onComplete) -> { 162 | taskContext.put("reason", "something bad"); 163 | onComplete.accept(false); 164 | }, (taskContext, onComplete) -> { 165 | context.fail("This should never be reached"); 166 | }).done((taskContext) -> { 167 | context.fail("shouldn't call done on failure"); 168 | }).except(taskContext -> { 169 | context.assertTrue(taskContext != null); 170 | context.assertTrue(taskContext.containsKey("reason")); 171 | async.complete(); 172 | }).eval(); 173 | } 174 | 175 | @Test 176 | public void testTimeout(TestContext context) { 177 | PromiseFactory factory = new PromiseFactory(vertx); 178 | 179 | Async async = context.async(); 180 | 181 | factory.createSerial((taskContext, onComplete) -> { 182 | // do nothing, aka don't hit the callback 183 | }, (taskContext, onComplete) -> { 184 | context.fail("This should never be reached"); 185 | }).done((taskContext) -> { 186 | context.fail("shouldn't call done on failure"); 187 | }).timeout(1000) 188 | .except(taskContext -> { 189 | context.assertTrue(taskContext != null); 190 | context.assertTrue(taskContext.containsKey(Promise.CONTEXT_FAILURE_KEY)); 191 | context.assertTrue(taskContext.getString(Promise.CONTEXT_FAILURE_KEY).indexOf("timed out") != -1); 192 | async.complete(); 193 | }).eval(); 194 | } 195 | 196 | @Test 197 | public void testTimeoutCancelled(TestContext context) { 198 | PromiseFactory factory = new PromiseFactory(vertx); 199 | 200 | Async async = context.async(); 201 | 202 | AtomicInteger count = new AtomicInteger(0); 203 | 204 | Promise promise = factory.createSerial((taskContext, onComplete) -> { 205 | count.incrementAndGet(); 206 | onComplete.accept(true); 207 | }, (taskContext, onComplete) -> { 208 | count.incrementAndGet(); 209 | onComplete.accept(true); 210 | }).done((taskContext) -> count.incrementAndGet()) 211 | .timeout(500) 212 | .except(taskContext -> { 213 | context.fail("We should not get here due to timeout"); 214 | }).eval(); 215 | 216 | vertx.setTimer(2000, (timer) -> { 217 | context.assertEquals(3, count.get()); 218 | context.assertTrue(promise.succeeded()); 219 | async.complete(); 220 | }); 221 | } 222 | 223 | @Test 224 | public void testExceptionOnCallback(TestContext context) { 225 | PromiseFactory factory = new PromiseFactory(vertx); 226 | 227 | Async async = context.async(); 228 | 229 | factory.createSerial((taskContext, onComplete) -> { 230 | throw new RuntimeException(); 231 | }, (taskContext, onComplete) -> { 232 | context.fail("This should never be reached"); 233 | }).done((taskContext) -> { 234 | context.fail("shouldn't call done on failure"); 235 | }).except((taskContext) -> { 236 | context.assertTrue(taskContext != null); 237 | context.assertTrue(taskContext.containsKey(Promise.CONTEXT_FAILURE_KEY)); 238 | context.assertTrue(taskContext.getString(Promise.CONTEXT_FAILURE_KEY).indexOf("RuntimeException") != -1); 239 | async.complete(); 240 | }).eval(); 241 | } 242 | 243 | @Test 244 | public void testIsEmpty(TestContext context) { 245 | PromiseFactory factory = new PromiseFactory(vertx); 246 | Promise p = factory.create().then((pContext, onResult) -> {}); 247 | 248 | context.assertFalse(p.isEmpty()); 249 | 250 | Promise p2 = factory.create(); 251 | 252 | context.assertTrue(p2.isEmpty()); 253 | } 254 | 255 | } -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/client/ServiceClientTest.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.client; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.json.JsonObject; 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Tests {@link ServiceClient} 11 | * 12 | * @author asarda@cyngn.com (Ajay Sarda) on 8/25/15. 13 | */ 14 | public class ServiceClientTest { 15 | 16 | private Vertx vertx; 17 | 18 | @Before 19 | public void before() { 20 | vertx = Vertx.vertx(); 21 | } 22 | 23 | @Test(expected = IllegalArgumentException.class) 24 | public void testDuplicationAPIThrowException() { 25 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 26 | builder.addApiTimeout("api", 1000L); 27 | builder.addApiTimeout("api", 1000L); 28 | } 29 | 30 | @Test 31 | public void testGetTimeout() { 32 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 33 | builder.withHost("localhost").withPort(1234).withTimeout(10); 34 | ServiceClient serviceClient = builder.build(); 35 | Assert.assertTrue(10L == serviceClient.getTimeout("api")); 36 | } 37 | 38 | @Test 39 | public void testGetApiTimeout() { 40 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 41 | builder.withHost("localhost").withPort(1234); 42 | builder.addApiTimeout("api", 1000L); 43 | ServiceClient serviceClient = builder.build(); 44 | Assert.assertTrue(1000L == serviceClient.getTimeout("api")); 45 | } 46 | 47 | @Test 48 | public void testGetApiTimeoutOverriden() { 49 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 50 | builder.withHost("localhost").withPort(1234).withTimeout(10); 51 | builder.addApiTimeout("api", 1000L); 52 | ServiceClient serviceClient = builder.build(); 53 | Assert.assertTrue(1000L == serviceClient.getTimeout("api")); 54 | } 55 | 56 | @Test 57 | public void testGetTimeoutOnNoTimeout() { 58 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 59 | builder.withHost("localhost").withPort(1234); 60 | ServiceClient serviceClient = builder.build(); 61 | Assert.assertTrue(0L == serviceClient.getTimeout("api")); 62 | } 63 | 64 | @Test(expected = IllegalArgumentException.class) 65 | public void testNegativeApiTimeout() { 66 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 67 | builder.withHost("localhost").withPort(1234); 68 | builder.addApiTimeout("api", -1000L); 69 | } 70 | 71 | @Test(expected = IllegalArgumentException.class) 72 | public void testNegativeTimeout() { 73 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 74 | builder.withHost("localhost").withPort(1234); 75 | builder.withTimeout(-1); 76 | } 77 | 78 | @Test(expected = IllegalArgumentException.class) 79 | public void testBuildWithoutHost() { 80 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 81 | builder.build(); 82 | } 83 | 84 | @Test(expected = IllegalArgumentException.class) 85 | public void testBuildWithoutPort() { 86 | ServiceClient.Builder builder = new ServiceClient.Builder(vertx); 87 | builder.withHost("localhost"); 88 | builder.build(); 89 | } 90 | 91 | @Test (expected = IllegalArgumentException.class) 92 | public void testBuildFromJsonConfigWithoutHost() { 93 | JsonObject config = new JsonObject(); 94 | ServiceClient.create(vertx, config); 95 | } 96 | 97 | @Test (expected = IllegalArgumentException.class) 98 | public void testBuildFromJsonConfigWithoutPort() { 99 | JsonObject config = new JsonObject(); 100 | config.put(ServiceClient.HOST, "localhost"); 101 | ServiceClient.create(vertx, config); 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/eventbus/EventBusToolsTest.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.eventbus; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.eventbus.EventBus; 5 | import io.vertx.core.json.JsonObject; 6 | import io.vertx.ext.unit.Async; 7 | import io.vertx.ext.unit.TestContext; 8 | import io.vertx.ext.unit.junit.VertxUnitRunner; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | /** 15 | * @author truelove@cyngn.com (Jeremy Truelove) 4/24/15 16 | */ 17 | @RunWith(VertxUnitRunner.class) 18 | public class EventBusToolsTest { 19 | 20 | 21 | 22 | @Test 23 | public void testSingleConsumer(TestContext context) { 24 | EventBus bus = Vertx.vertx().eventBus(); 25 | 26 | AtomicInteger counter = new AtomicInteger(0); 27 | EventBusTools.oneShotConsumer(bus, "test_message", event -> { 28 | counter.incrementAndGet(); 29 | }); 30 | 31 | Async async = context.async(); 32 | bus.send("test_message", new JsonObject().put("foo", "bar")); 33 | bus.send("test_message", new JsonObject()); 34 | 35 | Vertx.vertx().setTimer(500, event -> { 36 | context.assertEquals(1, counter.get()); 37 | async.complete(); 38 | }); 39 | } 40 | 41 | @Test 42 | public void testMulitConsumer(TestContext context) { 43 | EventBus bus = Vertx.vertx().eventBus(); 44 | 45 | AtomicInteger counter = new AtomicInteger(0); 46 | EventBusTools.consumeNTimes(bus, "test_message", event -> { 47 | counter.incrementAndGet(); 48 | }, 5, false); 49 | 50 | Async async = context.async(); 51 | bus.send("test_message", new JsonObject().put("foo", "bar")); 52 | bus.send("test_message", new JsonObject()); 53 | bus.send("test_message", new JsonObject().put("foo", "bar")); 54 | bus.send("test_message", new JsonObject().put("foo", "bar")); 55 | bus.send("test_message", new JsonObject().put("foo", "bar")); 56 | bus.send("test_message", new JsonObject().put("foo", "test")); 57 | 58 | Vertx.vertx().setTimer(500, event -> { 59 | context.assertEquals(5, counter.get()); 60 | async.complete(); 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/web/HttpHelperTest.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | /** 7 | * @author truelove@cyngn.com (Jeremy Truelove) 8/21/15 8 | */ 9 | public class HttpHelperTest { 10 | @Test 11 | public void testIsHttpErrorResponse() { 12 | Assert.assertTrue(!HttpHelper.isHttp2XXResponse(199)); 13 | Assert.assertTrue(!HttpHelper.isHttp2XXResponse(301)); 14 | Assert.assertTrue(HttpHelper.isHttp2XXResponse(250)); 15 | Assert.assertTrue(HttpHelper.isHttp2XXResponse(200)); 16 | Assert.assertTrue(HttpHelper.isHttp2XXResponse(299)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/web/JsonUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import io.vertx.core.json.JsonObject; 6 | import org.junit.Test; 7 | 8 | import java.time.LocalDateTime; 9 | import java.time.ZoneId; 10 | import java.time.ZonedDateTime; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | import static org.junit.Assert.assertNull; 14 | import static org.junit.Assert.assertTrue; 15 | 16 | /** 17 | * @author truelove@cyngn.com (Jeremy Truelove) 2/4/15 18 | */ 19 | public class JsonUtilTest { 20 | 21 | static class Foo { 22 | @JsonProperty 23 | public String bar; 24 | 25 | @JsonProperty 26 | public int testField; 27 | 28 | @JsonIgnore 29 | public String dontTouch; 30 | 31 | public Foo() { 32 | } 33 | } 34 | 35 | class Bar { 36 | public String foo; 37 | } 38 | 39 | @Test 40 | public void testJsonDeserialize() { 41 | String testStr = "{\"bar\":\"testStr\",\"testField\":5}"; 42 | 43 | Foo f = JsonUtil.parseJsonToObject(testStr, Foo.class); 44 | 45 | assertEquals("testStr", f.bar); 46 | assertEquals(5, f.testField); 47 | } 48 | 49 | @Test 50 | public void testInvalidJsonDeserialize() { 51 | Foo f = JsonUtil.parseJsonToObject((String) null, Foo.class); 52 | 53 | assertNull(f); 54 | } 55 | 56 | @Test 57 | public void testJsonSerialize() { 58 | Foo f = new Foo(); 59 | f.bar = "testStr"; 60 | f.testField = 5; 61 | f.dontTouch = "notMe"; 62 | 63 | String testStr = JsonUtil.getJsonForObject(f); 64 | 65 | JsonObject jsonObject = new JsonObject(testStr); 66 | assertTrue(jsonObject.getString("bar").equals(f.bar)); 67 | assertTrue(!jsonObject.containsKey("dontTouch")); 68 | 69 | assertEquals(testStr, jsonObject.toString()); 70 | } 71 | 72 | @Test(expected=IllegalArgumentException.class) 73 | public void testBadObjectJsonSerialize() { 74 | String testStr = JsonUtil.getJsonForObject(null); 75 | } 76 | 77 | @Test 78 | public void testInvalidJsonDeserializeBytes() { 79 | String badJson = "{[}"; 80 | Foo f = JsonUtil.parseJsonToObject(badJson.getBytes(), Foo.class); 81 | assertNull(f); 82 | } 83 | 84 | @Test 85 | public void testNullJsonDeserializeBytes() { 86 | Foo f = JsonUtil.parseJsonToObject((byte[])null, Foo.class); 87 | assertNull(f); 88 | } 89 | 90 | @Test 91 | public void testJavaTimeSerialization(){ 92 | LocalDateTime then = LocalDateTime.now(); 93 | 94 | ZonedDateTime utcZdt = ZonedDateTime.of(then, ZoneId.of("UTC")); 95 | String utcZdtJson = JsonUtil.getJsonForObject(utcZdt); 96 | ZonedDateTime utcZdtResurrected = JsonUtil.parseJsonToObject(utcZdtJson, ZonedDateTime.class); 97 | assertTrue(utcZdt.compareTo(utcZdtResurrected) == 0); 98 | 99 | ZonedDateTime gmtZdt = ZonedDateTime.of(then, ZoneId.of("GMT")); 100 | String gmtZdtJson = JsonUtil.getJsonForObject(gmtZdt); 101 | ZonedDateTime gmtZdtResurrected = JsonUtil.parseJsonToObject(gmtZdtJson, ZonedDateTime.class); 102 | assertTrue(gmtZdt.compareTo(gmtZdtResurrected) != 0);//our default is UTC, and UTC != GMT 103 | assertTrue(utcZdtResurrected.compareTo(gmtZdtResurrected) == 0);//however, both resurrected values match 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/web/RouterToolsTest.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web; 2 | 3 | import io.vertx.core.Handler; 4 | import io.vertx.core.Vertx; 5 | import io.vertx.ext.unit.TestContext; 6 | import io.vertx.ext.unit.junit.VertxUnitRunner; 7 | import io.vertx.ext.web.Route; 8 | import io.vertx.ext.web.Router; 9 | import io.vertx.ext.web.RoutingContext; 10 | import io.vertx.ext.web.handler.BodyHandler; 11 | import io.vertx.ext.web.handler.LoggerHandler; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author truelove@cyngn.com (Jeremy Truelove) 7/15/15 17 | */ 18 | @RunWith(VertxUnitRunner.class) 19 | public class RouterToolsTest { 20 | 21 | @Test 22 | public void testNoMatch(TestContext testContext) { 23 | 24 | Vertx vertx = Vertx.vertx(); 25 | 26 | Router r = Router.router(vertx); 27 | 28 | r.route().handler(LoggerHandler.create()); 29 | r.route().handler(BodyHandler.create()); 30 | 31 | r.get("/someRandomPath").handler(context -> {}); 32 | 33 | Handler foo = context -> {}; 34 | Route noMatch = RouterTools.noMatch(r, foo); 35 | 36 | testContext.assertEquals(4, r.getRoutes().size()); 37 | 38 | // no match should be last since we set it to the last handler on the router 39 | testContext.assertEquals(noMatch, r.getRoutes().get(3)); 40 | } 41 | 42 | @Test 43 | public void testRootHandlers(TestContext testContext) { 44 | 45 | Vertx vertx = Vertx.vertx(); 46 | 47 | Router r = Router.router(vertx); 48 | 49 | RouterTools.registerRootHandlers(r, LoggerHandler.create(), BodyHandler.create()); 50 | r.post("/fooBar").handler(context -> {}); 51 | 52 | testContext.assertEquals(3, r.getRoutes().size()); 53 | 54 | // no match should be last since we set it to the last handler on the router 55 | testContext.assertEquals(null, r.getRoutes().get(0).getPath()); 56 | testContext.assertEquals(null, r.getRoutes().get(1).getPath()); 57 | testContext.assertEquals("/fooBar", r.getRoutes().get(2).getPath()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/com/cyngn/vertx/web/handler/RequestIdResponseHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.cyngn.vertx.web.handler; 2 | 3 | import com.cyngn.vertx.web.RestApi; 4 | import io.vertx.core.http.HttpMethod; 5 | import io.vertx.ext.web.WebTestBase; 6 | import org.junit.Test; 7 | 8 | /** 9 | * @author truelove@cyngn.com (Jeremy Truelove) 9/5/15 10 | */ 11 | public class RequestIdResponseHandlerTest extends WebTestBase { 12 | 13 | @Test 14 | public void testRequestId() throws Exception { 15 | router.route().handler(RequestIdResponseHandler.create()); 16 | router.route().handler(rc -> { 17 | rc.response().end(); 18 | }); 19 | testRequest(HttpMethod.GET, "/", req -> req.putHeader(RestApi.X_REQUEST_ID, "aTestId"), 20 | resp -> { 21 | String idHeader = resp.headers().get(RestApi.X_REQUEST_ID); 22 | assertNotNull(idHeader); 23 | assertEquals("aTestId", idHeader); 24 | }, 25 | 200, "OK", null); 26 | } 27 | 28 | @Test 29 | public void testRequestIdAbsent() throws Exception { 30 | router.route().handler(RequestIdResponseHandler.create()); 31 | router.route().handler(rc -> { 32 | rc.response().end(); 33 | }); 34 | testRequest(HttpMethod.GET, "/", null, 35 | resp -> { 36 | String idHeader = resp.headers().get(RestApi.X_REQUEST_ID); 37 | assertNull(idHeader); 38 | }, 39 | 200, "OK", null); 40 | } 41 | } 42 | --------------------------------------------------------------------------------