├── .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 | [](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 |
--------------------------------------------------------------------------------