├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENSE ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ ├── com │ │ └── vgrazi │ │ │ └── play │ │ │ ├── PriceTick.java │ │ │ ├── SomeFeed.java │ │ │ └── SomeListener.java │ └── io │ │ └── projectreactor │ │ └── examples │ │ ├── MyReactiveLibrary.java │ │ ├── Sir.java │ │ └── spring │ │ ├── ByexampleApplication.java │ │ ├── DataExampleController.java │ │ ├── ExampleController.java │ │ └── FakeReactiveRepository.java └── resources │ └── application.properties └── test └── java └── io └── projectreactor └── examples ├── CustomSourceSnippets.java ├── MyReactiveLibraryTest.java ├── ReactorSnippets.java └── spring └── SpringTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | ### OSX template 26 | *.DS_Store 27 | .AppleDouble 28 | .LSOverride 29 | 30 | # Icon must end with two \r 31 | Icon 32 | 33 | # Thumbnails 34 | ._* 35 | 36 | # Files that might appear in the root of a volume 37 | .DocumentRevisions-V100 38 | .fseventsd 39 | .Spotlight-V100 40 | .TemporaryItems 41 | .Trashes 42 | .VolumeIcon.icns 43 | .com.apple.timemachine.donotpresent 44 | 45 | # Directories potentially created on remote AFP share 46 | .AppleDB 47 | .AppleDesktop 48 | Network Trash Folder 49 | Temporary Items 50 | .apdisk 51 | ### Windows template 52 | # Windows image file caches 53 | Thumbs.db 54 | ehthumbs.db 55 | 56 | # Folder config file 57 | Desktop.ini 58 | 59 | # Recycle Bin used on file shares 60 | $RECYCLE.BIN/ 61 | 62 | # Windows Installer files 63 | *.cab 64 | *.msi 65 | *.msm 66 | *.msp 67 | 68 | # Windows shortcuts 69 | *.lnk 70 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonbasle-demos/reactor-by-example/bef3470d5d0709a4b5ce0a88db85d9d00c141eb5/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Simon Baslé 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reactor by Example 2 | 3 | This project contains an executable version of snippets of code found in 4 | the ["Reactor by Example" InfoQ article](https://www.infoq.com/articles/reactor-by-example). 5 | 6 | Most of the snippets are found in the tests, but Spring usage is demonstrated 7 | from the main classes. 8 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 233 | ${WRAPPER_LAUNCHER} "$@" 234 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.projectreactor.examples 7 | byexample 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | byexample 12 | Reactor by Example 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.0.BUILD-SNAPSHOT 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot.experimental 30 | spring-boot-starter-web-reactive 31 | 32 | 33 | 34 | org.springframework.data 35 | spring-data-commons 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-test 41 | test 42 | 43 | 44 | 45 | io.projectreactor.addons 46 | reactor-test 47 | 3.0.3.RELEASE 48 | test 49 | 50 | 51 | org.assertj 52 | assertj-core 53 | 3.5.2 54 | test 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.springframework.data 62 | spring-data-releasetrain 63 | Kay-M1 64 | import 65 | pom 66 | 67 | 68 | org.springframework.boot.experimental 69 | spring-boot-dependencies-web-reactive 70 | 0.1.0.BUILD-SNAPSHOT 71 | pom 72 | import 73 | 74 | 75 | org.springframework.data 76 | spring-data-releasetrain 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 89 | 90 | 91 | 92 | 93 | 94 | spring-snapshots 95 | Spring Snapshots 96 | https://repo.spring.io/snapshot 97 | 98 | true 99 | 100 | 101 | 102 | spring-milestones 103 | Spring Milestones 104 | https://repo.spring.io/milestone 105 | 106 | false 107 | 108 | 109 | 110 | 111 | 112 | spring-snapshots 113 | Spring Snapshots 114 | https://repo.spring.io/snapshot 115 | 116 | true 117 | 118 | 119 | 120 | spring-milestones 121 | Spring Milestones 122 | https://repo.spring.io/milestone 123 | 124 | false 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /src/main/java/com/vgrazi/play/PriceTick.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2016 Victor J Grazi 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.vgrazi.play; 25 | 26 | import java.text.DecimalFormat; 27 | import java.text.NumberFormat; 28 | import java.text.SimpleDateFormat; 29 | import java.util.Date; 30 | 31 | /** 32 | * @author Victor J Grazi 33 | */ 34 | //source: https://github.com/vgrazi/rxjava-snippets/ 35 | public class PriceTick { 36 | private final int sequence; 37 | private final Date date; 38 | private final String instrument; 39 | private final double price; 40 | private final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("hh:mm:ss"); 41 | 42 | 43 | public PriceTick(int sequence, Date date, String instrument, double price) { 44 | this.sequence = sequence; 45 | this.date = date; 46 | this.instrument = instrument; 47 | this.price = price; 48 | } 49 | 50 | public int getSequence() { 51 | return sequence; 52 | } 53 | 54 | public Date getDate() { 55 | return date; 56 | } 57 | 58 | public String getInstrument() { 59 | return instrument; 60 | } 61 | 62 | public double getPrice() { 63 | return price; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return String.format("%6d %s %s %s", sequence, DATE_FORMAT.format(new Date()), instrument, price); 69 | } 70 | 71 | public boolean isLast() { 72 | // return false; 73 | return sequence >= 10; 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /src/main/java/com/vgrazi/play/SomeFeed.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2016 Victor J Grazi 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.vgrazi.play; 25 | 26 | 27 | import java.text.SimpleDateFormat; 28 | import java.util.Date; 29 | import java.util.LinkedList; 30 | import java.util.List; 31 | import java.util.Random; 32 | import java.util.concurrent.BrokenBarrierException; 33 | import java.util.concurrent.CyclicBarrier; 34 | import java.util.concurrent.ExecutorService; 35 | import java.util.concurrent.Executors; 36 | import java.util.concurrent.atomic.AtomicInteger; 37 | 38 | /** 39 | * @author Victor J Grazi 40 | */ 41 | //source: https://github.com/vgrazi/rxjava-snippets/ 42 | public class SomeFeed { 43 | private final boolean barriered; 44 | private AtomicInteger threadcounter = new AtomicInteger(1); 45 | 46 | private ExecutorService service = Executors.newCachedThreadPool(r -> { 47 | Thread thread = new Thread(r); 48 | thread.setName("Thread " + threadcounter.getAndIncrement()); 49 | return thread; 50 | }); 51 | private transient boolean running = true; 52 | 53 | private List listeners = new LinkedList<>(); 54 | private int threadCount; 55 | private CyclicBarrier barrier; 56 | 57 | private final Random RANDOM = new Random(0); 58 | private static final Random RANDOM_PRICE = new Random(0); 59 | 60 | private static final String[] instruments = {"IBM", "NMR", "BAC", "AAPL", "MSFT"}; 61 | 62 | public SomeFeed() { 63 | this(instruments.length); 64 | } 65 | 66 | public SomeFeed(int threadCount) { 67 | this(threadCount, false); 68 | } 69 | 70 | public SomeFeed(int threadCount, boolean barriered) { 71 | this.threadCount = threadCount; 72 | this.barriered = barriered; 73 | if (barriered) { 74 | barrier = new CyclicBarrier(threadCount, System.out::println); 75 | } 76 | launchPublishers(); 77 | } 78 | 79 | 80 | AtomicInteger sequence = new AtomicInteger(1); 81 | private void launchEventThread(String instrument, double startingPrice) { 82 | service.execute(() -> 83 | { 84 | final Object MUTEX = new Object(); 85 | SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss.SSS"); 86 | double price = startingPrice; 87 | while (running) { 88 | try { 89 | if (barriered) { 90 | barrier.await(); 91 | } 92 | price += RANDOM_PRICE.nextGaussian(); 93 | 94 | double finalPrice = price; 95 | listeners.forEach(subscriber -> { 96 | PriceTick tick = new PriceTick(sequence.getAndIncrement(), new Date(), instrument, finalPrice); 97 | String message = String.format("%s %s %s", format.format(new Date()), instrument, finalPrice); 98 | // Logger.print("Notifying " + message); 99 | subscriber.priceTick(tick); 100 | }); 101 | synchronized (MUTEX) { 102 | MUTEX.wait(RANDOM.nextInt(200) + 800); 103 | } 104 | } catch (InterruptedException | BrokenBarrierException e) { 105 | e.printStackTrace(); 106 | } 107 | } 108 | }); 109 | } 110 | 111 | double[] prices = {160, 5, 15, 108, 57}; 112 | void launchPublishers() { 113 | System.out.println("Launching publishers"); 114 | for (int i = 0; i < threadCount; i++) { 115 | launchEventThread(instruments[i%instruments.length], prices[i%prices.length]); 116 | } 117 | } 118 | 119 | public void register(SomeListener listener) { 120 | System.out.println("Registering subscriber " + listener); 121 | listeners.add(listener); 122 | } 123 | 124 | public void terminate() { 125 | running = false; 126 | } 127 | 128 | } -------------------------------------------------------------------------------- /src/main/java/com/vgrazi/play/SomeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2016 Victor J Grazi 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package com.vgrazi.play; 25 | 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | /** 29 | * @author Victor J Grazi 30 | */ 31 | //source: https://github.com/vgrazi/rxjava-snippets/ 32 | public abstract class SomeListener { 33 | private static final AtomicInteger COUNTER = new AtomicInteger(1); 34 | private final int ID; 35 | public SomeListener() 36 | { 37 | ID = COUNTER.getAndIncrement(); 38 | } 39 | 40 | public abstract void priceTick(PriceTick event); 41 | public abstract void error(Throwable throwable); 42 | 43 | @Override 44 | public String toString() { 45 | return String.format("Listener ID:%d:%s", ID, super.toString()); 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/io/projectreactor/examples/MyReactiveLibrary.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples; 2 | 3 | import java.time.Duration; 4 | 5 | import reactor.core.publisher.Flux; 6 | import reactor.core.publisher.Mono; 7 | 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * @author Simon Baslé 12 | */ 13 | @Component 14 | public class MyReactiveLibrary { 15 | 16 | public Flux alphabet5(char from) { 17 | int rangeSize = Math.min(5, 'z' - from + 1); 18 | return Flux.range((int) from, rangeSize) 19 | .map(i -> "" + (char) i.intValue()); 20 | } 21 | 22 | public Flux alphabet5fixed(char from) { 23 | return Flux.range((int) from, 5) 24 | .map(i -> "" + (char) i.intValue()) 25 | .take(Math.min(5, 'z' - from + 1)); 26 | } 27 | 28 | public Mono withDelay(String value, int delaySeconds) { 29 | return Mono.just(value) 30 | .delaySubscription(Duration.ofSeconds(delaySeconds)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/projectreactor/examples/Sir.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples; 2 | 3 | /** 4 | * @author Simon Baslé 5 | */ 6 | public class Sir { 7 | 8 | private String firstName; 9 | private String lastName; 10 | 11 | public Sir() { 12 | } 13 | 14 | public Sir(String firstName, String lastName) { 15 | this.firstName = firstName; 16 | this.lastName = lastName; 17 | } 18 | 19 | public void setFirstName(String firstName) { 20 | this.firstName = firstName; 21 | } 22 | 23 | public String getFirstName() { 24 | return firstName; 25 | } 26 | 27 | public void setLastName(String lastName) { 28 | this.lastName = lastName; 29 | } 30 | 31 | public String getLastName() { 32 | return lastName; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (this == o) { 38 | return true; 39 | } 40 | if (o == null || getClass() != o.getClass()) { 41 | return false; 42 | } 43 | 44 | Sir sir = (Sir) o; 45 | 46 | if (firstName != null ? !firstName.equals(sir.firstName) : 47 | sir.firstName != null) { 48 | return false; 49 | } 50 | return lastName != null ? lastName.equals(sir.lastName) : sir.lastName == null; 51 | 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | int result = firstName != null ? firstName.hashCode() : 0; 57 | result = 31 * result + (lastName != null ? lastName.hashCode() : 0); 58 | return result; 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/io/projectreactor/examples/spring/ByexampleApplication.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples.spring; 2 | 3 | import io.projectreactor.examples.MyReactiveLibrary; 4 | 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | @SpringBootApplication 10 | public class ByexampleApplication { 11 | 12 | @Bean 13 | public MyReactiveLibrary reactiveLibrary() { 14 | return new MyReactiveLibrary(); 15 | } 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(ByexampleApplication.class, args); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/projectreactor/examples/spring/DataExampleController.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples.spring; 2 | 3 | import io.projectreactor.examples.Sir; 4 | import reactor.core.publisher.Mono; 5 | 6 | import org.springframework.data.repository.reactive.ReactiveCrudRepository; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | /** 13 | * @author Simon Baslé 14 | */ 15 | @RestController 16 | public class DataExampleController { 17 | 18 | private final ReactiveCrudRepository reactiveRepository; 19 | 20 | //Note Spring Boot 4.3+ autowires single constructors now 21 | public DataExampleController(ReactiveCrudRepository repo) { 22 | this.reactiveRepository = repo; 23 | } 24 | 25 | @GetMapping("data/{who}") 26 | public Mono> hello(@PathVariable String who) { 27 | return reactiveRepository.findOne(who) 28 | .map(ResponseEntity::ok) 29 | .defaultIfEmpty(ResponseEntity.status(404).body(null)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/projectreactor/examples/spring/ExampleController.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples.spring; 2 | 3 | import io.projectreactor.examples.MyReactiveLibrary; 4 | import io.projectreactor.examples.Sir; 5 | import reactor.core.publisher.Flux; 6 | import reactor.core.publisher.Mono; 7 | 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | /** 15 | * @author Simon Baslé 16 | */ 17 | @RestController 18 | public class ExampleController { 19 | 20 | private final MyReactiveLibrary reactiveLibrary; 21 | 22 | //Note Spring Boot 4.3+ autowires single constructors now 23 | public ExampleController(MyReactiveLibrary reactiveLibrary) { 24 | this.reactiveLibrary = reactiveLibrary; 25 | } 26 | 27 | @GetMapping("hello/{who}") 28 | public Mono hello(@PathVariable String who) { 29 | return Mono.just(who) 30 | .map(w -> "Hello " + w + "!"); 31 | } 32 | 33 | @GetMapping("helloDelay/{who}") 34 | public Mono helloDelay(@PathVariable String who) { 35 | return reactiveLibrary.withDelay("Hello " + who + "!!", 2); 36 | } 37 | 38 | @PostMapping("heyMister") 39 | public Flux hey(@RequestBody Mono body) { 40 | return Mono.just("Hey mister ") 41 | .concatWith(body 42 | .flatMap(sir -> Flux.fromArray(sir.getLastName().split(""))) 43 | .map(String::toUpperCase) 44 | .take(1) 45 | ).concatWith(Mono.just(". how are you?")); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/projectreactor/examples/spring/FakeReactiveRepository.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples.spring; 2 | 3 | import io.projectreactor.examples.Sir; 4 | import org.reactivestreams.Publisher; 5 | import reactor.core.publisher.Flux; 6 | import reactor.core.publisher.Mono; 7 | 8 | import org.springframework.data.repository.reactive.ReactiveCrudRepository; 9 | import org.springframework.stereotype.Repository; 10 | 11 | /** 12 | * A fake incomplete Spring Data {@link ReactiveCrudRepository} to naively demonstrate 13 | * a reactive findOne. 14 | * 15 | * @author Simon Baslé 16 | */ 17 | @Repository 18 | public class FakeReactiveRepository implements ReactiveCrudRepository { 19 | 20 | @Override 21 | public Mono findOne(String s) { 22 | return Mono.just(s) 23 | .then(id -> { 24 | if ("notfound".equalsIgnoreCase(s)) 25 | return Mono.empty(); 26 | else 27 | return Mono.just(new Sir(s, "GUY")); 28 | }); 29 | } 30 | 31 | //== NOT IMPLEMENTED == 32 | @Override 33 | public Mono save(S s) { 34 | return null; 35 | } 36 | 37 | @Override 38 | public Flux save(Iterable iterable) { 39 | return null; 40 | } 41 | 42 | @Override 43 | public Flux save(Publisher publisher) { 44 | return null; 45 | } 46 | 47 | @Override 48 | public Mono findOne(Mono s) { 49 | return null; 50 | } 51 | 52 | @Override 53 | public Mono exists(String s) { 54 | return null; 55 | } 56 | 57 | @Override 58 | public Mono exists(Mono mono) { 59 | return null; 60 | } 61 | 62 | @Override 63 | public Flux findAll() { 64 | return null; 65 | } 66 | 67 | @Override 68 | public Flux findAll(Iterable iterable) { 69 | return null; 70 | } 71 | 72 | @Override 73 | public Flux findAll(Publisher publisher) { 74 | return null; 75 | } 76 | 77 | @Override 78 | public Mono count() { 79 | return null; 80 | } 81 | 82 | @Override 83 | public Mono delete(String s) { 84 | return null; 85 | } 86 | 87 | @Override 88 | public Mono delete(Sir sir) { 89 | return null; 90 | } 91 | 92 | @Override 93 | public Mono delete(Iterable iterable) { 94 | return null; 95 | } 96 | 97 | @Override 98 | public Mono delete(Publisher publisher) { 99 | return null; 100 | } 101 | 102 | @Override 103 | public Mono deleteAll() { 104 | return null; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonbasle-demos/reactor-by-example/bef3470d5d0709a4b5ce0a88db85d9d00c141eb5/src/main/resources/application.properties -------------------------------------------------------------------------------- /src/test/java/io/projectreactor/examples/CustomSourceSnippets.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples; 2 | 3 | import com.vgrazi.play.PriceTick; 4 | import com.vgrazi.play.SomeFeed; 5 | import com.vgrazi.play.SomeListener; 6 | import org.junit.Test; 7 | import reactor.core.publisher.ConnectableFlux; 8 | import reactor.core.publisher.Flux; 9 | import reactor.core.publisher.FluxSink; 10 | 11 | /** 12 | * @author Simon Baslé 13 | */ 14 | public class CustomSourceSnippets { 15 | 16 | @Test 17 | public void create() throws InterruptedException { 18 | SomeFeed feed = new SomeFeed<>(); 19 | Flux flux = 20 | Flux.create(emitter -> 21 | { 22 | SomeListener listener = new SomeListener() { 23 | @Override 24 | public void priceTick(PriceTick event) { 25 | emitter.next(event); 26 | if (event.isLast()) { 27 | emitter.complete(); 28 | } 29 | } 30 | 31 | @Override 32 | public void error(Throwable e) { 33 | emitter.error(e); 34 | }}; 35 | feed.register(listener); 36 | }, FluxSink.OverflowStrategy.BUFFER); 37 | 38 | ConnectableFlux hot = flux.publish(); 39 | 40 | hot.subscribe(priceTick -> System.out.printf("%s %4s %6.2f%n", priceTick 41 | .getDate(), priceTick.getInstrument(), priceTick.getPrice())); 42 | 43 | hot.subscribe(priceTick -> System.out.println(priceTick.getSequence() + 44 | ": " + priceTick.getInstrument())); 45 | hot.connect(); 46 | Thread.sleep(5000); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/io/projectreactor/examples/MyReactiveLibraryTest.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples; 2 | 3 | import java.time.Duration; 4 | 5 | import org.junit.Test; 6 | import reactor.test.StepVerifier; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | /** 11 | * @author Simon Baslé 12 | */ 13 | public class MyReactiveLibraryTest { 14 | 15 | @Test 16 | public void testAlphabet5LimitsToZ() { 17 | MyReactiveLibrary library = new MyReactiveLibrary(); 18 | StepVerifier.create(library.alphabet5fixed('x')) 19 | .expectNext("x", "y", "z") 20 | .expectComplete() 21 | .verify(); 22 | } 23 | 24 | @Test 25 | public void testAlphabet5LastItemIsAlphabeticalChar() { 26 | MyReactiveLibrary library = new MyReactiveLibrary(); 27 | StepVerifier.create(library.alphabet5('x')) 28 | .consumeNextWith(c -> assertThat(c) 29 | .as("first is alphabetic").matches("[a-z]")) 30 | .consumeNextWith(c -> assertThat(c) 31 | .as("second is alphabetic").matches("[a-z]")) 32 | .consumeNextWith(c -> assertThat(c) 33 | .as("third is alphabetic").matches("[a-z]")) 34 | .consumeNextWith(c -> assertThat(c) 35 | .as("fourth is alphabetic").matches("[a-z]")) 36 | .expectComplete() 37 | .verify(); 38 | } 39 | 40 | @Test 41 | public void testWithDelay() { 42 | MyReactiveLibrary library = new MyReactiveLibrary(); 43 | Duration testDuration = 44 | StepVerifier.withVirtualTime(() -> library.withDelay("foo", 30)) 45 | .expectSubscription() 46 | .thenAwait(Duration.ofSeconds(10)) 47 | .expectNoEvent(Duration.ofSeconds(10)) 48 | .thenAwait(Duration.ofSeconds(10)) 49 | .expectNext("foo") 50 | .expectComplete() 51 | .verify(); 52 | System.out.println(testDuration.toMillis() + "ms"); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/test/java/io/projectreactor/examples/ReactorSnippets.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import com.vgrazi.play.PriceTick; 7 | import com.vgrazi.play.SomeFeed; 8 | import com.vgrazi.play.SomeListener; 9 | import org.junit.Test; 10 | import reactor.core.publisher.ConnectableFlux; 11 | import reactor.core.publisher.Flux; 12 | import reactor.core.publisher.FluxSink; 13 | import reactor.core.publisher.Mono; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | public class ReactorSnippets { 18 | 19 | private static List words = Arrays.asList("the", 20 | "quick", 21 | "brown", 22 | "fox", 23 | "jumped", 24 | "over", 25 | "the", 26 | "lazy", 27 | "dog"); 28 | 29 | @Test 30 | public void simpleCreation() { 31 | Flux fewWords = Flux.just("Hello", "World"); 32 | Flux manyWords = Flux.fromIterable(words); 33 | 34 | fewWords.subscribe(System.out::println); 35 | System.out.println(); 36 | manyWords.subscribe(System.out::println); 37 | } 38 | 39 | @Test 40 | public void countingLetters() { 41 | Flux manyLetters = Flux 42 | .fromIterable(words) 43 | .flatMap(word -> Flux.fromArray(word.split(""))) 44 | .zipWith(Flux.range(1, Integer.MAX_VALUE), 45 | (string, count) -> String.format("%2d. %s", count, string)); 46 | 47 | manyLetters.subscribe(System.out::println); 48 | } 49 | 50 | @Test 51 | public void findingMissingLetter() { 52 | Flux manyLetters = Flux 53 | .fromIterable(words) 54 | .flatMap(word -> Flux.fromArray(word.split(""))) 55 | .distinct() 56 | .sort() 57 | .zipWith(Flux.range(1, Integer.MAX_VALUE), 58 | (string, count) -> String.format("%2d. %s", count, string)); 59 | 60 | manyLetters.subscribe(System.out::println); 61 | } 62 | 63 | @Test 64 | public void restoreMissingLetter() { 65 | Mono missing = Mono.just("s"); 66 | Flux allLetters = Flux 67 | .fromIterable(words) 68 | .flatMap(word -> Flux.fromArray(word.split(""))) 69 | .concatWith(missing) 70 | .distinct() 71 | .sort() 72 | .zipWith(Flux.range(1, Integer.MAX_VALUE), 73 | (string, count) -> String.format("%2d. %s", count, string)); 74 | 75 | allLetters.subscribe(System.out::println); 76 | } 77 | 78 | @Test 79 | public void shortCircuit() { 80 | Flux helloPauseWorld = Mono.just("Hello") 81 | .concatWith(Mono.just("world").delaySubscriptionMillis(500)); 82 | helloPauseWorld.subscribe(System.out::println); 83 | } 84 | 85 | @Test 86 | public void blocks() { 87 | Flux helloPauseWorld = Mono.just("Hello") 88 | .concatWith(Mono.just("world").delaySubscriptionMillis(5000)); 89 | helloPauseWorld.toStream() 90 | .forEach(System.out::println); 91 | } 92 | 93 | @Test 94 | public void firstEmitting() { 95 | Mono a = Mono.just("oops I'm late") 96 | .delaySubscriptionMillis(450); 97 | Flux b = Flux.just("let's get", "the party", "started") 98 | .delayMillis(400); 99 | 100 | Flux.firstEmitting(a, b) 101 | .toIterable() 102 | .forEach(System.out::println); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/test/java/io/projectreactor/examples/spring/SpringTests.java: -------------------------------------------------------------------------------- 1 | package io.projectreactor.examples.spring; 2 | 3 | import io.projectreactor.examples.Sir; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.web.client.TestRestTemplate; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.test.context.junit4.SpringRunner; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT) 17 | public class SpringTests { 18 | 19 | @Autowired 20 | private TestRestTemplate restTemplate; 21 | 22 | @Test 23 | public void testHello() { 24 | ResponseEntity entity = 25 | this.restTemplate.getForEntity("/hello/{who}", String.class, "Walter"); 26 | 27 | assertThat(entity.getBody()).isEqualToIgnoringCase("Hello Walter!"); 28 | } 29 | 30 | @Test 31 | public void testHelloDelay() { 32 | ResponseEntity entity = 33 | this.restTemplate.getForEntity("/helloDelay/{who}", String.class, "Walter"); 34 | 35 | assertThat(entity.getBody()).isEqualToIgnoringCase("Hello Walter!!"); 36 | } 37 | 38 | @Test 39 | public void testHeyMister() { 40 | ResponseEntity responseEntity = this.restTemplate.postForEntity( 41 | "/heyMister", 42 | new Sir("Walter", "tEST"), 43 | String.class); 44 | 45 | assertThat(responseEntity.getBody()).isEqualTo("Hey mister T. how are you?"); 46 | } 47 | 48 | @Test 49 | public void testDataFound() { 50 | ResponseEntity responseEntity = 51 | this.restTemplate.getForEntity("/data/{who}", Sir.class, "buddy"); 52 | 53 | assertThat(responseEntity.getStatusCodeValue()).isEqualTo(200); 54 | assertThat(responseEntity.getBody()).isEqualTo(new Sir("buddy", "GUY")); 55 | } 56 | 57 | @Test 58 | public void testDataNotFound() { 59 | ResponseEntity responseEntity = this.restTemplate.getForEntity( 60 | "/data/{who}", 61 | String.class, 62 | "notFound"); 63 | 64 | assertThat(responseEntity.getStatusCodeValue()).isEqualTo(404); 65 | assertThat(responseEntity.getBody()).isNull(); 66 | } 67 | 68 | } 69 | --------------------------------------------------------------------------------