├── .gitignore ├── README.md ├── docker-compose.yaml ├── justfile ├── pom.xml ├── reactive-landscape.png ├── reactive.http └── src ├── main ├── java │ └── org │ │ └── mvnsearch │ │ ├── reactor │ │ ├── HttpBinAPI.java │ │ ├── HttpBinResponse.java │ │ └── MutableContext.java │ │ └── spring │ │ ├── KotlinCoroutineMethod.java │ │ ├── ReactiveDemoApp.kt │ │ └── UserService.kt └── uml │ └── reactive-classes.puml └── test ├── java └── org │ └── mvnsearch │ ├── adapter │ ├── Account.java │ ├── AccountService.java │ ├── Cache.java │ ├── KafkaReactiveApi.java │ ├── Transaction.java │ ├── TransactionService.java │ └── TransactionTest.java │ ├── akka │ ├── AkkaStreamTest.java │ └── HelloActorContext.kt │ ├── awaitility │ └── AwaitilityTest.java │ ├── block │ ├── ReactiveServiceRpcProxy.java │ ├── ReactiveServiceRpcProxyInvocationHandler.java │ └── UserInterface.java │ ├── cart │ ├── Account.java │ ├── AccountReactiveService.java │ ├── AccountService.java │ ├── EmailReactiveService.java │ ├── EmailService.java │ ├── NotificationManager.java │ └── impl │ │ ├── NotificationManagerImpl.java │ │ ├── NotificationManagerReactiveImpl.java │ │ └── PortalController.java │ ├── coroutines │ ├── CoroutineInvocationHandler.kt │ ├── CoroutinesFlowTest.kt │ ├── DelegateTest.kt │ └── UserServiceKtTest.kt │ ├── eventloop │ ├── EventLoopTest.java │ └── ExecutorServiceTest.java │ ├── flow │ └── FlowTest.kt │ ├── future │ └── CompletableFutureTest.java │ ├── kafka_streams │ └── KafkaStreamsDemo.java │ ├── mutiny │ └── SmallRyeMutinyTest.java │ ├── reactor │ ├── EncodingException.java │ ├── EventListener.java │ ├── ExceptionTest.java │ ├── MonoCacheTest.java │ ├── ProcessorTest.kt │ ├── R2dbcConnection.java │ ├── ReactorContextTest.java │ ├── ReactorExceptionTest.java │ ├── ReactorFluxTest.java │ ├── ReactorKotlinTest.kt │ ├── ReactorMonoTest.java │ ├── ReactorPoolTest.java │ ├── ReactorProcessorTest.kt │ ├── ReactorToolsTest.java │ ├── Resilience4jTest.java │ ├── RetrofitTest.java │ ├── RetryTest.java │ ├── SchedulerTest.java │ ├── SvelteTest.java │ ├── TestPublisherTest.java │ ├── UpstreamEvent.java │ ├── UserServiceImplTest.java │ ├── WebClientTest.java │ ├── blockhound │ │ └── BlockHoundTest.java │ ├── filterchain │ │ ├── FilterChain.java │ │ ├── FilterChainTest.java │ │ └── ItemFilter.java │ ├── kafka │ │ ├── KafkaReactorReceiverTest.java │ │ └── KafkaReactorSenderTest.java │ └── netty │ │ ├── NettyClientTest.java │ │ ├── TcpServerApp.java │ │ └── User.java │ ├── reaktive │ └── ReaktiveTest.kt │ ├── rsocket │ ├── requester │ │ ├── RSocketAppRunner1.java │ │ ├── RSocketLocalTest.java │ │ └── RSocketRequesterApp.java │ └── responder │ │ ├── RSocketResponderHandler.java │ │ └── RsocketAppServer.java │ ├── rxjava │ ├── ObservableTest.java │ ├── RxJavaTest.java │ └── RxKotlinTest.kt │ ├── rxjava2 │ ├── FlowableTest.java │ └── SingleTest.java │ ├── rxjava3 │ └── RxJava3Test.java │ └── streamex │ └── StreamExTest.java └── resources ├── application.properties ├── junit-platform.properties └── logback-test.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/workspace.xml 8 | .idea/tasks.xml 9 | .idea/dictionaries 10 | .idea/vcs.xml 11 | .idea/jsLibraryMappings.xml 12 | 13 | # Sensitive or high-churn files: 14 | .idea/dataSources.ids 15 | .idea/dataSources.xml 16 | .idea/dataSources.local.xml 17 | .idea/sqlDataSources.xml 18 | .idea/dynamic.xml 19 | .idea/uiDesigner.xml 20 | 21 | # Gradle: 22 | .idea/gradle.xml 23 | .idea/libraries 24 | 25 | # Mongo Explorer plugin: 26 | .idea/mongoSettings.xml 27 | 28 | ## File-based project format: 29 | *.iws 30 | *.iml 31 | 32 | ## Plugin-specific files: 33 | 34 | # IntelliJ 35 | /out/ 36 | 37 | # mpeltonen/sbt-idea plugin 38 | .idea_modules/ 39 | 40 | # JIRA plugin 41 | atlassian-ide-plugin.xml 42 | 43 | # Crashlytics plugin (for Android Studio and IntelliJ) 44 | com_crashlytics_export_strings.xml 45 | crashlytics.properties 46 | crashlytics-build.properties 47 | fabric.properties 48 | ### Java template 49 | *.class 50 | 51 | # Mobile Tools for Java (J2ME) 52 | .mtj.tmp/ 53 | 54 | # Package Files # 55 | *.jar 56 | *.war 57 | *.ear 58 | 59 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 60 | hs_err_pid* 61 | ### Maven template 62 | target/ 63 | pom.xml.tag 64 | pom.xml.releaseBackup 65 | pom.xml.versionsBackup 66 | pom.xml.next 67 | release.properties 68 | dependency-reduced-pom.xml 69 | buildNumber.properties 70 | .mvn/timing.properties 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Reactive Programming 2 | ==================== 3 | 4 | ![Reactive Landscape](reactive-landscape.png) 5 | 6 | ### Demos 7 | Please use JDK 11 to run all demos. 8 | 9 | * Java 9 Flow 10 | * RxJava 1.x 11 | * RxJava 2.x 12 | * Reactor 3.2.x 13 | * RSocket 14 | * Reactive API for Cache, Messaging 15 | * Reactive with HTTP: webclient & retrofit 16 | * Kotlin 17 | * Webflux 18 | * Kotlin Coroutines & Flow 19 | * Reaktive: Kotlin multi-platform implementation of Reactive Extensions 20 | 21 | ### Glossary 22 | 23 | * reactive: 响应的,响应式的 24 | * streaming: 流式的 25 | * asynchronous: 异步的 26 | * non-blocking: 非阻塞的 27 | * Observable 可观测的 28 | * Single: 单个的 29 | * Flux: 流量 30 | * Mono: 单一的 31 | 32 | ### Reactive Operation 33 | 34 | * Creation 35 | * Combine 36 | * transform(map,flatMap) 37 | * Filter 38 | * Mathematical and Aggregate Operators 39 | * Utility Operators 40 | * Conditional and Boolean Operators 41 | * Error Handling 42 | 43 | ##### reactive stream 44 | 45 | * Publisher: A Publisher is a provider of a potentially unbounded number of sequenced elements, publishing them according to the demand received from its Subscriber(s). 46 | * Subscriber: Receive call to onSubscribe(Subscription) once after passing an instance of Subscriber to Publisher.subscribe(Subscriber). 47 | * Subscription: A Subscription represents a one-to-one lifecycle of a Subscriber subscribing to a Publisher, use subscription.request() method to request items. 48 | * Processor: Processor represents a processing stage—which is both a Subscriber and a Publisher and obeys the contracts of both 49 | 50 | ### ReactiveX 51 | 52 | * Observable 53 | * Operators 54 | * Single 55 | * Subject 56 | * Scheduler 57 | 58 | ### Java 9 59 | 60 | https://www.baeldung.com/java-9-reactive-streams 61 | 62 | * java.util.concurrent.Flow: Interrelated interfaces and static methods for establishing flow-controlled components 63 | * SubmissionPublisher: publisher 64 | 65 | ##### RxJava 66 | 67 | * Observable: This class provides methods for subscribing to the Observable as well as delegate methods to the various Observers. 68 | * Single: Reactive Pattern for a single value response 69 | * Observer: Provides a mechanism for receiving push-based notifications 70 | * Subscriber: Provides a mechanism for receiving push-based notifications from Observables, and permits manual unsubscribing from these Observables. 71 | 72 | #### RxJava 2 & 3 73 | 74 | RxJava 2 features several base classes you can discover operators on: 75 | 76 | * io.reactivex.Flowable: 0..N flows, supporting Reactive-Streams and backpressure 77 | * io.reactivex.Observable: 0..N flows, no backpressure, 78 | * io.reactivex.Single: a flow of exactly 1 item or an error, 79 | * io.reactivex.Completable: a flow without items but only a completion or error signal, 80 | * io.reactivex.Maybe: a flow with no items, exactly one item or an error. 81 | * Notification: Represents the reactive signal types: onNext, onError and onComplete and holds their parameter values (a value, a Throwable, nothing) 82 | * Subject: Represents an Observer and an Observable at the same time, allowing multicasting events from a single source to multiple child Observers 83 | 84 | ##### Reactor 85 | 86 | * Flux: A Reactive Streams {@link Publisher} with rx operators that emits 0 to N elements, and then completes (successfully or with an error). 87 | * Mono: A Reactive Streams {@link Publisher} with basic rx operators that completes successfully by emitting an element, or with an error. 88 | * FluxSink: next/error/complete sink to push data to flux 89 | * FluxProcessor: processor 90 | * Signal: A domain representation of a Reactive Stream signal. There are 4 distinct signals and their possible sequence is defined as such: onError | (onSubscribe onNext* (onError | onComplete)?) 91 | 92 | #### Akka Stream 93 | 94 | Stream process: Source -> Flow -> Sink 95 | 96 | * Source: A processing stage with exactly one output, emitting data elements whenever downstream processing stages are ready to receive them. 97 | * Sink: A processing stage with exactly one input, requesting and accepting data elements possibly slowing down the upstream producer of elements 98 | * Flow: A processing stage which has exactly one input and output, which connects its up- and downstreams by transforming the data elements flowing through it. 99 | * Source.single: Stream a single object 100 | 101 | Operators: https://doc.akka.io/docs/akka/2.5/stream/operators/index.html#source-operators 102 | 103 | ### Kafka Streams 104 | 105 | https://kafka.apache.org/documentation/streams/ 106 | 107 | * Topology: A topology is an acyclic graph of sources, processors, and sinks. 108 | * KafkaStreams: A Kafka client that allows for performing continuous computation on input coming from one or more input topics and sends output to zero, one, or more output topics. 109 | * KStream: an abstraction of a record stream of eyValue pairs 110 | 111 | ### Kafka Reactor 112 | 113 | https://projectreactor.io/docs/kafka/release/reference/ 114 | 115 | * KafkaSender: publishing messages to Kafka 116 | * KafkaReceiver: consuming messages from Kafka 117 | 118 | ### RxJava VS Reactor 119 | 120 | * RxJava诞生早,使用广泛,尤其在Netflix产品中 121 | * Reactor和Spring整合密切 122 | * 在Java 8支持方面,Reactor基于Java 8,而RxJava是自己的API 123 | * RxJava的模型在其他语言都有实现,如果你用多语言的场景化,RxJava的模型更好一些,如Angular等 124 | 125 | https://www.nurkiewicz.com/2019/02/rxjava-vs-reactor.html 126 | 127 | ### Kotlin Extension 128 | 129 | * https://github.com/ReactiveX/RxKotlin 130 | * https://github.com/reactor/reactor-kotlin-extensions 131 | 132 | ### BlockHound 133 | BlockHound(block代码猎犬)是一个Java agent,主要检测非阻塞线程中的同步调用。 我们常见的同步方法如下: 134 | 135 | * java.lang.Thread: sleep, yield, onSpinWait 136 | * java.lang.Object: wait 137 | * java.io.RandomAccessFile: read0, readBytes, write0, writeBytes 138 | * java.io.FileInputStream: read0, readBytes 139 | * java.io.FileOutputStream: write, writeBytes 140 | * java.net.Socket: connect 141 | * java.net.DatagramSocket: connect 142 | * java.net.PlainDatagramSocketImpl: connect0, peekData, send 143 | * java.net.PlainSocketImpl: socketAccept 144 | * java.net.SocketInputStream: socketRead0 145 | * java.net.SocketOutputStream: socketWrite0 146 | * sun.misc.Unsafe: park 147 | * jdk.internal.misc.Unsafe: park 148 | * java.lang.ProcessImpl: forkAndExec 149 | * java.lang.UNIXProcess: forkAndExec 150 | 151 | 152 | 凡是涉及到以上代码,都是同步调用,需要注意,尤其是使用wait和notifyAll来设计线程协调。 153 | 154 | * java.util.concurrent.CountDownLatch: await 155 | 156 | 157 | ### Exception handling 158 | 159 | Reactor for handling error: doOnError, onErrorMap, onErrorReturn, and onErrorResume 160 | 161 | * doOnError: executed when an error is thrown and hasn't been caught 162 | * onErrorMap: used to map an error into another error. As it's only being mapped, the error is still thrown 163 | * onErrorReturn: set a fallback value that will be returned if error is thrown. The next operator in the chain will get the fallback value instead of error. 164 | * onErrorResume: set a fallback method that will be executed if error is thrown. The next operator in the chain will get the result of the fallback method instead of error. 165 | 166 | ### References 167 | 168 | * http://www.reactive-streams.org: Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure 169 | * http://reactivex.io: An API for asynchronous programming with observable streams 170 | * http://projectreactor.io: Reactor is a second-generation Reactive library for building non-blocking applications on the JVM based on the Reactive Streams Specification 171 | * https://tech.io/playgrounds/929/reactive-programming-with-reactor-3/content/Intro 172 | * http://reactivex.io/documentation/operators.html: reactive operations 173 | * https://dzone.com/articles/functional-amp-reactive-spring-along-with-netflix: Functional and Reactive Spring with Reactor and Netflix OSS 174 | * RxJava2 响应式编程介绍: https://zouzhberk.github.io/rxjava-study/ 175 | * Reactive Streams: https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.2/README.md#specification 176 | * RSocket: http://rsocket.io/ 177 | * Reactor Netty: http://projectreactor.io/docs/netty/release/reference/index.html 178 | * Reactor Testing: http://projectreactor.io/docs/core/release/reference/index.html 179 | * RxJava Extensions: https://github.com/akarnokd 180 | * Reaktive — a multiplatform library for reactive Kotlin: https://github.com/badoo/Reaktive https://badootech.badoo.com/reaktive-a-multiplatform-library-for-reactive-kotlin-android-ios-77b6ff25adb1 181 | * Reactive Spring Boot: https://learning.oreilly.com/live-training/courses/reactive-spring-boot/0636920371410/ 182 | * Reactive marble diagram generator: https://bitbucket.org/achary/rx-marbles/ https://medium.com/@jshvarts/read-marble-diagrams-like-a-pro-3d72934d3ef5 183 | * Animated playground for Rx Observables: https://rxviz.com/ 184 | * SmallRye Mutiny: a reactive programming library https://github.com/smallrye/smallrye-mutiny 185 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | zookeeper: 4 | image: zookeeper:3.4.13 5 | ports: 6 | - "2181:2181" 7 | - "2888:2888" 8 | - "3888:3888" 9 | kafka: 10 | image: wurstmeister/kafka:2.12-2.1.0 11 | ports: 12 | - "9092:9092" 13 | environment: 14 | KAFKA_ADVERTISED_HOST_NAME: 127.0.01 15 | KAFKA_CREATE_TOPICS: "Topic1:1:1:delete,testTopic:1:1:delete" 16 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 17 | kafka-manager: 18 | image: sheepkiller/kafka-manager 19 | ports: 20 | - "9000:9000" 21 | links: 22 | - kafka 23 | - zookeeper 24 | environment: 25 | APPLICATION_SECRET: 123456 26 | ZK_HOSTS: zookeeper:2181 27 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # rsocket request/response testing 2 | rsocket_request_response: 3 | rsocket-cli --request -i "I am a Client" tcp://localhost:7000 4 | 5 | # send messages to kafka from console input 6 | send_kafka_messages: 7 | docker-compose exec kafka /opt/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic testTopic 8 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.mvnsearch.spring.reactor 7 | reactive-demo 8 | 1.0.0-SNAPSHOT 9 | jar 10 | 11 | Reactive Demo 12 | 13 | 14 | UTF-8 15 | 21 16 | 2023.0.0 17 | 1.3.8 18 | 2.2.21 19 | 3.1.8 20 | 2.0.1 21 | 2.4.0 22 | 1.9.21 23 | 1.8.0-RC 24 | 5.0.0-alpha.11 25 | 2.9.0 26 | 1.1.4 27 | 2.8.1 28 | 3.6.0 29 | 3.2.0 30 | 2.1.0 31 | 0.8.2 32 | 5.10.1 33 | 34 | 35 | 36 | 37 | org.jetbrains 38 | annotations 39 | 24.1.0 40 | 41 | 42 | org.projectlombok 43 | lombok 44 | 45 | 46 | org.reactivestreams 47 | reactive-streams 48 | 1.0.4 49 | 50 | 51 | io.projectreactor 52 | reactor-core 53 | 54 | 55 | io.projectreactor.addons 56 | reactor-extra 57 | 58 | 59 | io.projectreactor.addons 60 | reactor-adapter 61 | 62 | 74 | 75 | io.projectreactor 76 | reactor-tools 77 | 78 | 79 | io.projectreactor.addons 80 | reactor-pool 81 | 82 | 83 | io.projectreactor.kotlin 84 | reactor-kotlin-extensions 85 | 1.2.2 86 | 87 | 88 | io.reactivex 89 | rxjava 90 | ${rxjava.version} 91 | 92 | 93 | io.reactivex.rxjava2 94 | rxjava 95 | ${rxjava2.version} 96 | 97 | 98 | io.reactivex.rxjava3 99 | rxjava 100 | ${rxjava3.version} 101 | 102 | 103 | io.reactivex.rxjava2 104 | rxkotlin 105 | 2.4.0 106 | 107 | 108 | io.reactivex.rxjava3 109 | rxkotlin 110 | 3.0.1 111 | 112 | 113 | com.github.akarnokd 114 | rxjava2-extensions 115 | 0.20.10 116 | 117 | 118 | com.github.akarnokd 119 | rxjava3-extensions 120 | 3.1.1 121 | 122 | 123 | 124 | com.typesafe.akka 125 | akka-stream_2.13 126 | 127 | 128 | 129 | org.apache.kafka 130 | kafka-streams 131 | ${kafka-streams.version} 132 | 133 | 134 | io.projectreactor.kafka 135 | reactor-kafka 136 | 137 | 138 | 139 | com.jakewharton.retrofit 140 | retrofit2-reactor-adapter 141 | 2.1.0 142 | 143 | 144 | com.squareup.retrofit2 145 | retrofit 146 | ${retrofit.version} 147 | 148 | 149 | com.squareup.retrofit2 150 | converter-jackson 151 | ${retrofit.version} 152 | 153 | 154 | com.squareup.okhttp3 155 | okhttp 156 | ${okhttp3.version} 157 | 158 | 159 | io.github.resilience4j 160 | resilience4j-reactor 161 | ${resilience4j.version} 162 | 163 | 164 | io.github.resilience4j 165 | resilience4j-ratelimiter 166 | ${resilience4j.version} 167 | 168 | 169 | one.util 170 | streamex 171 | ${streamex.version} 172 | 173 | 174 | 175 | org.jetbrains.kotlin 176 | kotlin-stdlib 177 | 178 | 179 | org.jetbrains.kotlinx 180 | kotlinx-coroutines-core 181 | 182 | 183 | org.jetbrains.kotlinx 184 | kotlinx-coroutines-reactor 185 | 186 | 187 | 188 | com.fasterxml.jackson.core 189 | jackson-databind 190 | 191 | 192 | 193 | io.rsocket 194 | rsocket-core 195 | 196 | 197 | io.rsocket 198 | rsocket-transport-netty 199 | 200 | 201 | io.rsocket 202 | rsocket-transport-local 203 | 204 | 205 | org.springframework.boot 206 | spring-boot-starter-webflux 207 | 208 | 209 | com.google.guava 210 | guava 211 | 32.1.3-jre 212 | 213 | 214 | io.projectreactor.netty 215 | reactor-netty 216 | 217 | 218 | com.badoo.reaktive 219 | reaktive-jvm 220 | ${reaktive.version} 221 | 222 | 223 | com.badoo.reaktive 224 | rxjava2-interop 225 | ${reaktive.version} 226 | 227 | 228 | com.badoo.reaktive 229 | coroutines-interop-jvm 230 | ${reaktive.version} 231 | 232 | 233 | io.smallrye.reactive 234 | mutiny 235 | 236 | 237 | io.smallrye.reactive 238 | mutiny-reactor 239 | 240 | 241 | io.projectreactor 242 | reactor-test 243 | test 244 | 245 | 246 | org.awaitility 247 | awaitility 248 | 4.2.0 249 | test 250 | 251 | 252 | org.awaitility 253 | awaitility-kotlin 254 | 4.2.0 255 | test 256 | 257 | 258 | org.junit.jupiter 259 | junit-jupiter 260 | test 261 | 262 | 263 | org.assertj 264 | assertj-core 265 | 3.24.2 266 | test 267 | 268 | 269 | 270 | 271 | 272 | 273 | org.junit 274 | junit-bom 275 | ${junit5.version} 276 | import 277 | pom 278 | 279 | 280 | org.jetbrains.kotlin 281 | kotlin-bom 282 | ${kotlin.version} 283 | import 284 | pom 285 | 286 | 287 | org.jetbrains.kotlinx 288 | kotlinx-coroutines-bom 289 | ${kotlinx-coroutines.version} 290 | import 291 | pom 292 | 293 | 294 | io.projectreactor 295 | reactor-bom 296 | ${reactor-bom.version} 297 | pom 298 | import 299 | 300 | 301 | io.smallrye.reactive 302 | mutiny-bom 303 | ${mutiny.version} 304 | pom 305 | import 306 | 307 | 308 | com.typesafe.akka 309 | akka-bom_2.13 310 | ${akka.version} 311 | pom 312 | import 313 | 314 | 315 | io.rsocket 316 | rsocket-bom 317 | ${rsocket.version} 318 | pom 319 | import 320 | 321 | 322 | org.springframework.boot 323 | spring-boot-dependencies 324 | ${spring-boot.version} 325 | import 326 | pom 327 | 328 | 329 | 330 | 331 | 332 | 333 | kotlin-maven-plugin 334 | org.jetbrains.kotlin 335 | ${kotlin.version} 336 | 337 | 338 | kapt 339 | 340 | kapt 341 | 342 | 343 | 344 | ${project.basedir}/src/main/kotlin 345 | 346 | 347 | 348 | 349 | compile 350 | process-sources 351 | 352 | compile 353 | 354 | 355 | 356 | ${project.basedir}/src/main/kotlin 357 | ${project.basedir}/src/main/java 358 | 359 | 360 | 361 | 362 | test-compile 363 | 364 | test-compile 365 | 366 | 367 | 368 | ${project.basedir}/src/test/kotlin 369 | ${project.basedir}/src/test/java 370 | 371 | 372 | 373 | 374 | 375 | ${java.version} 376 | true 377 | 378 | -Xjsr305=strict 379 | 380 | 381 | spring 382 | 383 | 384 | 385 | 386 | org.jetbrains.kotlin 387 | kotlin-maven-allopen 388 | ${kotlin.version} 389 | 390 | 391 | 392 | 393 | org.apache.maven.plugins 394 | maven-compiler-plugin 395 | 3.11.0 396 | 397 | ${java.version} 398 | ${java.version} 399 | true 400 | 401 | 402 | 403 | 404 | default-compile 405 | none 406 | 407 | 408 | 409 | default-testCompile 410 | none 411 | 412 | 413 | java-compile 414 | compile 415 | 416 | compile 417 | 418 | 419 | 420 | java-test-compile 421 | test-compile 422 | 423 | testCompile 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | -------------------------------------------------------------------------------- /reactive-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linux-china/reactive-demo/7fe3a20bfb214f7303df10e88574972f401a68b6/reactive-landscape.png -------------------------------------------------------------------------------- /reactive.http: -------------------------------------------------------------------------------- 1 | ### http request for mono 2 | GET http://localhost:8080/ 3 | 4 | ### http request for single 5 | GET http://localhost:8080/rx 6 | 7 | ### 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/org/mvnsearch/reactor/HttpBinAPI.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import reactor.core.publisher.Mono; 4 | import retrofit2.http.GET; 5 | 6 | /** 7 | * httpbin api 8 | * 9 | * @author linux_china 10 | */ 11 | public interface HttpBinAPI { 12 | @GET("/ip") 13 | Mono ip(); 14 | 15 | @GET("/uuid") 16 | Mono uuid(); 17 | 18 | @GET("/headers") 19 | Mono headers(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/mvnsearch/reactor/HttpBinResponse.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * httpbin response 10 | * 11 | * @author linux_china 12 | */ 13 | @Data 14 | public class HttpBinResponse { 15 | private String uuid; 16 | @JsonProperty(value = "origin") 17 | private String ip; 18 | private Map headers; 19 | } -------------------------------------------------------------------------------- /src/main/java/org/mvnsearch/reactor/MutableContext.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import reactor.util.context.Context; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.stream.Stream; 9 | 10 | /** 11 | * Mutable Reactor context 12 | * 13 | * @author linux_china 14 | */ 15 | public class MutableContext implements Context { 16 | HashMap holder = new HashMap<>(); 17 | 18 | @SuppressWarnings("unchecked") 19 | @NotNull 20 | @Override 21 | public T get(@NotNull Object key) { 22 | return (T) holder.get(key); 23 | } 24 | 25 | @Override 26 | public boolean hasKey(@NotNull Object key) { 27 | return holder.containsKey(key); 28 | } 29 | 30 | @NotNull 31 | @Override 32 | public Context put(@NotNull Object key, @NotNull Object value) { 33 | holder.put(key, value); 34 | return this; 35 | } 36 | 37 | @NotNull 38 | @Override 39 | public Context delete(@NotNull Object key) { 40 | holder.remove(key); 41 | return this; 42 | } 43 | 44 | @Override 45 | public int size() { 46 | return holder.size(); 47 | } 48 | 49 | @NotNull 50 | @Override 51 | public Stream> stream() { 52 | return holder.entrySet().stream(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/mvnsearch/spring/KotlinCoroutineMethod.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.spring; 2 | 3 | import kotlinx.coroutines.flow.Flow; 4 | 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.ParameterizedType; 7 | import java.lang.reflect.Type; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * 12 | * @author linux_china 13 | */ 14 | public class KotlinCoroutineMethod { 15 | private String name; 16 | private int paramCount = 0; 17 | private Class returnType; 18 | private Class inferredReturnType; 19 | private boolean flow; 20 | private boolean suspend; 21 | 22 | public KotlinCoroutineMethod(Method method) { 23 | this.name = method.getName(); 24 | this.returnType = method.getReturnType(); 25 | if (this.returnType.equals(Flow.class)) { 26 | this.flow = true; 27 | } 28 | Type[] parameterTypes = method.getGenericParameterTypes(); 29 | this.paramCount = parameterTypes.length; 30 | if (paramCount > 0) { 31 | Type lastParamType = parameterTypes[paramCount - 1]; 32 | if (lastParamType.getTypeName().startsWith("kotlin.coroutines.Continuation<")) { 33 | this.suspend = true; 34 | if (lastParamType.getTypeName().contains("kotlin.Unit>")) { 35 | 36 | } else { 37 | this.inferredReturnType = parseInferredClass(lastParamType); 38 | } 39 | //kotlin.coroutines.Continuation 40 | //"(Lkotlin/coroutines/Continuation<-Lkotlin/Unit;>;)Ljava/lang/Object;" 41 | } 42 | } 43 | } 44 | 45 | public static Class parseInferredClass(Type genericType) { 46 | Class inferredClass = null; 47 | if (genericType instanceof ParameterizedType) { 48 | ParameterizedType type = (ParameterizedType) genericType; 49 | Type[] typeArguments = type.getActualTypeArguments(); 50 | if (typeArguments.length > 0) { 51 | final Type typeArgument = typeArguments[0]; 52 | if (typeArgument instanceof ParameterizedType) { 53 | inferredClass = (Class) ((ParameterizedType) typeArgument).getActualTypeArguments()[0]; 54 | } else if (typeArgument instanceof Class) { 55 | inferredClass = (Class) typeArgument; 56 | } else { 57 | String typeName = typeArgument.getTypeName(); 58 | if (typeName.contains(" ")) { 59 | typeName = typeName.substring(typeName.lastIndexOf(" ") + 1); 60 | } 61 | if (typeName.contains("<")) { 62 | typeName = typeName.substring(0, typeName.indexOf("<")); 63 | } 64 | try { 65 | inferredClass = Class.forName(typeName); 66 | } catch (Exception e) { 67 | 68 | } 69 | } 70 | } 71 | } 72 | if (inferredClass == null && genericType instanceof Class) { 73 | inferredClass = (Class) genericType; 74 | } 75 | return inferredClass; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/mvnsearch/spring/ReactiveDemoApp.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.spring 2 | 3 | import io.reactivex.Single 4 | import org.springframework.boot.autoconfigure.SpringBootApplication 5 | import org.springframework.boot.runApplication 6 | import org.springframework.web.bind.annotation.GetMapping 7 | import org.springframework.web.bind.annotation.RestController 8 | import reactor.core.publisher.Mono 9 | import reactor.kotlin.core.publisher.toMono 10 | 11 | /** 12 | * reactive demo app 13 | * 14 | * @author linux_china 15 | */ 16 | @SpringBootApplication 17 | class ReactiveDemoApp 18 | 19 | @RestController 20 | class PortalController { 21 | 22 | @GetMapping("/") 23 | fun index(): Mono { 24 | return "Hello Mono!".toMono() 25 | } 26 | 27 | @GetMapping("/rx") 28 | fun rxIndex(): Single { 29 | return Single.just("Hello Single!") 30 | } 31 | } 32 | 33 | fun main(args: Array) { 34 | runApplication(*args) 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/mvnsearch/spring/UserService.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.spring 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import kotlinx.coroutines.flow.asFlow 5 | import kotlinx.coroutines.reactor.asFlux 6 | import reactor.core.publisher.Flux 7 | 8 | /** 9 | * User service 10 | * 11 | * @author linux_china 12 | */ 13 | interface UserService { 14 | 15 | suspend fun job1() 16 | 17 | suspend fun getNickById(id: Int): String 18 | suspend fun getNicks(id: Int): List? 19 | 20 | fun getAllNames(): Flow 21 | 22 | fun findNamesByType(type: Int): Flow 23 | } 24 | 25 | 26 | class UserServiceImpl : UserService { 27 | 28 | override suspend fun job1() { 29 | println("job1") 30 | } 31 | 32 | override suspend fun getNickById(id: Int): String { 33 | return "nick $id" 34 | } 35 | 36 | override suspend fun getNicks(id: Int): List? { 37 | return null 38 | } 39 | 40 | override fun getAllNames(): Flow { 41 | return arrayOf("first", "second").asFlow(); 42 | } 43 | 44 | override fun findNamesByType(type: Int): Flow { 45 | return arrayOf("first", "second").asFlow(); 46 | } 47 | 48 | fun getAllNamesFlux(): Flux { 49 | return getAllNames().asFlux() 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/uml/reactive-classes.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | package "org.reactivestreams" { 4 | interface Publisher { 5 | void subscribe(Subscriber s) 6 | } 7 | 8 | interface Subscriber { 9 | void onSubscribe(Subscription s) 10 | void onNext(T t) 11 | void onError(Throwable t) 12 | void onComplete() 13 | } 14 | 15 | interface Subscription { 16 | void request(long n) 17 | void cancel() 18 | } 19 | Publisher -> Subscriber: Use 20 | Subscriber -down-> Subscription: Use 21 | } 22 | 23 | package "rx" { 24 | interface Observer { 25 | void onCompleted() 26 | void onError(Throwable e) 27 | void onNext(T t) 28 | } 29 | 30 | interface RxSubscription { 31 | void unsubscribe() 32 | boolean isUnsubscribed() 33 | } 34 | 35 | class RxSubscriber implements Observer,RxSubscription { 36 | 37 | } 38 | 39 | class Observable { 40 | Observable create(OnSubscribe f) 41 | Subscription subscribe(Observer observer) 42 | Observable just(T... data) 43 | } 44 | 45 | Observable --> Observer: Use 46 | } 47 | 48 | package "reactor" { 49 | 50 | class Flux implements Publisher { 51 | Flux just(T... data) 52 | } 53 | 54 | class Mono implements Publisher { 55 | Mono just(T data) 56 | Mono justOrEmpty(T data) 57 | } 58 | 59 | } 60 | 61 | @enduml -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/adapter/Account.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.adapter; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * 6 | * @author linux_china 7 | */ 8 | public class Account { 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/adapter/AccountService.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.adapter; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * 8 | * @author linux_china 9 | */ 10 | public interface AccountService { 11 | 12 | Mono findAccountById(long id); 13 | 14 | Mono findAccountByUUID(String uuid); 15 | 16 | Mono findAccountByEmail(String email); 17 | 18 | Mono findAccountByMobile(String mobile); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/adapter/Cache.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.adapter; 2 | 3 | import reactor.core.publisher.Flux; 4 | import reactor.core.publisher.Mono; 5 | import reactor.util.function.Tuple2; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * Reactive Cache API 11 | * 12 | * @author linux_china 13 | */ 14 | public interface Cache { 15 | 16 | Mono get(K key); 17 | 18 | Mono put(K key, V value); 19 | 20 | Mono remove(K key); 21 | 22 | Mono containsKey(K key); 23 | 24 | Flux> getAll(Set keys); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/adapter/KafkaReactiveApi.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.adapter; 2 | 3 | import reactor.core.publisher.Flux; 4 | import reactor.core.publisher.Mono; 5 | 6 | /** 7 | * Kafka reactive API 8 | * 9 | * @author linux_china 10 | */ 11 | public interface KafkaReactiveApi { 12 | 13 | Mono send(String topic, byte[] data); 14 | 15 | Flux subscribe(String topic); 16 | 17 | Flux subscribeWithGroup(String topic, String group); 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/adapter/Transaction.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.adapter; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * 6 | * @author linux_china 7 | */ 8 | public class Transaction { 9 | private Long id; 10 | 11 | public Transaction(Long id) { 12 | this.id = id; 13 | } 14 | 15 | public double total() { 16 | return 1.0; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/adapter/TransactionService.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.adapter; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | /** 6 | * transaction service 7 | * 8 | * @author linux_china 9 | */ 10 | public class TransactionService { 11 | 12 | public Mono findById(Long id) { 13 | return Mono.just(new Transaction(id)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/adapter/TransactionTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.adapter; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.math.MathFlux; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * 10 | * @author linux_china 11 | */ 12 | public class TransactionTest { 13 | 14 | @Test 15 | public void testSum() throws Exception { 16 | TransactionService transactionService = new TransactionService(); 17 | Long[] transactionIds = new Long[]{111L, 222L, 333L}; 18 | Flux.fromArray(transactionIds) 19 | .flatMap(transactionService::findById) 20 | .window(2) 21 | .flatMap(transactions -> MathFlux.sumDouble(transactions.map(Transaction::total))) 22 | .reduce(0.0, (x1, x2) -> x1 + x2) 23 | .subscribe(System.out::println); 24 | 25 | Thread.sleep(1000); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/akka/AkkaStreamTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.akka; 2 | 3 | import akka.Done; 4 | import akka.NotUsed; 5 | import akka.actor.ActorSystem; 6 | import akka.stream.ActorMaterializer; 7 | import akka.stream.Materializer; 8 | import akka.stream.javadsl.Flow; 9 | import akka.stream.javadsl.Sink; 10 | import akka.stream.javadsl.Source; 11 | import org.junit.jupiter.api.AfterAll; 12 | import org.junit.jupiter.api.BeforeAll; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import java.util.concurrent.CompletionStage; 16 | 17 | /** 18 | * Akka stream test 19 | * 20 | * @author linux_china 21 | */ 22 | public class AkkaStreamTest { 23 | private static ActorSystem system; 24 | private static Materializer materializer; 25 | 26 | @BeforeAll 27 | public static void setUp() { 28 | system = ActorSystem.create("QuickStart"); 29 | materializer = ActorMaterializer.create(system); 30 | } 31 | 32 | @AfterAll 33 | public static void tearDown() { 34 | system.terminate(); 35 | } 36 | 37 | @Test 38 | public void testDemo() throws Exception { 39 | final Source source = Source.range(1, 10); 40 | source.map(t -> { 41 | return t + 1; 42 | }).runForeach(i -> { 43 | System.out.println(i); 44 | }, materializer); 45 | 46 | Thread.sleep(1000); 47 | } 48 | 49 | @Test 50 | public void testSingle() throws Exception { 51 | Source single = Source.single("A"); 52 | single.runForeach(t -> { 53 | System.out.println(t); 54 | }, materializer); 55 | Thread.sleep(1000); 56 | } 57 | 58 | @Test 59 | public void testSink() throws Exception { 60 | Sink> print = Sink.foreach(t -> { 61 | System.out.println(t); 62 | }); 63 | Source single = Source.single("A"); 64 | single.runWith(print, materializer); 65 | Thread.sleep(1000); 66 | } 67 | 68 | @Test 69 | public void testFlow() throws Exception { 70 | Flow flowPrototype = Flow.create(); 71 | Flow flow = flowPrototype.map(t -> { 72 | return t + ":"; 73 | }); 74 | Source.single("A").via(flow).runForeach(t -> { 75 | System.out.println(t); 76 | }, materializer); 77 | Thread.sleep(1000); 78 | 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/akka/HelloActorContext.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("HasPlatformType") 2 | 3 | package org.mvnsearch.akka 4 | 5 | import akka.actor.AbstractLoggingActor 6 | import akka.actor.ActorRef 7 | import akka.actor.ActorSystem 8 | import akka.actor.Props 9 | import akka.japi.pf.ReceiveBuilder 10 | 11 | /** 12 | * hello actor 13 | * 14 | * @author linux_china 15 | */ 16 | class HelloKotlinActor : AbstractLoggingActor() { 17 | override fun createReceive() = ReceiveBuilder().match(String::class.java) { log().info("Hello $it") }.build() 18 | } 19 | 20 | 21 | fun main() { 22 | /* val actorSystem = ActorSystem.create("actorSystem1") 23 | val helloActor = actorSystem.actorOf(Props.create(HelloKotlinActor::class.java)) 24 | helloActor.tell("jackie", ActorRef.noSender()) 25 | Thread.sleep(1000) 26 | actorSystem.terminate()*/ 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/awaitility/AwaitilityTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.awaitility; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.Duration; 6 | 7 | import static org.awaitility.Awaitility.await; 8 | 9 | /** 10 | * Awaitility test 11 | * 12 | * @author linux_china 13 | */ 14 | public class AwaitilityTest { 15 | 16 | @Test 17 | public void testOperation() { 18 | await().timeout(Duration.ofSeconds(1)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/block/ReactiveServiceRpcProxy.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.block; 2 | 3 | /** 4 | * Reactive service proxy for RPC 5 | * 6 | * @author linux_china 7 | */ 8 | public interface ReactiveServiceRpcProxy { 9 | Object invoke(String serviceName, String method, Object params, String version); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/block/ReactiveServiceRpcProxyInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.block; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * 9 | * @author linux_china 10 | */ 11 | public class ReactiveServiceRpcProxyInvocationHandler implements InvocationHandler { 12 | @Override 13 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/block/UserInterface.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.block; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * 6 | * @author linux_china 7 | */ 8 | public interface UserInterface { 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/Account.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart; 2 | 3 | /** 4 | * account 5 | * 6 | * @author linux_china 7 | */ 8 | public class Account { 9 | private Long id; 10 | private String nick; 11 | private String mobile; 12 | private String email; 13 | 14 | public Long getId() { 15 | return id; 16 | } 17 | 18 | public void setId(Long id) { 19 | this.id = id; 20 | } 21 | 22 | public String getNick() { 23 | return nick; 24 | } 25 | 26 | public void setNick(String nick) { 27 | this.nick = nick; 28 | } 29 | 30 | public String getMobile() { 31 | return mobile; 32 | } 33 | 34 | public void setMobile(String mobile) { 35 | this.mobile = mobile; 36 | } 37 | 38 | public String getEmail() { 39 | return email; 40 | } 41 | 42 | public void setEmail(String email) { 43 | this.email = email; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/AccountReactiveService.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | /** 6 | * account service 7 | * 8 | * @author linux_china 9 | */ 10 | public interface AccountReactiveService { 11 | 12 | Mono findAccountById(Long id); 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/AccountService.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart; 2 | 3 | /** 4 | * account service 5 | * 6 | * @author linux_china 7 | */ 8 | public interface AccountService { 9 | 10 | Account findAccountById(Long id); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/EmailReactiveService.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.util.Map; 6 | 7 | /** 8 | * email service 9 | * 10 | * @author linux_china 11 | */ 12 | public interface EmailReactiveService { 13 | 14 | Mono sendEmail(Account account, Integer templateId, Map params); 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/EmailService.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * email service 7 | * 8 | * @author linux_china 9 | */ 10 | public interface EmailService { 11 | 12 | String sendEmail(Account account, Integer templateId, Map params); 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/NotificationManager.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart; 2 | 3 | /** 4 | * shopping cart notification manager 5 | * 6 | * @author leijuan 7 | */ 8 | public interface NotificationManager { 9 | 10 | void sendDiscountEmail(Long accountId); 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/impl/NotificationManagerImpl.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart.impl; 2 | 3 | import org.mvnsearch.cart.Account; 4 | import org.mvnsearch.cart.AccountService; 5 | import org.mvnsearch.cart.EmailService; 6 | import org.mvnsearch.cart.NotificationManager; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.concurrent.*; 14 | 15 | /** 16 | * notification manager 17 | * 18 | * @author linux_china 19 | */ 20 | @Component 21 | public class NotificationManagerImpl implements NotificationManager { 22 | private Logger log = LoggerFactory.getLogger(NotificationManagerImpl.class); 23 | private AccountService accountService; 24 | private EmailService emailService; 25 | private ExecutorService executor = new ThreadPoolExecutor(8, 8, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(200)); 26 | 27 | public NotificationManagerImpl(AccountService accountService, EmailService emailService) { 28 | this.accountService = accountService; 29 | this.emailService = emailService; 30 | } 31 | 32 | @Override 33 | public void sendDiscountEmail(Long accountId) { 34 | executor.execute(() -> { 35 | Account account = accountService.findAccountById(accountId); 36 | if (account != null) { 37 | Map params = new HashMap<>(); 38 | //todo 填充必要email模板需要的参数 39 | String messageId = emailService.sendEmail(account, 18, params); 40 | //todo 将message id保存一下,方便后续查找邮件是否发出 41 | log.info("Email sent with ID:" + messageId); 42 | } 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/impl/NotificationManagerReactiveImpl.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart.impl; 2 | 3 | import org.mvnsearch.cart.Account; 4 | import org.mvnsearch.cart.AccountReactiveService; 5 | import org.mvnsearch.cart.EmailReactiveService; 6 | import org.mvnsearch.cart.NotificationManager; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Component; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.concurrent.ArrayBlockingQueue; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.ThreadPoolExecutor; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * notification manager 21 | * 22 | * @author linux_china 23 | */ 24 | @Component 25 | public class NotificationManagerReactiveImpl implements NotificationManager { 26 | private Logger log = LoggerFactory.getLogger(NotificationManagerReactiveImpl.class); 27 | private AccountReactiveService accountService; 28 | private EmailReactiveService emailService; 29 | private ExecutorService executor = new ThreadPoolExecutor(8, 8, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(200)); 30 | 31 | public NotificationManagerReactiveImpl(AccountReactiveService accountService, EmailReactiveService emailService) { 32 | this.accountService = accountService; 33 | this.emailService = emailService; 34 | } 35 | 36 | @Override 37 | public void sendDiscountEmail(Long accountId) { 38 | executor.execute(() -> { 39 | Map params = new HashMap<>(); 40 | Mono account = accountService.findAccountById(accountId); 41 | // Mono result = emailService.sendEmail(account, 18, params); 42 | //result.subscribe(messageId -> System.out.println("Email sent with ID:" + messageId)); 43 | }); 44 | } 45 | 46 | public void demo(Long accountId) { 47 | Map params = new HashMap<>(); 48 | accountService.findAccountById(accountId) 49 | .map(account -> emailService.sendEmail(account, 18, params)) 50 | .subscribe(messageId -> System.out.println("Email sent with ID:" + messageId)); 51 | 52 | // Mono result = emailService.sendEmail(account, 18, params); 53 | // result.subscribe(messageId -> System.out.println("Email sent with ID:" + messageId)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/cart/impl/PortalController.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.cart.impl; 2 | 3 | import org.mvnsearch.cart.Account; 4 | import org.mvnsearch.cart.AccountReactiveService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import reactor.core.publisher.Mono; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * 12 | * @author linux_china 13 | */ 14 | public class PortalController { 15 | @Autowired 16 | private AccountReactiveService reactiveService; 17 | 18 | @RequestMapping("/we") 19 | public Mono findAccount(Long id) { 20 | return reactiveService.findAccountById(id); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/coroutines/CoroutineInvocationHandler.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.coroutines 2 | 3 | import org.mvnsearch.spring.UserService 4 | import java.lang.reflect.InvocationHandler 5 | import java.lang.reflect.Method 6 | import java.lang.reflect.Proxy 7 | import kotlin.coroutines.Continuation 8 | import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED 9 | import kotlin.coroutines.resume 10 | 11 | class CoroutineInvocationHandler : InvocationHandler { 12 | 13 | override fun invoke(proxy: Any?, method: Method?, args: Array?): Any { 14 | val lastArg = args?.lastOrNull() 15 | if (lastArg is Continuation<*>) { 16 | val cont = lastArg as Continuation 17 | val argsButLast = args.take(args.size - 1) 18 | doSomethingWith(method, argsButLast, onComplete = { result: Any -> 19 | cont.resume(result) 20 | }) 21 | return COROUTINE_SUSPENDED 22 | } else { 23 | return 0 24 | } 25 | } 26 | } 27 | 28 | fun userServiceStub(): UserService { 29 | return Proxy.newProxyInstance( 30 | UserService::class.java.classLoader, arrayOf>(UserService::class.java), 31 | CoroutineInvocationHandler() 32 | ) as UserService 33 | } 34 | 35 | fun doSomethingWith(method: Method?, argsButLast: List, onComplete: (Any) -> Unit) { 36 | onComplete.invoke("demo") 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/coroutines/CoroutinesFlowTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.coroutines 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.flow.* 5 | import org.junit.jupiter.api.Test 6 | 7 | /** 8 | * Coroutines Flow test 9 | * 10 | * @author linux_china 11 | */ 12 | class CoroutinesFlowTest { 13 | 14 | @Test 15 | fun testFlow() = runBlocking { 16 | val flowA = flowOf(1, 2, 3) 17 | .map { it + 1 } // Will be executed in ctxA 18 | .collect { println(it) } 19 | } 20 | 21 | @ExperimentalCoroutinesApi 22 | @Test 23 | fun testStateFlow() = runBlocking { 24 | val counterModel = CounterModel() 25 | launch { 26 | for (k in 1..3) { 27 | delay(100) 28 | counterModel.setName("name: $k") 29 | } 30 | } 31 | launch { 32 | counterModel.counter.collect { value -> println("New value: $value") } 33 | } 34 | launch { 35 | counterModel.counter.onEach { 36 | println("New2 value: $it") 37 | }.launchIn(GlobalScope) 38 | } 39 | delay(1000) 40 | } 41 | } 42 | 43 | @ExperimentalCoroutinesApi 44 | class CounterModel { 45 | private val _counter = MutableStateFlow("") // private mutable state flow 46 | val counter: StateFlow get() = _counter // publicly exposed as read-only state flow 47 | 48 | fun setName(name: String) { 49 | _counter.value = name 50 | } 51 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/coroutines/DelegateTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.coroutines 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.flow.MutableStateFlow 5 | import kotlinx.coroutines.flow.collect 6 | import kotlinx.coroutines.launch 7 | import kotlinx.coroutines.runBlocking 8 | import org.junit.jupiter.api.Test 9 | import kotlin.concurrent.thread 10 | import kotlin.reflect.KProperty 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * 15 | * @author linux_china 16 | */ 17 | class DelegateTest { 18 | 19 | @Test 20 | fun testDemo() { 21 | var p by delegate2 { 22 | "hi" 23 | } 24 | p = "hello" 25 | println(p) 26 | Thread.sleep(1000) 27 | } 28 | 29 | } 30 | 31 | fun delegate2(initializer: () -> String): Delegate2 { 32 | val delegate2 = Delegate2(initializer()) 33 | thread { 34 | runBlocking { 35 | launch { 36 | delegate2.stateFlow.collect { 37 | print("ooo") 38 | } 39 | } 40 | } 41 | } 42 | return delegate2; 43 | } 44 | 45 | 46 | class Delegate2(value2: String) { 47 | val stateFlow = MutableStateFlow(value2) 48 | 49 | operator fun getValue(thisRef: Any?, property: KProperty<*>): String { 50 | return stateFlow.value; 51 | } 52 | 53 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { 54 | this.stateFlow.value = value; 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/coroutines/UserServiceKtTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.coroutines 2 | 3 | import kotlinx.coroutines.runBlocking 4 | import org.junit.jupiter.api.Test 5 | import org.mvnsearch.spring.UserService 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * 10 | * @author linux_china 11 | */ 12 | class UserServiceKtTest { 13 | val userService: UserService = CoroutineInvocationHandler2.userServiceStub()//userServiceStub() 14 | 15 | @Test 16 | fun testFindByNick() = runBlocking { 17 | val nick = userService.getNickById(1) 18 | println(nick) 19 | } 20 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/eventloop/EventLoopTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.eventloop; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.concurrent.BlockingQueue; 6 | import java.util.concurrent.LinkedBlockingQueue; 7 | 8 | /** 9 | * event loop test 10 | * 11 | * @author linux_china 12 | */ 13 | public class EventLoopTest { 14 | 15 | @Test 16 | public void testEventLoop() throws Exception{ 17 | BlockingQueue q = new LinkedBlockingQueue<>(); 18 | new Thread(() -> { 19 | while (true) { 20 | try { 21 | String t = q.take(); 22 | System.out.println(t); 23 | //t will never be null, do something with it here. 24 | } catch (Exception ignore) { 25 | 26 | } 27 | } 28 | },"EventLoop").start(); 29 | q.put("first"); 30 | q.put("second"); 31 | Thread.sleep(1000); 32 | q.put("three"); 33 | Thread.sleep(1000); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/eventloop/ExecutorServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.eventloop; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.scheduler.Scheduler; 6 | import reactor.core.scheduler.Schedulers; 7 | 8 | import java.time.Duration; 9 | import java.util.concurrent.ArrayBlockingQueue; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.ThreadPoolExecutor; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * executor service 16 | * 17 | * @author linux_china 18 | */ 19 | public class ExecutorServiceTest { 20 | private static ExecutorService executorService = new ThreadPoolExecutor(1, 3, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(3)); 21 | 22 | @Test 23 | public void testExecute() throws Exception { 24 | for (int i = 2; i < 10; i++) { 25 | executorService.execute(delayRunnable(2)); 26 | } 27 | Thread.sleep(10000); 28 | } 29 | 30 | @Test 31 | public void testFlux() throws Exception { 32 | for (int i = 1; i < 100; i++) { 33 | Mono.just("111@111.com").flatMap(this::send).subscribe(t -> { 34 | System.out.println(t); 35 | System.out.println("Thread:" + Thread.currentThread().getName()); 36 | }); 37 | } 38 | Thread.sleep(10000); 39 | 40 | } 41 | 42 | private Runnable delayRunnable(int seconds) { 43 | return new Runnable() { 44 | @Override 45 | public void run() { 46 | try { 47 | TimeUnit.SECONDS.sleep(seconds); 48 | System.out.println("running"); 49 | } catch (Exception e) { 50 | 51 | } 52 | } 53 | }; 54 | } 55 | 56 | private Mono send(String email) { 57 | return Mono.delay(Duration.ofSeconds(5)).map(t -> email); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/flow/FlowTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.flow 2 | 3 | 4 | import kotlinx.coroutines.flow.* 5 | import kotlinx.coroutines.reactive.asFlow 6 | import kotlinx.coroutines.runBlocking 7 | import org.junit.jupiter.api.Test 8 | import reactor.core.publisher.Flux 9 | import java.util.concurrent.Flow 10 | import java.util.concurrent.SubmissionPublisher 11 | 12 | /** 13 | * Java Flow test 14 | * 15 | * @author linux_china 16 | */ 17 | class FlowTest { 18 | 19 | @Test 20 | fun testSpike() { 21 | val pub = SubmissionPublisher() 22 | pub.consume { println(it) } 23 | pub.submit(11L) 24 | Thread.sleep(1000) 25 | } 26 | 27 | @Test 28 | fun testSecond() { 29 | Flow.Publisher { } 30 | } 31 | 32 | @Test 33 | fun testFluxToFlow() { 34 | Flux.just("1", "2").asFlow(); 35 | } 36 | 37 | @Test 38 | fun testMutableFlow(): Unit = runBlocking { 39 | val flow = flowOf("first","second") 40 | val messages = flow.shareIn(this, SharingStarted.Eagerly, 0) 41 | messages.collect { 42 | print(it) 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/future/CompletableFutureTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.future; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | 10 | /** 11 | * completable future test https://learning.oreilly.com/library/view/java-high-performance-apps/9781789130515/ch05s04.html 12 | * 13 | * @author linux_china 14 | */ 15 | public class CompletableFutureTest { 16 | ExecutorService es = Executors.newFixedThreadPool(10); 17 | 18 | @Test 19 | public void testCompletableFuture() throws Exception { 20 | CompletableFuture future = CompletableFuture.supplyAsync(() -> "jackie", es); 21 | future.whenCompleteAsync((s, throwable) -> { 22 | System.out.println(s); 23 | }); 24 | Thread.sleep(1000); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/kafka_streams/KafkaStreamsDemo.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.kafka_streams; 2 | 3 | import org.apache.kafka.common.serialization.Serdes; 4 | import org.apache.kafka.streams.KafkaStreams; 5 | import org.apache.kafka.streams.StreamsBuilder; 6 | import org.apache.kafka.streams.StreamsConfig; 7 | import org.apache.kafka.streams.kstream.KStream; 8 | 9 | import java.util.Properties; 10 | import java.util.concurrent.CountDownLatch; 11 | 12 | public class KafkaStreamsDemo { 13 | 14 | public static void main(String[] args) throws Exception { 15 | final StreamsBuilder builder = new StreamsBuilder(); 16 | buildTopology(builder); 17 | final KafkaStreams streams = new KafkaStreams(builder.build(), kafkaProps()); 18 | final CountDownLatch latch = new CountDownLatch(1); 19 | // attach shutdown handler to catch control-c 20 | Runtime.getRuntime().addShutdownHook(new Thread("streams-shutdown-hook") { 21 | @Override 22 | public void run() { 23 | streams.close(); 24 | latch.countDown(); 25 | } 26 | }); 27 | try { 28 | streams.start(); 29 | latch.await(); 30 | } catch (Throwable e) { 31 | System.exit(1); 32 | } 33 | System.exit(0); 34 | } 35 | 36 | 37 | private static void buildTopology(StreamsBuilder builder) { 38 | KStream testTopic = builder.stream("testTopic"); 39 | testTopic.foreach((key, value) -> { 40 | System.out.println(value); 41 | }); 42 | } 43 | 44 | private static Properties kafkaProps() { 45 | Properties props = new Properties(); 46 | props.put(StreamsConfig.APPLICATION_ID_CONFIG, "streams-demo"); 47 | props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); 48 | props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass()); 49 | props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass()); 50 | return props; 51 | } 52 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/mutiny/SmallRyeMutinyTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.mutiny; 2 | 3 | import io.smallrye.mutiny.Multi; 4 | import io.smallrye.mutiny.Uni; 5 | import io.smallrye.mutiny.converters.uni.UniReactorConverters; 6 | import org.junit.jupiter.api.Test; 7 | import reactor.core.publisher.Mono; 8 | 9 | /** 10 | * SmallRye Mutiny test 11 | * 12 | * @author linux_china 13 | */ 14 | public class SmallRyeMutinyTest { 15 | 16 | @Test 17 | public void testUniOperation() { 18 | Uni.createFrom().item("Hello") 19 | .onItem().transform(s -> s.toUpperCase() + " ") 20 | .subscribe().with(System.out::print); 21 | } 22 | 23 | @Test 24 | public void testMultiOperation() { 25 | Multi.createFrom().items("hello", "world") 26 | .onItem().transform(s -> s.toUpperCase() + " ") 27 | .onCompletion().continueWith("!") 28 | .subscribe().with(System.out::print); 29 | } 30 | 31 | @Test 32 | public void testUniMono() { 33 | Uni uni = Uni.createFrom().item("Hello"); 34 | Mono mono = UniReactorConverters.toMono().apply(uni); 35 | System.out.println(mono.block()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/EncodingException.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * 6 | * @author linux_china 7 | */ 8 | public class EncodingException extends RuntimeException { 9 | 10 | public EncodingException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/EventListener.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | /** 4 | * event listener for guava 5 | * 6 | * @author linux_china 7 | */ 8 | public interface EventListener { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ExceptionTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | import reactor.retry.Repeat; 6 | 7 | import java.util.concurrent.atomic.AtomicInteger; 8 | 9 | /** 10 | * Exception test for doOnError, onErrorMap, onErrorReturn, and onErrorResume 11 | * 12 | * @author linux_china 13 | */ 14 | public class ExceptionTest { 15 | 16 | @Test 17 | public void testOnErrorMap() throws Exception { 18 | Mono.just("good").map(this::exceptionCall).doOnError(error -> { 19 | System.out.println(error.getMessage()); 20 | }).onErrorMap(EncodingException.class, error -> { 21 | System.out.println("map exception"); 22 | return new Exception("cattcched exception"); 23 | }).subscribe(text -> { 24 | System.out.println("subsribed: " + text); 25 | }); 26 | Thread.sleep(1000); 27 | } 28 | 29 | @Test 30 | public void testEmpty() { 31 | AtomicInteger atomicInteger = new AtomicInteger(1); 32 | Mono result = Mono.just(0) 33 | .map(num -> { 34 | System.out.println("map: " + atomicInteger.get()); 35 | return num + 1; 36 | }) 37 | .flatMap(text -> { 38 | System.out.println("flatMap: "); 39 | if (atomicInteger.incrementAndGet() <= 3) { 40 | return Mono.empty(); 41 | } 42 | return Mono.just(atomicInteger.get()); 43 | }).repeatWhenEmpty(Repeat.times(5)); 44 | System.out.println(result.block()); 45 | } 46 | 47 | public String exceptionCall(String text) throws EncodingException { 48 | throw new EncodingException("error to encode"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/MonoCacheTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Mono cache test 11 | * 12 | * @author linux_china 13 | */ 14 | public class MonoCacheTest { 15 | Map cache = new HashMap<>(); 16 | 17 | @Test 18 | public void testCacheOperate() throws Exception { 19 | Mono.just("first").doOnNext(s -> { 20 | cache.put("first", "第一"); 21 | }).subscribe(t -> { 22 | System.out.println(t); 23 | }); 24 | 25 | if (cache.containsKey("first")) { 26 | Mono.just(cache.get("first")).subscribe(t -> { 27 | System.out.println(t); 28 | }); 29 | } 30 | Thread.sleep(1000); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ProcessorTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor 2 | 3 | import org.junit.jupiter.api.Test 4 | import reactor.core.publisher.DirectProcessor 5 | import reactor.core.publisher.Flux 6 | 7 | /** 8 | * Processor test 9 | * 10 | * @author linux_china 11 | */ 12 | class ProcessorTest { 13 | 14 | @Test 15 | fun testSpike() { 16 | val processor = DirectProcessor.create() 17 | processor.onNext(1); 18 | processor.onComplete() 19 | } 20 | 21 | 22 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/R2dbcConnection.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.util.UUID; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * 10 | * @author linux_china 11 | */ 12 | public class R2dbcConnection { 13 | private String uuid = UUID.randomUUID().toString(); 14 | Mono con = Mono.create(sink -> { 15 | sink.success("good"); 16 | }); 17 | 18 | public Mono beginTransaction() { 19 | return Mono.empty(); 20 | } 21 | 22 | public Mono commit() { 23 | return Mono.empty(); 24 | } 25 | 26 | public Mono select() { 27 | return Mono.just("demo"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorContextTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | import reactor.core.scheduler.Schedulers; 6 | import reactor.util.context.Context; 7 | 8 | /** 9 | * Reactor context test https://projectreactor.io/docs/core/release/reference/#context 10 | * 11 | * @author linux_china 12 | */ 13 | public class ReactorContextTest { 14 | 15 | @Test 16 | public void testContextOf() throws Exception { 17 | Mono connection = Mono.create(sink -> { 18 | sink.success(new R2dbcConnection()); 19 | }); 20 | Mono.defer(() -> { 21 | return connection.flatMap(connection1 -> { 22 | return connection1.beginTransaction().contextWrite(Context.of(R2dbcConnection.class, connection1)); 23 | }); 24 | }) 25 | .thenReturn("goood").flatMap(s -> { 26 | return Mono.deferContextual(context -> { 27 | return Mono.just("second"); 28 | }); 29 | }) 30 | .subscribe(demo -> { 31 | System.out.println(demo); 32 | }); 33 | Thread.sleep(1000); 34 | } 35 | 36 | @Test 37 | public void testContextSupport() throws Exception { 38 | MutableContext context = new MutableContext(); 39 | context.put("nic2", "Jackie"); 40 | Mono.just("Hello") 41 | .flatMap(s -> Mono.deferContextual(ctx -> Mono.just(s + " " + ctx.get("nick")))) 42 | .contextWrite(ctx -> ctx.put("nick", "Reactor")) 43 | .contextWrite(context) 44 | .subscribe(t -> { 45 | System.out.println(t); 46 | }); 47 | Thread.sleep(1000); 48 | } 49 | 50 | @Test 51 | public void testDeferWithContext() throws Exception { 52 | Mono.deferContextual(ctx -> Mono.just(ctx.get("nick"))) 53 | .contextWrite(Context.of("nick", "linux_china")) 54 | .subscribe(nick -> { 55 | System.out.println(nick); 56 | }); 57 | Thread.sleep(1000); 58 | } 59 | 60 | @Test 61 | public void testThreadLocal() throws Exception { 62 | ThreadLocal userThreadLocal = new ThreadLocal<>(); 63 | userThreadLocal.set("yourNick"); 64 | MutableContext context = new MutableContext(); 65 | context.put("nick", userThreadLocal.get()); 66 | Mono.deferContextual(ctx -> Mono.just(ctx.get("nick"))) 67 | .contextWrite(context) 68 | .subscribe(text -> { 69 | System.out.println(text); 70 | }); 71 | Thread.sleep(1000); 72 | } 73 | 74 | @Test 75 | public void testScheduleHook() throws Exception { 76 | Schedulers.onScheduleHook("mdc", runnable -> () -> { 77 | System.out.println("before hook"); 78 | runnable.run(); 79 | System.out.println("after hook"); 80 | }); 81 | Mono.just("first") 82 | .doOnNext(text -> { 83 | System.out.println("Thread:" + Thread.currentThread().getName()); 84 | System.out.println("next"); 85 | }) 86 | .subscribeOn(Schedulers.immediate()) 87 | .subscribe(text -> { 88 | System.out.println("Thread:" + Thread.currentThread().getName()); 89 | System.out.println(text); 90 | }); 91 | 92 | Thread.sleep(1000); 93 | } 94 | 95 | @Test 96 | public void testContextFromOutside() { 97 | MutableContext context = new MutableContext(); 98 | context.put("greeting", "Hello"); 99 | String name = monoWithContext("Jackie").contextWrite(context).block(); 100 | System.out.println(name); 101 | } 102 | 103 | 104 | public Mono monoWithContext(String name) { 105 | return Mono.deferContextual((context) -> { 106 | return context.get("greeting"); 107 | }).map((greeting) -> { 108 | return greeting + " " + name; 109 | }); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorExceptionTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | 6 | import java.net.URI; 7 | 8 | /** 9 | * Reactor Exception test 10 | * 11 | * @author linux_china 12 | */ 13 | public class ReactorExceptionTest { 14 | @Test 15 | public void testExceptionDuringMap() throws Exception { 16 | Mono.just("https://www.taobao.com/").handle((text, sink) -> { 17 | try { 18 | sink.next(new URI(text)); 19 | } catch (Exception e) { 20 | sink.error(e); 21 | } 22 | }).subscribe(uri -> { 23 | System.out.println(uri.toString()); 24 | }); 25 | Thread.sleep(100); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorFluxTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import com.google.common.eventbus.EventBus; 4 | import com.google.common.eventbus.Subscribe; 5 | import org.junit.jupiter.api.Test; 6 | import org.reactivestreams.Publisher; 7 | import org.reactivestreams.Subscriber; 8 | import org.reactivestreams.Subscription; 9 | import org.springframework.context.ApplicationListener; 10 | import org.springframework.context.support.ClassPathXmlApplicationContext; 11 | import reactor.core.publisher.*; 12 | import reactor.core.scheduler.Schedulers; 13 | import reactor.test.publisher.TestPublisher; 14 | import reactor.util.context.Context; 15 | 16 | import java.nio.charset.StandardCharsets; 17 | import java.nio.file.Files; 18 | import java.nio.file.Path; 19 | import java.time.Duration; 20 | import java.time.LocalDateTime; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | import java.util.concurrent.atomic.AtomicLong; 24 | import java.util.function.BiFunction; 25 | import java.util.function.Function; 26 | import java.util.stream.Collectors; 27 | import java.util.stream.Stream; 28 | 29 | /** 30 | * reactor test 31 | * 32 | * @author linux_china 33 | */ 34 | public class ReactorFluxTest { 35 | 36 | @Test 37 | public void testFinally() throws Exception { 38 | Flux.just("abc", "123").doFinally(signalType -> { 39 | System.out.println("finally"); 40 | }).subscribe(s -> { 41 | System.out.println(s); 42 | }); 43 | Thread.sleep(1000); 44 | } 45 | 46 | @Test 47 | public void testReadFile() throws Exception { 48 | Flux lines = Flux.using( 49 | () -> Files.lines(Path.of("justfile"), StandardCharsets.UTF_8), 50 | Flux::fromStream, 51 | Stream::close 52 | ); 53 | lines.subscribe(System.out::println); 54 | Thread.sleep(1000); 55 | } 56 | 57 | @Test 58 | public void testConnectableFlux() throws Exception { 59 | ConnectableFlux flux = Flux.just("1", "2").publish(); 60 | flux.subscribe(seq -> { 61 | System.out.println("sub1:" + seq); 62 | }); 63 | System.out.println("sleep1"); 64 | Thread.sleep(1000); 65 | System.out.println("sleep2"); 66 | flux.subscribe(seq -> { 67 | System.out.println("sub2:" + seq); 68 | }); 69 | flux.connect(); 70 | Thread.sleep(2000); 71 | } 72 | 73 | @Test 74 | public void testStartWith() throws Exception { 75 | Flux.just("second", "third").startWith("first").subscribe(System.out::println); 76 | Thread.sleep(1000); 77 | 78 | Flux interval = Flux.interval(Duration.ofSeconds(1)); 79 | } 80 | 81 | @Test 82 | public void testDoFirst() throws Exception { 83 | Flux.just(1, 2, 3) 84 | .doFirst(() -> { 85 | System.out.println("first"); 86 | }) 87 | .doOnTerminate(() -> { 88 | System.out.println("finally"); 89 | }) 90 | .subscribe(number -> { 91 | System.out.println(number); 92 | }); 93 | Thread.sleep(1000); 94 | } 95 | 96 | @Test 97 | public void testTakeFirst() throws Exception { 98 | Flux.just("id,name", "1,leijuan", "2,juven").switchOnFirst((signal, stringFlux) -> { 99 | System.out.println("First: " + signal.get()); 100 | return stringFlux.skip(1); 101 | }).subscribe(text -> { 102 | System.out.println(text); 103 | }); 104 | Thread.sleep(1000); 105 | } 106 | 107 | @Test 108 | public void testDelaySequence() throws Exception { 109 | DirectProcessor processor = DirectProcessor.create(); 110 | System.out.println(LocalDateTime.now()); 111 | processor.delaySequence(Duration.ofSeconds(2)).subscribe(t -> { 112 | System.out.println(LocalDateTime.now() + ": " + t); 113 | }); 114 | processor.onNext(1); 115 | processor.onNext(2); 116 | System.out.println(LocalDateTime.now()); 117 | Thread.sleep(1000); 118 | processor.onNext(3); 119 | processor.onNext(4); 120 | Thread.sleep(5000); 121 | } 122 | 123 | /** 124 | * flux simple create, such as from array, list 125 | */ 126 | @Test 127 | public void testSimpleCreate() { 128 | Flux flux = Flux.just("red", "White", "blue").delayElements(Duration.ofMillis(100)); 129 | flux.map(String::toUpperCase).subscribe(System.out::println); 130 | flux.filter(str -> str.length() > 3).map(String::toLowerCase).subscribe(System.out::println); 131 | } 132 | 133 | @Test 134 | public void testMergeMonAndFlux() throws Exception { 135 | Flux.just(1).mergeWith(Flux.just(2, 3, 4)).subscribe(System.out::println); 136 | Thread.sleep(1000); 137 | } 138 | 139 | @Test 140 | public void testDefer() throws Exception { 141 | Flux.defer(() -> Flux.just("1", "2")).subscribe(t -> System.out.println(t)); 142 | Thread.sleep(1000); 143 | } 144 | 145 | @Test 146 | public void testContext() { 147 | Flux flux = Flux.just(1, 2); //1 148 | Flux stringFlux = flux.flatMap(i -> { 149 | return Mono.deferContextual(contextView -> { 150 | int pid = contextView.get("pid"); 151 | return Mono.just("pid:" + pid + ",value:" + i); 152 | }); 153 | }); 154 | stringFlux.contextWrite(Context.of("pid", 1)) 155 | .subscribe(System.out::println); 156 | } 157 | 158 | @Test 159 | public void testSwitchOnFirst() throws Exception { 160 | Flux.just(1, 2, 3).switchOnFirst((BiFunction, Flux, Publisher>) (signal, integerFlux) -> { 161 | System.out.println("signal:" + signal.get()); 162 | return integerFlux.skip(1); 163 | }).subscribe(num -> System.out.println(num)); 164 | Thread.sleep(1000); 165 | } 166 | 167 | @Test 168 | public void testBatch() { 169 | Flux flux = Flux.just(1, 2, 3, 4, 5); 170 | flux.buffer(2).map(list1 -> { 171 | System.out.println("size:" + list1); 172 | return list1.size(); 173 | }).subscribe(System.out::println); 174 | } 175 | 176 | @Test 177 | public void testTestPublisher() { 178 | TestPublisher publisher = TestPublisher.create(); 179 | publisher.subscribe(new Subscriber() { 180 | @Override 181 | public void onSubscribe(Subscription s) { 182 | s.request(10); 183 | } 184 | 185 | @Override 186 | public void onNext(Integer integer) { 187 | System.out.println(integer); 188 | } 189 | 190 | @Override 191 | public void onError(Throwable t) { 192 | 193 | } 194 | 195 | @Override 196 | public void onComplete() { 197 | 198 | } 199 | }); 200 | publisher.next(1); 201 | publisher.next(2); 202 | } 203 | 204 | 205 | @Test 206 | public void testSchedulers() throws Exception { 207 | Flux.generate( 208 | AtomicLong::new, 209 | (state, sink) -> { 210 | long i = state.getAndIncrement(); 211 | sink.next("3 x " + i + " = " + 3 * i); 212 | System.out.println("Generate thread:" + Thread.currentThread().getName()); 213 | if (i == 10) sink.complete(); 214 | return state; 215 | }) 216 | .publishOn(Schedulers.parallel()) 217 | .subscribeOn(Schedulers.single()) 218 | .subscribe(t -> { 219 | System.out.println("Subscribe thread:" + Thread.currentThread().getName()); 220 | System.out.println(t); 221 | }); 222 | 223 | 224 | Thread.sleep(1000); 225 | } 226 | 227 | /** 228 | * flux generate from Consumer> generator 229 | */ 230 | @Test 231 | public void testGenerate() { 232 | Flux flux = Flux.generate( 233 | AtomicLong::new, 234 | (state, sink) -> { 235 | long i = state.getAndIncrement(); 236 | sink.next("3 x " + i + " = " + 3 * i); 237 | if (i == 10) sink.complete(); 238 | return state; 239 | }); 240 | flux.subscribe(System.out::println); 241 | } 242 | 243 | @Test 244 | public void testCreate() throws Exception { 245 | EventBus eventBus = new EventBus(); 246 | Flux objectFlux = Flux.create(sink -> { 247 | eventBus.register(new EventListener() { 248 | @Subscribe 249 | public void stringEvent(String event) { 250 | sink.next(event); 251 | } 252 | }); 253 | }); 254 | objectFlux.subscribe(System.out::println); 255 | eventBus.post("first"); 256 | eventBus.post("second"); 257 | Thread.sleep(100); 258 | System.out.println("wake up"); 259 | eventBus.post("three"); 260 | eventBus.post("four"); 261 | Thread.sleep(10000); 262 | } 263 | 264 | @Test 265 | public void testPublisher() throws Exception { 266 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); 267 | Flux> upstreamFlux = Flux.create(sink -> { 268 | context.addApplicationListener((ApplicationListener) upstreamEvent -> sink.next(upstreamEvent.getIpList())); 269 | }); 270 | context.refresh(); 271 | upstreamFlux.subscribe(System.out::println); 272 | context.publishEvent(new UpstreamEvent(Arrays.asList("127", "128"))); 273 | Thread.sleep(10000); 274 | } 275 | 276 | @Test 277 | public void testZip() { 278 | Flux.just(1, 2).zipWith(Flux.just(3, 4)).subscribe(System.out::println); 279 | } 280 | 281 | @Test 282 | public void testRepeat() throws Exception { 283 | Flux.just(1, 3).repeat(2).subscribe(System.out::println); 284 | } 285 | 286 | @Test 287 | public void testWindow() { 288 | Flux.just(1, 2, 3, 4, 5) 289 | .window(2) 290 | .subscribe(flux -> flux.count().subscribe(System.out::println)); 291 | } 292 | 293 | @Test 294 | public void testTakeWhile() throws Exception { 295 | Flux.just(1, 2, 3, 4, 5) 296 | .takeUntil(number -> number < 3) 297 | .subscribe(System.out::println); 298 | Thread.sleep(1000); 299 | } 300 | 301 | 302 | @Test 303 | public void testBuffer() { 304 | Flux sequences = Flux.just("1", "2", "3", "4", "5"); 305 | sequences.buffer(2).flatMap(strings -> { 306 | System.out.println("length:" + strings.size()); 307 | return Flux.fromStream(strings.stream().map(i -> i + ":")); 308 | }).subscribe(System.out::print); 309 | } 310 | 311 | @Test 312 | public void testTerminal() { 313 | Flux names = Flux.just("one", "two", "three"); 314 | names.map(String::toUpperCase) 315 | .collect(Collectors.joining(",")) 316 | .subscribe(System.out::println); 317 | } 318 | 319 | @Test 320 | public void testThenMany() throws Exception { 321 | Function, Flux> filterAndMap = 322 | f -> f.filter(color -> !color.equals("orange")) 323 | .map(String::toUpperCase); 324 | 325 | Flux.just(1, 2, 3) 326 | .mergeWith(Flux.just(4, 5)) 327 | .delayElements(Duration.ofSeconds(1)) 328 | .doOnNext(t -> System.out.println("on:" + t)) 329 | .subscribe(System.out::println); 330 | Thread.sleep(7000); 331 | } 332 | 333 | } 334 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorKotlinTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor 2 | 3 | import org.junit.jupiter.api.Test 4 | import reactor.core.publisher.EmitterProcessor 5 | import reactor.core.publisher.Mono 6 | import reactor.kotlin.core.publisher.toMono 7 | import reactor.kotlin.test.test 8 | 9 | /** 10 | * reactor kotlin test 11 | * 12 | * @author linux_china 13 | */ 14 | 15 | class ReactorKotlinTest { 16 | 17 | @Test 18 | fun testMono() { 19 | val mono = "foo".toMono() 20 | mono.subscribe() { 21 | println(it) 22 | } 23 | mono.test().expectNext("foo").verifyComplete(); 24 | } 25 | 26 | @Test 27 | fun testCallable() { 28 | Mono.fromCallable { "good" }.subscribe { println(it) } 29 | } 30 | 31 | @Test 32 | fun testPublisher() { 33 | val emitter = EmitterProcessor.create() 34 | emitter 35 | .map { it + 1 } 36 | .subscribe { println(it) } 37 | emitter.onNext(1) 38 | emitter.onNext(2) 39 | } 40 | 41 | @Test 42 | fun testDefer() { 43 | val lazyValue by lazy { 44 | println("computed!") 45 | "Hello" 46 | } 47 | Mono.just(lazyValue).subscribe { println(it) } 48 | } 49 | 50 | 51 | @Test 52 | fun testMonoGenerator() { 53 | Mono.fromSupplier { "demo" }.subscribe { } 54 | } 55 | 56 | @Test 57 | fun testException() { 58 | Mono.empty() 59 | .thenReturn("first") 60 | .subscribe { 61 | println(it) 62 | } 63 | Thread.sleep(1000) 64 | } 65 | 66 | 67 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorMonoTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import com.google.common.base.Joiner; 4 | import com.google.common.eventbus.EventBus; 5 | import com.google.common.eventbus.Subscribe; 6 | import org.junit.jupiter.api.Test; 7 | import org.reactivestreams.Publisher; 8 | import org.reactivestreams.Subscriber; 9 | import org.reactivestreams.Subscription; 10 | import reactor.core.publisher.Flux; 11 | import reactor.core.publisher.Mono; 12 | import reactor.core.publisher.MonoProcessor; 13 | import reactor.util.context.Context; 14 | 15 | import java.time.Duration; 16 | import java.util.Arrays; 17 | import java.util.List; 18 | import java.util.Random; 19 | import java.util.function.BiFunction; 20 | import java.util.function.Function; 21 | 22 | /** 23 | * reactor mon test 24 | * 25 | * @author linux_china 26 | */ 27 | public class ReactorMonoTest { 28 | 29 | @Test 30 | public void testTransform() throws Exception { 31 | Mono.just("first").transformDeferred(origin -> { 32 | return Mono.just("demo"); 33 | }).subscribe(text -> { 34 | System.out.println(text); 35 | }); 36 | Thread.sleep(1000); 37 | } 38 | 39 | @Test 40 | public void testContext() throws Exception { 41 | Mono first = Mono.just("demo"); 42 | Mono.deferContextual(context -> { 43 | System.out.println(context.get("name").toString()); 44 | return first; 45 | }).contextWrite(Context.of("name", "value")) 46 | .subscribe(text -> { 47 | System.out.println(text); 48 | }); 49 | Thread.sleep(1000); 50 | } 51 | 52 | @Test 53 | public void testTimeout() throws Exception { 54 | Mono.just("first").delayElement(Duration.ofSeconds(5)) 55 | .timeout(Duration.ofSeconds(2)) 56 | .doOnError(throwable -> { 57 | System.out.println(throwable.getClass()); 58 | }).subscribe(); 59 | Thread.sleep(10000); 60 | } 61 | 62 | @Test 63 | public void testOnSuccess() throws Exception { 64 | Mono.empty().doOnSuccess(o -> { 65 | System.out.println("goodo"); 66 | }).subscribe(); 67 | 68 | Thread.sleep(1000); 69 | } 70 | 71 | @Test 72 | public void testTryFinally() throws Exception { 73 | Mono.just("demo").doOnSubscribe(subscription -> { 74 | System.out.println("Begin to subscribe"); 75 | }).doFinally((signalType -> { 76 | System.out.println("finished"); 77 | })).subscribe(); 78 | Thread.sleep(100); 79 | } 80 | 81 | @Test 82 | public void testDemo() throws Exception { 83 | Mono.empty().doOnSuccess(data -> { 84 | System.out.println(data); 85 | }).subscribe(); 86 | Thread.sleep(1000); 87 | } 88 | 89 | @Test 90 | public void testCache() throws Exception { 91 | Mono user = Mono.create(monoSink -> { 92 | System.out.println("Only Once"); 93 | monoSink.success("nick"); 94 | }).cache(Duration.ofSeconds(2)); 95 | user.subscribe(t -> { 96 | System.out.println(t); 97 | }); 98 | user.subscribe(t -> { 99 | System.out.println(t); 100 | }); 101 | Thread.sleep(3000); 102 | user.subscribe(t -> { 103 | System.out.println(t); 104 | }); 105 | user.subscribe(t -> { 106 | System.out.println(t); 107 | }); 108 | Thread.sleep(1000); 109 | } 110 | 111 | @Test 112 | public void testDefer() throws Exception { 113 | Mono nick = getNick(); 114 | System.out.println("nick invoked"); 115 | Mono defer = Mono.defer(this::getNick); 116 | defer.subscribe(text -> { 117 | System.out.println(text); 118 | }); 119 | defer.subscribe(text -> { 120 | System.out.println(text); 121 | }); 122 | Thread.sleep(1000); 123 | } 124 | 125 | @Test 126 | public void delayUntil() throws Exception { 127 | Mono demo = Mono.just(1).delayUntil(new Function>() { 128 | @Override 129 | public Publisher apply(Integer number) { 130 | return Mono.just(number).delay(Duration.ofSeconds(1)); 131 | } 132 | }); 133 | demo.subscribe(num -> { 134 | System.out.println(num); 135 | }); 136 | System.out.println("good"); 137 | Thread.sleep(2000); 138 | 139 | } 140 | 141 | public Mono getNick() { 142 | System.out.println("get nick"); 143 | return Mono.just("nick"); 144 | } 145 | 146 | @Test 147 | public void testTimeOut() throws Exception { 148 | MonoProcessor.create((sink -> { 149 | //operation here 150 | })).timeout(Duration.ofSeconds(2)).doOnError((ex) -> { 151 | System.out.println(ex.getMessage()); 152 | }).subscribe(); 153 | Thread.sleep(5000); 154 | } 155 | 156 | @Test 157 | public void testOperations() throws Exception { 158 | Mono r1 = Mono.just(1); 159 | Mono r2 = Mono.error(new Exception("Not a number")); 160 | Mono r3 = Mono.empty(); 161 | Mono.empty() 162 | .doOnNext(num -> { 163 | System.out.println(num); 164 | }) 165 | .doOnError(error -> { 166 | System.out.println("error"); 167 | }) 168 | .switchIfEmpty(Mono.fromRunnable(() -> { 169 | System.out.println("empty"); 170 | })) 171 | .subscribe(); 172 | Thread.sleep(1000); 173 | } 174 | 175 | @Test 176 | public void testEmptyWithError() { 177 | System.out.println(Mono.error(new Exception("goood")).switchIfEmpty(Mono.just("first")).block()); 178 | } 179 | 180 | @Test 181 | public void testMonoProcessor() throws Exception { 182 | MonoProcessor monoProcessor = MonoProcessor.create(); 183 | monoProcessor.subscribe(System.out::println); 184 | monoProcessor.onNext("first"); 185 | Thread.sleep(1000); 186 | } 187 | 188 | @Test 189 | public void testSpike() throws Exception { 190 | Mono.just(1).dematerialize().subscribe(t -> { 191 | System.out.println(t); 192 | }); 193 | Thread.sleep(1000); 194 | } 195 | 196 | @Test 197 | public void testDynamicMono() { 198 | Mono integerMono = Mono.fromCallable(() -> (new Random()).nextInt()); 199 | System.out.println(integerMono.block()); 200 | System.out.println(integerMono.block()); 201 | System.out.println(integerMono.block()); 202 | System.out.println(integerMono.block()); 203 | System.out.println(integerMono.block()); 204 | } 205 | 206 | @Test 207 | public void testTerminal() throws Exception { 208 | MonoProcessor onClose = MonoProcessor.create(); 209 | onClose.doOnTerminate(() -> { 210 | System.out.println("terminal1"); 211 | }).subscribe(); 212 | onClose.doOnTerminate(() -> { 213 | System.out.println("terminal2"); 214 | }).subscribe(); 215 | onClose.onComplete(); 216 | Thread.sleep(1000); 217 | } 218 | 219 | @Test 220 | public void testCreate() { 221 | Mono.fromCallable(() -> "good").subscribe(System.out::println); 222 | Mono.just("good").subscribe(System.out::println); 223 | Mono.just("first").doOnNext(it -> { 224 | System.out.println(it); 225 | }).subscribe(it -> { 226 | System.out.println(it); 227 | }); 228 | } 229 | 230 | @Test 231 | public void testListen() { 232 | System.out.println(Mono.just(1).block()); 233 | System.out.println(Flux.just(1, 2, 3).collectList().block()); 234 | } 235 | 236 | 237 | @Test 238 | public void testZipWith() { 239 | Mono result = Mono.just(1); 240 | result.zipWith(Mono.just("demo"), new BiFunction() { 241 | @Override 242 | public String apply(Integer integer, String s) { 243 | return s + ":" + integer; 244 | } 245 | }).subscribe(t -> { 246 | System.out.println(t); 247 | }); 248 | } 249 | 250 | @Test 251 | public void testPublisher() throws Exception { 252 | EventBus eventBus = new EventBus(); 253 | final Flux> flux = Flux.create(sink -> { 254 | eventBus.register(new EventListener() { 255 | @Subscribe 256 | public void stringEvent(List event) { 257 | sink.next(event); 258 | } 259 | }); 260 | }); 261 | Publisher> publisher = new Publisher>() { 262 | @Override 263 | public void subscribe(Subscriber> subscriber) { 264 | flux.subscribe(s -> { 265 | subscriber.onNext(s); 266 | }); 267 | } 268 | }; 269 | publisher.subscribe(new Subscriber>() { 270 | private Subscription subscription; 271 | 272 | @Override 273 | public void onSubscribe(Subscription subscription) { 274 | this.subscription = subscription; 275 | subscription.request(Long.MAX_VALUE); 276 | } 277 | 278 | @Override 279 | public void onNext(List strings) { 280 | System.out.println(Joiner.on(',').join(strings)); 281 | } 282 | 283 | @Override 284 | public void onError(Throwable throwable) { 285 | 286 | } 287 | 288 | @Override 289 | public void onComplete() { 290 | 291 | } 292 | }); 293 | eventBus.post(Arrays.asList("first")); 294 | Thread.sleep(1000); 295 | eventBus.post(Arrays.asList("second")); 296 | Thread.sleep(1000); 297 | eventBus.post(Arrays.asList("third")); 298 | Thread.sleep(10000); 299 | 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorPoolTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | import reactor.pool.Pool; 6 | import reactor.pool.PoolBuilder; 7 | import reactor.pool.PoolConfig; 8 | 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | import java.util.function.Function; 11 | 12 | /** 13 | * reactor pool test 14 | * 15 | * @author linux_china 16 | */ 17 | public class ReactorPoolTest { 18 | 19 | @Test 20 | public void testPool() throws Exception { 21 | 22 | AtomicInteger newCount = new AtomicInteger(); 23 | PoolBuilder> builder = PoolBuilder 24 | //default maxUse is 5, but this test relies on it being 2 25 | .from(Mono.defer(() -> Mono.just(newCount.getAndIncrement()))) 26 | .sizeBetween(2, 3); 27 | Pool pool = ReactorPoolTest.simplePoolFifo().apply(builder); 28 | pool.withPoolable(resource -> { 29 | return Mono.just(resource + 1); 30 | }).subscribe(System.out::println); 31 | Thread.sleep(1000); 32 | } 33 | 34 | static final Function, Pool> simplePoolFifo() { 35 | return new Function<>() { 36 | @Override 37 | public Pool apply(PoolBuilder builder) { 38 | return builder.buildPool(); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "simplePool FIFO"; 44 | } 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorProcessorTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor 2 | 3 | import org.junit.jupiter.api.Test 4 | import reactor.core.publisher.DirectProcessor 5 | import reactor.core.publisher.ReplayProcessor 6 | 7 | /** 8 | * reactor processor test, please refer https://learning.oreilly.com/library/view/hands-on-reactive-programming/9781789135794/1e2fe499-843f-4e14-a5cb-0765ffed2f15.xhtml 9 | * 10 | * @author linux_china 11 | */ 12 | 13 | class ReactorProcessorTest { 14 | 15 | @Test 16 | fun testDirectProcessor() { 17 | val processor = DirectProcessor.create() 18 | processor.subscribe { println(it) } 19 | processor.onNext("good") 20 | processor.onNext("morning") 21 | } 22 | 23 | @Test 24 | fun testGetLastForSubscribe() { 25 | val processor = ReplayProcessor.cacheLast() 26 | processor.subscribe { println("S1:$it") } 27 | processor.onNext("good") 28 | processor.onNext("morning") 29 | processor.subscribe { println("S2:$it") } 30 | val mono = processor.last(); 31 | mono.subscribe { 32 | println("last:${it}") 33 | } 34 | Thread.sleep(1000) 35 | } 36 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/ReactorToolsTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | import reactor.core.publisher.Flux; 6 | import reactor.tools.agent.ReactorDebugAgent; 7 | 8 | /** 9 | * reactor tools test 10 | * 11 | * @author linux_china 12 | */ 13 | public class ReactorToolsTest { 14 | 15 | 16 | @BeforeAll 17 | public static void setUp() { 18 | ReactorDebugAgent.init(); 19 | } 20 | 21 | @Test 22 | public void testDemo() throws Exception { 23 | Flux.range(0, 5) 24 | .single().subscribe(); 25 | Thread.sleep(1000); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/Resilience4jTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import io.github.resilience4j.ratelimiter.RateLimiter; 4 | import io.github.resilience4j.ratelimiter.RateLimiterConfig; 5 | import io.github.resilience4j.reactor.ratelimiter.operator.RateLimiterOperator; 6 | import org.junit.jupiter.api.Test; 7 | import reactor.core.publisher.Mono; 8 | 9 | import java.time.Duration; 10 | 11 | /** 12 | * resilience4j with Reactor test 13 | * 14 | * @author linux_china 15 | */ 16 | public class Resilience4jTest { 17 | 18 | @Test 19 | public void testLimiter() throws Exception { 20 | RateLimiterConfig config = RateLimiterConfig.custom() 21 | .limitRefreshPeriod(Duration.ofMillis(1000)) 22 | .limitForPeriod(2) 23 | .build(); 24 | RateLimiter rateLimiter = RateLimiter.of("rpcLimiter", config); 25 | for (int i = 0; i < 10; i++) { 26 | Mono stringMono = Mono.fromCallable(this::getNick) 27 | .transformDeferred(RateLimiterOperator.of(rateLimiter)); 28 | stringMono.subscribe(text -> { 29 | System.out.println(text); 30 | }); 31 | } 32 | Thread.sleep(1000); 33 | } 34 | 35 | public String getNick() { 36 | return "nick"; 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/RetrofitTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.jakewharton.retrofit2.adapter.reactor.ReactorCallAdapterFactory; 6 | import okhttp3.OkHttpClient; 7 | import org.junit.jupiter.api.Test; 8 | import reactor.core.publisher.Mono; 9 | import retrofit2.Retrofit; 10 | import retrofit2.converter.jackson.JacksonConverterFactory; 11 | 12 | /** 13 | * retrofit test 14 | * 15 | * @author linux_china 16 | */ 17 | public class RetrofitTest { 18 | 19 | @Test 20 | public void testRetrofitMono() throws Exception { 21 | HttpBinAPI httpBinAPI = httpBinServiceRetrofitAPI(); 22 | Mono ip = httpBinAPI.ip(); 23 | ip.subscribe(httpBinResponse -> System.out.print(httpBinResponse.getIp())); 24 | Thread.sleep(1000); 25 | } 26 | 27 | private HttpBinAPI httpBinServiceRetrofitAPI() { 28 | return new Retrofit.Builder(). 29 | baseUrl("http://httpbin.org"). 30 | client(okHttpClient()). 31 | addCallAdapterFactory(ReactorCallAdapterFactory.create()). 32 | addConverterFactory(JacksonConverterFactory.create(objectMapper())). 33 | build(). 34 | create(HttpBinAPI.class); 35 | } 36 | 37 | private OkHttpClient okHttpClient() { 38 | return new OkHttpClient.Builder().build(); 39 | } 40 | 41 | private ObjectMapper objectMapper() { 42 | ObjectMapper mapper = new ObjectMapper(); 43 | mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 44 | return mapper; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/RetryTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.reactivestreams.Publisher; 5 | import reactor.core.publisher.Mono; 6 | 7 | import java.util.function.Supplier; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * 12 | * @author linux_china 13 | */ 14 | public class RetryTest { 15 | @Test 16 | public void testRetry() throws Exception { 17 | Mono.fromCallable(() -> { 18 | System.out.println("call:" + System.currentTimeMillis()); 19 | return "demo"; 20 | }) 21 | .retry(2) 22 | .subscribe(text -> { 23 | System.out.println("text:" + text); 24 | }); 25 | Thread.sleep(2000); 26 | } 27 | 28 | @Test 29 | public void testRetryDefer() { 30 | System.out.println(Publisher.class.isAssignableFrom(Mono.class)); 31 | } 32 | 33 | private Supplier> textSupply() { 34 | return () -> { 35 | System.out.println("call"); 36 | return Mono.error(new Exception("bad")); 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/SchedulerTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | 4 | import org.junit.jupiter.api.Test; 5 | import reactor.core.Disposable; 6 | import reactor.core.scheduler.Scheduler; 7 | import reactor.core.scheduler.Schedulers; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * Scheduler test 13 | * 14 | * @author linux_china 15 | */ 16 | public class SchedulerTest { 17 | private static Scheduler scheduler = Schedulers.single(); 18 | 19 | @Test 20 | public void testRunNow() throws Exception { 21 | Disposable disposable = scheduler.schedule(() -> { 22 | System.out.println("good"); 23 | }); 24 | } 25 | 26 | @Test 27 | public void testDelay() throws Exception { 28 | Disposable disposable = scheduler.schedule(() -> { 29 | System.out.println("good"); 30 | }, 2, TimeUnit.SECONDS); 31 | //disposable.dispose(); 32 | Thread.sleep(3000); 33 | } 34 | 35 | @Test 36 | public void testPeriodicalCall() throws Exception { 37 | Disposable disposable = scheduler.schedulePeriodically(() -> { 38 | System.out.println("good"); 39 | }, 0, 1, TimeUnit.SECONDS); 40 | Thread.sleep(9000); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/SvelteTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import io.reactivex.rxjava3.core.Single; 4 | import io.reactivex.rxjava3.subjects.BehaviorSubject; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * 10 | * @author linux_china 11 | */ 12 | public class SvelteTest { 13 | 14 | @Test 15 | public void testComponentProp() throws Exception { 16 | BehaviorSubject subject = BehaviorSubject.create(); 17 | subject.subscribe(num -> { 18 | System.out.println("subscriber1: " + num); 19 | }); 20 | subject.subscribe(num -> { 21 | System.out.println("subscriber2: " + num); 22 | }); 23 | subject.onNext(1); 24 | Thread.sleep(2000); 25 | } 26 | 27 | @Test 28 | public void testAwait() throws Exception { 29 | Single single = Single.just(1); 30 | single.subscribe(num -> { 31 | System.out.println("subscriber1: " + num); 32 | }); 33 | single.subscribe(num -> { 34 | System.out.println("subscriber2: " + num); 35 | }); 36 | Thread.sleep(2000); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/TestPublisherTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Flux; 5 | import reactor.test.publisher.PublisherProbe; 6 | import reactor.test.publisher.TestPublisher; 7 | 8 | /** 9 | * test publisher test 10 | * 11 | * @author linux_china 12 | */ 13 | public class TestPublisherTest { 14 | 15 | @Test 16 | public void testFlux() throws Exception { 17 | TestPublisher testPublisher = TestPublisher.create(); 18 | testPublisher.mono().subscribe(t -> System.out.println(t)); 19 | testPublisher.flux().subscribe(t -> System.out.println(t)); 20 | testPublisher.next(1); 21 | testPublisher.next(2); 22 | testPublisher.assertSubscribers(); 23 | Thread.sleep(1000); 24 | } 25 | 26 | @Test 27 | public void testProbe() { 28 | PublisherProbe probe = PublisherProbe.of(Flux.just(1, 2)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/UpstreamEvent.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * upstream event 9 | * 10 | * @author linux_china 11 | */ 12 | public class UpstreamEvent extends ApplicationEvent { 13 | private List ipList; 14 | 15 | public UpstreamEvent(List ipList) { 16 | super(ipList); 17 | this.ipList = ipList; 18 | } 19 | 20 | public List getIpList() { 21 | return ipList; 22 | } 23 | 24 | public void setIpList(List ipList) { 25 | this.ipList = ipList; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/UserServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import kotlin.coroutines.EmptyCoroutineContext; 4 | import kotlinx.coroutines.reactor.MonoKt; 5 | import kotlinx.coroutines.reactor.ReactorFlowKt; 6 | import org.junit.jupiter.api.Test; 7 | import org.mvnsearch.spring.KotlinCoroutineMethod; 8 | import org.mvnsearch.spring.UserServiceImpl; 9 | import reactor.core.publisher.Flux; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | * User service test 16 | * 17 | * @author linux_china 18 | */ 19 | public class UserServiceImplTest { 20 | private UserServiceImpl userService = new UserServiceImpl(); 21 | 22 | @Test 23 | public void testMethods() { 24 | /*Method method = findMethod(userService.getClass(), "getNickById"); 25 | KotlinCoroutineMethod coroutineMethod = new KotlinCoroutineMethod(method); 26 | System.out.println(coroutineMethod); 27 | KotlinCoroutineMethod flowMethod = new KotlinCoroutineMethod(findMethod(userService.getClass(), "getAllNames")); 28 | System.out.println(flowMethod);*/ 29 | KotlinCoroutineMethod job1Method = new KotlinCoroutineMethod(findMethod(userService.getClass(), "getNicks")); 30 | System.out.println(job1Method); 31 | } 32 | 33 | 34 | @Test 35 | public void testConvertToReactor() throws Exception { 36 | Method method = findMethod(userService.getClass(), "getNickById"); 37 | Mono mono = MonoKt.mono(EmptyCoroutineContext.INSTANCE, 38 | (coroutineScope, continuation) -> { 39 | Object obj = null; 40 | try { 41 | Object[] args = new Object[]{1, continuation}; 42 | obj = method.invoke(userService, args); 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | return obj; 47 | }); 48 | mono.map(text -> { 49 | return "text: " + text; 50 | }).subscribe(text -> { 51 | System.out.println(text); 52 | }); 53 | Thread.sleep(1000); 54 | } 55 | 56 | @Test 57 | public void testFlowToFlux() { 58 | /* userService.getAllNamesFlux().toStream().forEach(s -> { 59 | System.out.println(s); 60 | });*/ 61 | Flux flux = ReactorFlowKt.asFlux(userService.getAllNames()); 62 | flux.toStream().forEach(s -> { 63 | System.out.println(s); 64 | }); 65 | } 66 | 67 | public Method findMethod(Class clazz, String methodName) { 68 | for (Method method : clazz.getDeclaredMethods()) { 69 | if (method.getName().equals(methodName)) { 70 | return method; 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/WebClientTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.web.reactive.function.client.WebClient; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * webclient test 10 | * 11 | * @author linux_china 12 | */ 13 | public class WebClientTest { 14 | 15 | @Test 16 | public void testDemo() throws Exception { 17 | WebClient webClient = WebClient.builder().build(); 18 | webClient 19 | .get() 20 | .uri("http://httpbin.org/ip") 21 | .retrieve() 22 | .bodyToMono(Map.class). 23 | subscribe(result->System.out.println(result.get("origin"))); 24 | Thread.sleep(1000); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/blockhound/BlockHoundTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.blockhound; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | 6 | import java.security.SecureRandom; 7 | import java.time.Duration; 8 | 9 | /** 10 | * block hound test 11 | * 12 | * @author linux_china 13 | */ 14 | public class BlockHoundTest { 15 | 16 | @Test 17 | public void testBlockDetect() throws Exception { 18 | Mono.delay(Duration.ofSeconds(1)) 19 | .doOnNext(it -> { 20 | try { 21 | byte[] demo = new byte[128]; 22 | (new SecureRandom()).nextBytes(demo); 23 | } catch (Exception e) { 24 | throw new RuntimeException(e); 25 | } 26 | }).subscribe(); 27 | Thread.sleep(3000); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/filterchain/FilterChain.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.filterchain; 2 | 3 | import reactor.core.publisher.Flux; 4 | import reactor.core.publisher.Mono; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * 12 | * @author linux_china 13 | */ 14 | public class FilterChain { 15 | private List filters = new ArrayList<>(); 16 | 17 | public void add(ItemFilter filter) { 18 | this.filters.add(filter); 19 | } 20 | 21 | public Mono execute(Object item) { 22 | return Flux.fromIterable(filters) 23 | .filterWhen(filter -> filter.test(item)) 24 | .then(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/filterchain/FilterChainTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.filterchain; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import reactor.core.publisher.Mono; 5 | 6 | /** 7 | * filter chain test 8 | * 9 | * @author linux_china 10 | */ 11 | public class FilterChainTest { 12 | 13 | @Test 14 | public void testFilter() throws Exception{ 15 | FilterChain chain = new FilterChain(); 16 | chain.add(item -> { 17 | System.out.println("filter1: " + item); 18 | return Mono.just(true); 19 | }); 20 | chain.add(item -> { 21 | System.out.println("filter2: " + item); 22 | return Mono.just(false); 23 | }); 24 | chain.add(item -> { 25 | System.out.println("filter3: " + item); 26 | return Mono.just(true); 27 | }); 28 | 29 | chain.execute("good").subscribe(); 30 | Thread.sleep(1000); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/filterchain/ItemFilter.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.filterchain; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | /** 6 | * item filter 7 | * 8 | * @author linux_china 9 | */ 10 | public interface ItemFilter { 11 | 12 | Mono test(Object item); 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/kafka/KafkaReactorReceiverTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.kafka; 2 | 3 | import org.apache.kafka.clients.consumer.ConsumerConfig; 4 | import org.apache.kafka.common.serialization.StringDeserializer; 5 | import org.junit.jupiter.api.AfterAll; 6 | import org.junit.jupiter.api.BeforeAll; 7 | import org.junit.jupiter.api.Test; 8 | import reactor.core.publisher.Flux; 9 | import reactor.kafka.receiver.KafkaReceiver; 10 | import reactor.kafka.receiver.ReceiverOptions; 11 | import reactor.kafka.receiver.ReceiverRecord; 12 | 13 | import java.util.Collections; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | /** 18 | * kafka receiver test 19 | * 20 | * @author linux_china 21 | */ 22 | public class KafkaReactorReceiverTest { 23 | private static Flux> inboundFlux; 24 | 25 | @BeforeAll 26 | public static void setUp() { 27 | inboundFlux = KafkaReceiver.create(ReceiverOptions.create(consumerProps()) 28 | .subscription(Collections.singleton("testTopic"))) 29 | .receive(); 30 | } 31 | 32 | @AfterAll 33 | public static void tearDown() { 34 | } 35 | 36 | @Test 37 | public void testConsumer() { 38 | inboundFlux.subscribe(t -> { 39 | System.out.println(t); 40 | }); 41 | } 42 | 43 | private static Map consumerProps() { 44 | Map consumerProps = new HashMap<>(); 45 | consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); 46 | consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "sample-group"); 47 | consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 48 | consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 49 | return consumerProps; 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/kafka/KafkaReactorSenderTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.kafka; 2 | 3 | import org.apache.kafka.clients.producer.ProducerConfig; 4 | import org.apache.kafka.common.serialization.StringSerializer; 5 | import org.junit.jupiter.api.BeforeAll; 6 | import org.junit.jupiter.api.Test; 7 | import reactor.core.publisher.Flux; 8 | import reactor.kafka.sender.KafkaSender; 9 | import reactor.kafka.sender.SenderOptions; 10 | import reactor.kafka.sender.SenderRecord; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.UUID; 15 | 16 | /** 17 | * kafka reactor sender test 18 | * 19 | * @author linux_china 20 | */ 21 | public class KafkaReactorSenderTest { 22 | private static KafkaSender sender; 23 | 24 | @BeforeAll 25 | public static void setUp() { 26 | sender = KafkaSender.create(SenderOptions.create(producerProps()).maxInFlight(1024)); 27 | } 28 | 29 | public Flux outboundFlux() { 30 | return Flux.just("first", "second"); 31 | } 32 | 33 | @Test 34 | public void testSend() { 35 | //no metadata 36 | //sender.createOutbound().send(outboundFlux().map(text -> new ProducerRecord<>("testTopic", UUID.randomUUID().toString(), text))).then().subscribe(); 37 | //with metadata 38 | sender.send(outboundFlux().map(text -> SenderRecord.create("testTopic", 1, System.currentTimeMillis(), UUID.randomUUID().toString(), text, null))).subscribe(); 39 | //close sender 40 | //sender.close(); 41 | } 42 | 43 | 44 | public static Map producerProps() { 45 | Map producerProps = new HashMap<>(); 46 | producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); 47 | producerProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 48 | producerProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 49 | return producerProps; 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/netty/NettyClientTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.netty; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.jupiter.api.Test; 5 | import reactor.core.Exceptions; 6 | import reactor.core.publisher.Mono; 7 | import reactor.netty.Connection; 8 | import reactor.netty.tcp.TcpClient; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.IOException; 12 | 13 | /** 14 | * Netty TCP client test 15 | * 16 | * @author linux_china 17 | */ 18 | public class NettyClientTest { 19 | private static ObjectMapper objectMapper = new ObjectMapper(); 20 | 21 | @Test 22 | public void testConnect() throws Exception { 23 | Connection client = TcpClient.create().host("127.0.0.1").port(1234) 24 | .handle((in, out) -> { //in 25 | in.receive() 26 | .asByteArray() 27 | .log("receive") 28 | .subscribe(data -> { 29 | try { 30 | User user = objectMapper.readValue(data, User.class); 31 | System.out.println("Nick:" + user.getNick()); 32 | } catch (Exception e) { 33 | 34 | } 35 | }); 36 | 37 | //out 38 | return out.send(Mono.just(new User(1, "linux_china")) 39 | .map(s -> { 40 | try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { 41 | objectMapper.writeValue(os, s); 42 | return out.alloc() 43 | .buffer() 44 | .writeBytes(os.toByteArray()); 45 | } catch (IOException ioe) { 46 | throw Exceptions.propagate(ioe); 47 | } 48 | })).neverComplete(); 49 | }) 50 | .wiretap(true) 51 | .connectNow(); 52 | Thread.sleep(2000); 53 | client.dispose(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/netty/TcpServerApp.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.netty; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.reactivestreams.Publisher; 6 | import reactor.core.Exceptions; 7 | import reactor.core.publisher.Mono; 8 | import reactor.netty.NettyInbound; 9 | import reactor.netty.NettyOutbound; 10 | import reactor.netty.tcp.TcpServer; 11 | 12 | import java.io.IOException; 13 | import java.util.concurrent.CountDownLatch; 14 | import java.util.function.BiFunction; 15 | 16 | /** 17 | * Netty Reactor Tcp Server 18 | * 19 | * @author linux_china 20 | */ 21 | public class TcpServerApp { 22 | private static ObjectMapper objectMapper = new ObjectMapper(); 23 | 24 | public static void main(String[] args) throws Exception { 25 | CountDownLatch latch = new CountDownLatch(1); 26 | TcpServer.create() 27 | .handle(new BiFunction>() { 28 | @Override 29 | public Publisher apply(NettyInbound in, NettyOutbound out) { 30 | in.receive() 31 | .asByteArray() 32 | .map(bb -> { 33 | try { 34 | return objectMapper.readValue(bb, User.class); 35 | } catch (IOException io) { 36 | throw Exceptions.propagate(io); 37 | } 38 | }) 39 | .log("conn") 40 | .subscribe(data -> { 41 | if ("Kill".equals(data.getNick())) { 42 | latch.countDown(); 43 | } 44 | System.out.println("Nick:" + data.getNick()); 45 | }); 46 | 47 | try { 48 | return out.sendByteArray(Mono.just(objectMapper.writeValueAsBytes(new User(1, "Jackie")))).neverComplete(); 49 | } catch (JsonProcessingException e) { 50 | e.printStackTrace(); 51 | } 52 | return Mono.empty(); 53 | } 54 | }) 55 | .host("127.0.0.1") 56 | .port(1234) 57 | .bind() 58 | .block(); 59 | latch.await(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reactor/netty/User.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reactor.netty; 2 | 3 | /** 4 | * user 5 | * 6 | * @author linux_china 7 | */ 8 | public class User { 9 | private Integer id; 10 | private String nick; 11 | 12 | public User() { 13 | } 14 | 15 | 16 | public User(Integer id, String nick) { 17 | this.id = id; 18 | this.nick = nick; 19 | } 20 | 21 | public Integer getId() { 22 | return id; 23 | } 24 | 25 | public void setId(Integer id) { 26 | this.id = id; 27 | } 28 | 29 | public String getNick() { 30 | return nick; 31 | } 32 | 33 | public void setNick(String nick) { 34 | this.nick = nick; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/reaktive/ReaktiveTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.reaktive 2 | 3 | import com.badoo.reaktive.observable.observableOf 4 | import com.badoo.reaktive.observable.subscribe 5 | import com.badoo.reaktive.single.singleOf 6 | import com.badoo.reaktive.single.subscribe 7 | import com.badoo.reaktive.subject.publish.PublishSubject 8 | import org.junit.jupiter.api.Test 9 | 10 | /** 11 | * reaktive test 12 | * 13 | * @author linux_china 14 | */ 15 | class ReaktiveTest { 16 | 17 | @Test 18 | fun testSpike() { 19 | val first = singleOf("First") 20 | first.subscribe { 21 | println(it) 22 | } 23 | Thread.sleep(1000) 24 | } 25 | 26 | @Test 27 | fun testFlow() { 28 | val flow = observableOf("First", "Second") 29 | flow.subscribe { 30 | println(it) 31 | } 32 | Thread.sleep(1000) 33 | } 34 | 35 | @Test 36 | fun testSubject() { 37 | val subject = PublishSubject() 38 | subject.subscribe { 39 | println(it) 40 | } 41 | subject.onNext("first") 42 | subject.onNext("second") 43 | Thread.sleep(1000) 44 | } 45 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rsocket/requester/RSocketAppRunner1.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rsocket.requester; 2 | 3 | import io.rsocket.Payload; 4 | import io.rsocket.RSocket; 5 | import io.rsocket.util.DefaultPayload; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.ApplicationArguments; 8 | import org.springframework.boot.ApplicationRunner; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * rsocket app runner 13 | * 14 | * @author linux_china 15 | */ 16 | @Component 17 | public class RSocketAppRunner1 implements ApplicationRunner { 18 | @Autowired 19 | private RSocket rSocket; 20 | 21 | @Override 22 | public void run(ApplicationArguments args) throws Exception { 23 | rSocket.requestResponse(DefaultPayload.create("Hello")) 24 | .map(Payload::getDataUtf8) 25 | .onErrorReturn("error") 26 | .subscribe(System.out::println); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rsocket/requester/RSocketLocalTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rsocket.requester; 2 | 3 | import io.rsocket.*; 4 | import io.rsocket.core.RSocketConnector; 5 | import io.rsocket.core.RSocketServer; 6 | import io.rsocket.transport.local.LocalClientTransport; 7 | import io.rsocket.transport.local.LocalServerTransport; 8 | import io.rsocket.util.DefaultPayload; 9 | import org.junit.jupiter.api.AfterAll; 10 | import org.junit.jupiter.api.BeforeAll; 11 | import org.junit.jupiter.api.Test; 12 | import reactor.core.publisher.Mono; 13 | 14 | /** 15 | * RSocket local test 16 | * 17 | * @author linux_china 18 | */ 19 | public class RSocketLocalTest { 20 | private static RSocket rSocket; 21 | private static Closeable localServer; 22 | 23 | @BeforeAll 24 | public static void setUp() { 25 | //create a local test server 26 | localServer = RSocketServer.create() 27 | .acceptor(new SocketAcceptor() { 28 | @Override 29 | public Mono accept(ConnectionSetupPayload setup, RSocket sendingSocket) { 30 | return Mono.just(new RSocket() { 31 | @Override 32 | public Mono requestResponse(Payload payload) { 33 | return Mono.just(payload); 34 | } 35 | }); 36 | } 37 | }) 38 | .bind(LocalServerTransport.create("test-local-server")) 39 | .block(); 40 | //rsocket 41 | rSocket = RSocketConnector.create() 42 | .connect(LocalClientTransport.create("local:test-local-server")) 43 | .onTerminateDetach() 44 | .block(); 45 | } 46 | 47 | @AfterAll 48 | public static void tearDown() { 49 | rSocket.dispose(); 50 | localServer.dispose(); 51 | } 52 | 53 | @Test 54 | public void testRequest() throws Exception { 55 | rSocket.requestResponse(DefaultPayload.create("hello")) 56 | .subscribe(); 57 | Thread.sleep(1000); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rsocket/requester/RSocketRequesterApp.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rsocket.requester; 2 | 3 | import io.rsocket.Payload; 4 | import io.rsocket.RSocket; 5 | import io.rsocket.core.RSocketConnector; 6 | import io.rsocket.transport.netty.client.TcpClientTransport; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.context.annotation.Bean; 11 | import reactor.core.publisher.Mono; 12 | 13 | /** 14 | * RSocket requester 15 | * 16 | * @author linux_china 17 | */ 18 | @SpringBootApplication 19 | public class RSocketRequesterApp { 20 | public static void main(String[] args) throws Exception { 21 | SpringApplication.run(RSocketRequesterApp.class, args); 22 | } 23 | 24 | @Bean(destroyMethod = "dispose") 25 | public RSocket rSocket() { 26 | return RSocketConnector.create() 27 | .metadataMimeType("text/plain") 28 | .dataMimeType("application/json") 29 | .acceptor((setup, sendingSocket) -> { 30 | return Mono.just(new RSocket() { 31 | @NotNull 32 | @Override 33 | public Mono requestResponse(@NotNull Payload payload) { 34 | System.out.println("Received from responder " + payload.getDataUtf8()); 35 | return Mono.just(payload); 36 | } 37 | }); 38 | }) 39 | .connect(TcpClientTransport.create("localhost", 7000)) 40 | .onTerminateDetach() 41 | .block(); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rsocket/responder/RSocketResponderHandler.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rsocket.responder; 2 | 3 | import io.rsocket.ConnectionSetupPayload; 4 | import io.rsocket.Payload; 5 | import io.rsocket.RSocket; 6 | import reactor.core.publisher.Mono; 7 | 8 | /** 9 | * RSocket responder handler 10 | * 11 | * @author linux_china 12 | */ 13 | public class RSocketResponderHandler implements RSocket { 14 | 15 | public RSocketResponderHandler(ConnectionSetupPayload setup, RSocket sendingSocket) { 16 | 17 | } 18 | 19 | @Override 20 | public Mono requestResponse(Payload payload) { 21 | return Mono.just(payload); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rsocket/responder/RsocketAppServer.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rsocket.responder; 2 | 3 | import io.rsocket.core.RSocketServer; 4 | import io.rsocket.transport.netty.server.TcpServerTransport; 5 | import jakarta.annotation.PostConstruct; 6 | import jakarta.annotation.PostConstruct; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import reactor.core.publisher.Mono; 10 | 11 | 12 | /** 13 | * RSocket responder 14 | * 15 | * @author linux_china 16 | */ 17 | @SpringBootApplication 18 | public class RsocketAppServer { 19 | 20 | public static void main(String[] args) throws Exception { 21 | SpringApplication.run(RsocketAppServer.class, args); 22 | } 23 | 24 | @PostConstruct 25 | public void init() { 26 | System.out.println("Starting the RSocket Server"); 27 | RSocketServer.create() 28 | .acceptor((setup, sendingSocket) -> Mono.just(new RSocketResponderHandler(setup, sendingSocket))) 29 | .bind(TcpServerTransport.create("localhost", 42252)) 30 | .subscribe(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rxjava/ObservableTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rxjava; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import rx.Observable; 5 | 6 | /** 7 | * Observable test 8 | * 9 | * @author linux_china 10 | */ 11 | public class ObservableTest { 12 | 13 | @Test 14 | public void testSubscribe() { 15 | Observable.just(1, 2).subscribe(System.out::println); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rxjava/RxJavaTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rxjava; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import rx.Observable; 5 | import rx.Single; 6 | import rx.Subscriber; 7 | import rx.functions.Action1; 8 | import rx.subjects.PublishSubject; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * rxjava 1.x test 14 | * 15 | * @author linux_china 16 | */ 17 | public class RxJavaTest { 18 | 19 | @Test 20 | public void testSingle() { 21 | Single.just("Jacky").subscribe(System.out::println); 22 | } 23 | 24 | @Test 25 | public void testEmit() { 26 | PublishSubject subject = PublishSubject.create(); 27 | subject.subscribe(System.out::println); 28 | subject.onNext("first"); 29 | subject.onNext("second"); 30 | } 31 | 32 | @Test 33 | public void testSpike() { 34 | Observable.just(1, 2, 3) 35 | .map(integer -> 1 + integer) 36 | .subscribe(new Subscriber() { 37 | public void onCompleted() { 38 | System.out.println("Completed"); 39 | } 40 | 41 | public void onError(Throwable throwable) { 42 | System.out.println(throwable.getMessage()); 43 | } 44 | 45 | public void onNext(Integer num) { 46 | System.out.println(num); 47 | } 48 | }); 49 | System.out.println("================="); 50 | Observable.just(1, 2, 3).subscribe(System.out::println); 51 | } 52 | 53 | @Test 54 | public void testInterval() throws Exception { 55 | Observable.interval(1, TimeUnit.SECONDS).subscribe(new Action1() { 56 | @Override 57 | public void call(Long aLong) { 58 | System.out.println(aLong); 59 | } 60 | }); 61 | Thread.sleep(5000); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rxjava/RxKotlinTest.kt: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rxjava 2 | 3 | import io.reactivex.rxkotlin.toObservable 4 | import org.junit.jupiter.api.Test 5 | import rx.subjects.PublishSubject 6 | 7 | /** 8 | * RxKotlin test 9 | * 10 | * @author linux_china 11 | */ 12 | 13 | class RxKotlinTest { 14 | @Test 15 | fun testFirst() { 16 | val list = listOf("Alpha", "Beta", "Gamma", "Delta", "Epsilon") 17 | 18 | list.toObservable() // extension function for Iterables 19 | .filter { it.length >= 5 } 20 | .subscribe { 21 | println(it) 22 | } 23 | 24 | } 25 | 26 | @Test 27 | fun testPublisher() { 28 | val subject = PublishSubject.create() 29 | subject.subscribe { 30 | println("The number is $it") 31 | } 32 | subject.onNext(1) 33 | subject.onNext(2) 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rxjava2/FlowableTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rxjava2; 2 | 3 | import io.reactivex.Flowable; 4 | import org.junit.jupiter.api.Test; 5 | 6 | /** 7 | * flowable test 8 | * 9 | * @author linux_china 10 | */ 11 | public class FlowableTest { 12 | 13 | @Test 14 | public void testFlowable() { 15 | Flowable.just("Hello").subscribe(t -> System.out.println(t)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rxjava2/SingleTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rxjava2; 2 | 3 | import io.reactivex.Maybe; 4 | import io.reactivex.Single; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * Single Test 9 | * 10 | * @author linux_china 11 | */ 12 | @SuppressWarnings("ResultOfMethodCallIgnored") 13 | public class SingleTest { 14 | 15 | @Test 16 | public void testSingle() { 17 | Single.just(1).subscribe(System.out::println); 18 | } 19 | 20 | @Test 21 | public void testMaybe() { 22 | Single single = Maybe.just(null).isEmpty(); 23 | Maybe.just(2).subscribe(System.out::println); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/rxjava3/RxJava3Test.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.rxjava3; 2 | 3 | import io.reactivex.rxjava3.core.Flowable; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.Method; 7 | import java.lang.reflect.Type; 8 | 9 | /** 10 | * RxJava 3 test 11 | * 12 | * @author linux_china 13 | */ 14 | public class RxJava3Test { 15 | 16 | @Test 17 | public void testSpike() { 18 | Flowable.just("Hello world").subscribe(System.out::println); 19 | } 20 | 21 | @Test 22 | public void testConvert() throws Exception { 23 | for (Method method : this.getClass().getDeclaredMethods()) { 24 | System.out.println(method.getReturnType().getCanonicalName()); 25 | Type genericReturnType = method.getGenericReturnType(); 26 | genericReturnType.getTypeName(); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/org/mvnsearch/streamex/StreamExTest.java: -------------------------------------------------------------------------------- 1 | package org.mvnsearch.streamex; 2 | 3 | import one.util.streamex.StreamEx; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class StreamExTest { 7 | 8 | @Test 9 | public void testOperator() { 10 | StreamEx.of("first", "second").map(String::length) 11 | .forEach(System.out::println); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=0 -------------------------------------------------------------------------------- /src/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.testinstance.lifecycle.default = per_class -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------