diff --git a/spring-5-mongodb/.gitignore b/spring-5-mongodb/.gitignore
new file mode 100644
index 0000000000..dec013dfa4
--- /dev/null
+++ b/spring-5-mongodb/.gitignore
@@ -0,0 +1,12 @@
+#folders#
+.idea
+/target
+/neoDb*
+/data
+/src/main/webapp/WEB-INF/classes
+*/META-INF/*
+
+# Packaged files #
+*.jar
+*.war
+*.ear
\ No newline at end of file
diff --git a/spring-5-mongodb/README.md b/spring-5-mongodb/README.md
new file mode 100644
index 0000000000..df96d933ba
--- /dev/null
+++ b/spring-5-mongodb/README.md
@@ -0,0 +1,20 @@
+## Spring REST Example Project
+
+### The Course
+The "REST With Spring" Classes: http://bit.ly/restwithspring
+
+### Relevant Articles
+
+- [Concurrent Test Execution in Spring 5](http://www.baeldung.com/spring-5-concurrent-tests)
+- [Introduction to the Functional Web Framework in Spring 5](http://www.baeldung.com/spring-5-functional-web)
+- [Exploring the Spring 5 MVC URL Matching Improvements](http://www.baeldung.com/spring-5-mvc-url-matching)
+- [Spring 5 WebClient](http://www.baeldung.com/spring-5-webclient)
+- [Spring 5 Functional Bean Registration](http://www.baeldung.com/spring-5-functional-beans)
+- [The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5](http://www.baeldung.com/spring-5-junit-config)
+- [Spring Security 5 for Reactive Applications](http://www.baeldung.com/spring-security-5-reactive)
+- [Spring 5 Testing with @EnabledIf Annotation](https://github.com/eugenp/tutorials/tree/master/spring-5)
+- [Reactive WebSockets with Spring 5](http://www.baeldung.com/spring-5-reactive-websockets)
+- [Spring Boot Actuator](http://www.baeldung.com/spring-boot-actuators)
+- [Spring Webflux Filters](http://www.baeldung.com/spring-webflux-filters)
+- [Reactive Flow with MongoDB, Kotlin, and Spring WebFlux](http://www.baeldung.com/kotlin-mongodb-spring-webflux)
+- [Spring Data Reactive Repositories with MongoDB](http://www.baeldung.com/spring-data-mongodb-reactive)
diff --git a/spring-5-mongodb/pom.xml b/spring-5-mongodb/pom.xml
new file mode 100644
index 0000000000..2db41ba6c7
--- /dev/null
+++ b/spring-5-mongodb/pom.xml
@@ -0,0 +1,157 @@
+
+
+ 4.0.0
+
+ com.baeldung
+ spring-5-mongodb
+ 0.0.1-SNAPSHOT
+ jar
+ spring-5-mongodb
+ spring 5 mongodb sample project
+
+
+ com.baeldung
+ parent-boot-2
+ 0.0.1-SNAPSHOT
+ ../parent-boot-2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.projectreactor
+ reactor-spring
+ ${reactor-spring.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ org.springframework
+ spring-test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.apache.commons
+ commons-collections4
+ ${commons-collections4.version}
+ test
+
+
+ org.projectlombok
+ lombok
+ compile
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-surefire-provider
+ ${junit.platform.version}
+ test
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-mongodb-reactive
+
+
+ de.flapdoodle.embed
+ de.flapdoodle.embed.mongo
+ test
+
+
+ io.reactivex.rxjava2
+ rxjava
+ ${rxjava-version}
+
+
+ io.projectreactor
+ reactor-test
+ ${project-reactor-test}
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.baeldung.reactive.Spring5ReactiveApplication
+ JAR
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ 3
+ true
+ methods
+ true
+
+ **/*IntegrationTest.java
+ **/*IntTest.java
+ **/*LiveTest.java
+
+
+
+
+
+
+
+ 1.0.0
+ 5.0.2
+ 2.20
+ 1.0.1.RELEASE
+ 2.1.12
+ 4.1
+ 3.1.6.RELEASE
+ 1.1.3
+ 1.0
+ 1.0
+
+
+
diff --git a/spring-5-mongodb/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java b/spring-5-mongodb/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java
new file mode 100644
index 0000000000..e96767145e
--- /dev/null
+++ b/spring-5-mongodb/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java
@@ -0,0 +1,25 @@
+package com.baeldung.reactive;
+
+import com.mongodb.reactivestreams.client.MongoClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+
+@SpringBootApplication
+public class Spring5ReactiveApplication{
+
+ public static void main(String[] args) {
+ SpringApplication.run(Spring5ReactiveApplication.class, args);
+ }
+
+ @Autowired
+ MongoClient mongoClient;
+
+ @Bean
+ public ReactiveMongoTemplate reactiveMongoTemplate() {
+ return new ReactiveMongoTemplate(mongoClient, "test");
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/model/Account.java b/spring-5-mongodb/src/main/java/com/baeldung/reactive/model/Account.java
similarity index 100%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/model/Account.java
rename to spring-5-mongodb/src/main/java/com/baeldung/reactive/model/Account.java
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java b/spring-5-mongodb/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java
similarity index 100%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java
rename to spring-5-mongodb/src/main/java/com/baeldung/reactive/repository/AccountCrudRepository.java
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java b/spring-5-mongodb/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java
similarity index 100%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java
rename to spring-5-mongodb/src/main/java/com/baeldung/reactive/repository/AccountMongoRepository.java
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java b/spring-5-mongodb/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java
similarity index 100%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java
rename to spring-5-mongodb/src/main/java/com/baeldung/reactive/repository/AccountRxJavaRepository.java
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java b/spring-5-mongodb/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java
similarity index 100%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java
rename to spring-5-mongodb/src/main/java/com/baeldung/reactive/template/AccountTemplateOperations.java
diff --git a/spring-5-mongodb/src/main/resources/application.properties b/spring-5-mongodb/src/main/resources/application.properties
new file mode 100644
index 0000000000..234834b894
--- /dev/null
+++ b/spring-5-mongodb/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+logging.level.root=INFO
+
+management.endpoints.web.exposure.include.=*
+
+info.app.name=Spring Boot 2 actuator Application
diff --git a/spring-5-mongodb/src/main/webapp/WEB-INF/web.xml b/spring-5-mongodb/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..bfcf43dad2
--- /dev/null
+++ b/spring-5-mongodb/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+
+
+
+ Spring Functional Application
+
+
+ functional
+ com.baeldung.functional.RootServlet
+ 1
+ true
+
+
+ functional
+ /
+
+
+
+
\ No newline at end of file
diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryIntegrationTest.java b/spring-5-mongodb/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryIntegrationTest.java
similarity index 100%
rename from spring-5-reactive/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryIntegrationTest.java
rename to spring-5-mongodb/src/test/java/com/baeldung/reactive/repository/AccountCrudRepositoryIntegrationTest.java
diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryIntegrationTest.java b/spring-5-mongodb/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryIntegrationTest.java
similarity index 100%
rename from spring-5-reactive/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryIntegrationTest.java
rename to spring-5-mongodb/src/test/java/com/baeldung/reactive/repository/AccountMongoRepositoryIntegrationTest.java
diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryIntegrationTest.java b/spring-5-mongodb/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryIntegrationTest.java
similarity index 100%
rename from spring-5-reactive/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryIntegrationTest.java
rename to spring-5-mongodb/src/test/java/com/baeldung/reactive/repository/AccountRxJavaRepositoryIntegrationTest.java
diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsIntegrationTest.java b/spring-5-mongodb/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsIntegrationTest.java
similarity index 100%
rename from spring-5-reactive/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsIntegrationTest.java
rename to spring-5-mongodb/src/test/java/com/baeldung/reactive/template/AccountTemplateOperationsIntegrationTest.java
diff --git a/spring-5-reactive/pom.xml b/spring-5-reactive/pom.xml
index 33fcad4e1d..acc82be0d1 100644
--- a/spring-5-reactive/pom.xml
+++ b/spring-5-reactive/pom.xml
@@ -127,15 +127,6 @@
commons-lang3
-
- org.springframework.boot
- spring-boot-starter-data-mongodb-reactive
-
-
- de.flapdoodle.embed
- de.flapdoodle.embed.mongo
- test
-
io.reactivex.rxjava2
rxjava
diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/Actor.java b/spring-5-reactive/src/main/java/com/baeldung/functional/Actor.java
new file mode 100644
index 0000000000..23c88b89e1
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/functional/Actor.java
@@ -0,0 +1,23 @@
+package com.baeldung.functional;
+
+class Actor {
+ private String firstname;
+ private String lastname;
+
+ public Actor() {
+ }
+
+ public Actor(String firstname, String lastname) {
+ this.firstname = firstname;
+ this.lastname = lastname;
+ }
+
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public String getLastname() {
+ return lastname;
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/FormHandler.java b/spring-5-reactive/src/main/java/com/baeldung/functional/FormHandler.java
new file mode 100644
index 0000000000..05069735bb
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/functional/FormHandler.java
@@ -0,0 +1,41 @@
+package com.baeldung.functional;
+
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
+import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
+import static org.springframework.web.reactive.function.BodyInserters.fromObject;
+import static org.springframework.web.reactive.function.server.ServerResponse.ok;
+
+public class FormHandler {
+
+ Mono handleLogin(ServerRequest request) {
+ return request.body(toFormData())
+ .map(MultiValueMap::toSingleValueMap)
+ .filter(formData -> "baeldung".equals(formData.get("user")))
+ .filter(formData -> "you_know_what_to_do".equals(formData.get("token")))
+ .flatMap(formData -> ok().body(Mono.just("welcome back!"), String.class))
+ .switchIfEmpty(ServerResponse.badRequest()
+ .build());
+ }
+
+ Mono handleUpload(ServerRequest request) {
+ return request.body(toDataBuffers())
+ .collectList()
+ .flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString())));
+ }
+
+ private AtomicLong extractData(List dataBuffers) {
+ AtomicLong atomicLong = new AtomicLong(0);
+ dataBuffers.forEach(d -> atomicLong.addAndGet(d.asByteBuffer()
+ .array().length));
+ return atomicLong;
+ }
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java
new file mode 100644
index 0000000000..402b607b19
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalSpringBootApplication.java
@@ -0,0 +1,87 @@
+package com.baeldung.functional;
+
+import static org.springframework.web.reactive.function.BodyInserters.fromObject;
+import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
+import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
+import static org.springframework.web.reactive.function.server.RequestPredicates.path;
+import static org.springframework.web.reactive.function.server.RouterFunctions.route;
+import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
+import static org.springframework.web.reactive.function.server.ServerResponse.ok;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.server.reactive.HttpHandler;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import org.springframework.web.server.WebHandler;
+import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
+
+import reactor.core.publisher.Flux;
+
+@SpringBootApplication
+@ComponentScan(basePackages = { "com.baeldung.functional" })
+public class FunctionalSpringBootApplication {
+
+ private static final Actor BRAD_PITT = new Actor("Brad", "Pitt");
+ private static final Actor TOM_HANKS = new Actor("Tom", "Hanks");
+ private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
+
+ private RouterFunction routingFunction() {
+ FormHandler formHandler = new FormHandler();
+
+ RouterFunction restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest.bodyToMono(Actor.class)
+ .doOnNext(actors::add)
+ .then(ok().build()));
+
+ return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
+ .andRoute(POST("/upload"), formHandler::handleUpload)
+ .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
+ .andNest(path("/actor"), restfulRouter)
+ .filter((request, next) -> {
+ System.out.println("Before handler invocation: " + request.path());
+ return next.handle(request);
+ });
+ }
+
+ @Bean
+ public ServletRegistrationBean servletRegistrationBean() throws Exception {
+ HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler((WebHandler) toHttpHandler(routingFunction()))
+ .filter(new IndexRewriteFilter())
+ .build();
+ ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(new RootServlet(httpHandler), "/");
+ registrationBean.setLoadOnStartup(1);
+ registrationBean.setAsyncSupported(true);
+ return registrationBean;
+ }
+
+ @Configuration
+ @EnableWebSecurity
+ @Profile("!https")
+ static class SecurityConfig extends WebSecurityConfigurerAdapter {
+ @Override
+ protected void configure(final HttpSecurity http) throws Exception {
+ http.authorizeRequests()
+ .anyRequest()
+ .permitAll();
+ }
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(FunctionalSpringBootApplication.class, args);
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalWebApplication.java b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalWebApplication.java
new file mode 100644
index 0000000000..5a7d70d3db
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/functional/FunctionalWebApplication.java
@@ -0,0 +1,80 @@
+package com.baeldung.functional;
+
+import static org.springframework.web.reactive.function.BodyInserters.fromObject;
+import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
+import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
+import static org.springframework.web.reactive.function.server.RequestPredicates.path;
+import static org.springframework.web.reactive.function.server.RouterFunctions.route;
+import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
+import static org.springframework.web.reactive.function.server.ServerResponse.ok;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
+import org.springframework.boot.web.server.WebServer;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.server.reactive.HttpHandler;
+import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import org.springframework.web.server.WebHandler;
+import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
+
+import reactor.core.publisher.Flux;
+
+public class FunctionalWebApplication {
+
+ private static final Actor BRAD_PITT = new Actor("Brad", "Pitt");
+ private static final Actor TOM_HANKS = new Actor("Tom", "Hanks");
+ private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
+
+ private RouterFunction routingFunction() {
+ FormHandler formHandler = new FormHandler();
+
+ RouterFunction restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest.bodyToMono(Actor.class)
+ .doOnNext(actors::add)
+ .then(ok().build()));
+
+ return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
+ .andRoute(POST("/upload"), formHandler::handleUpload)
+ .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
+ .andNest(path("/actor"), restfulRouter)
+ .filter((request, next) -> {
+ System.out.println("Before handler invocation: " + request.path());
+ return next.handle(request);
+ });
+ }
+
+ WebServer start() throws Exception {
+ WebHandler webHandler = (WebHandler) toHttpHandler(routingFunction());
+ HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(webHandler)
+ .filter(new IndexRewriteFilter())
+ .build();
+
+ Tomcat tomcat = new Tomcat();
+ tomcat.setHostname("localhost");
+ tomcat.setPort(9090);
+ Context rootContext = tomcat.addContext("", System.getProperty("java.io.tmpdir"));
+ ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler);
+ Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
+ rootContext.addServletMappingDecoded("/", "httpHandlerServlet");
+
+ TomcatWebServer server = new TomcatWebServer(tomcat);
+ server.start();
+ return server;
+
+ }
+
+ public static void main(String[] args) {
+ try {
+ new FunctionalWebApplication().start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/IndexRewriteFilter.java b/spring-5-reactive/src/main/java/com/baeldung/functional/IndexRewriteFilter.java
new file mode 100644
index 0000000000..551ea6c84b
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/functional/IndexRewriteFilter.java
@@ -0,0 +1,25 @@
+package com.baeldung.functional;
+
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+class IndexRewriteFilter implements WebFilter {
+
+ @Override
+ public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
+ ServerHttpRequest request = serverWebExchange.getRequest();
+ if (request.getURI()
+ .getPath()
+ .equals("/")) {
+ return webFilterChain.filter(serverWebExchange.mutate()
+ .request(builder -> builder.method(request.getMethod())
+ .path("/test"))
+ .build());
+ }
+ return webFilterChain.filter(serverWebExchange);
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/MyService.java b/spring-5-reactive/src/main/java/com/baeldung/functional/MyService.java
new file mode 100644
index 0000000000..b7b8b13d8b
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/functional/MyService.java
@@ -0,0 +1,11 @@
+package com.baeldung.functional;
+
+import java.util.Random;
+
+public class MyService {
+
+ public int getRandomNumber() {
+ return (new Random().nextInt(10));
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/functional/RootServlet.java b/spring-5-reactive/src/main/java/com/baeldung/functional/RootServlet.java
new file mode 100644
index 0000000000..8fe24821de
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/functional/RootServlet.java
@@ -0,0 +1,82 @@
+package com.baeldung.functional;
+
+import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
+import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
+import static org.springframework.web.reactive.function.BodyInserters.fromObject;
+import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
+import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
+import static org.springframework.web.reactive.function.server.RequestPredicates.path;
+import static org.springframework.web.reactive.function.server.RouterFunctions.route;
+import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
+import static org.springframework.web.reactive.function.server.ServerResponse.ok;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.server.reactive.HttpHandler;
+import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import org.springframework.web.server.WebHandler;
+import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class RootServlet extends ServletHttpHandlerAdapter {
+
+ public RootServlet() {
+ this(WebHttpHandlerBuilder.webHandler((WebHandler) toHttpHandler(routingFunction()))
+ .filter(new IndexRewriteFilter())
+ .build());
+ }
+
+ RootServlet(HttpHandler httpHandler) {
+ super(httpHandler);
+ }
+
+ private static final Actor BRAD_PITT = new Actor("Brad", "Pitt");
+ private static final Actor TOM_HANKS = new Actor("Tom", "Hanks");
+ private static final List actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
+
+ private static RouterFunction> routingFunction() {
+
+ return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), serverRequest -> serverRequest.body(toFormData())
+ .map(MultiValueMap::toSingleValueMap)
+ .map(formData -> {
+ System.out.println("form data: " + formData.toString());
+ if ("baeldung".equals(formData.get("user")) && "you_know_what_to_do".equals(formData.get("token"))) {
+ return ok().body(Mono.just("welcome back!"), String.class)
+ .block();
+ }
+ return ServerResponse.badRequest()
+ .build()
+ .block();
+ }))
+ .andRoute(POST("/upload"), serverRequest -> serverRequest.body(toDataBuffers())
+ .collectList()
+ .map(dataBuffers -> {
+ AtomicLong atomicLong = new AtomicLong(0);
+ dataBuffers.forEach(d -> atomicLong.addAndGet(d.asByteBuffer()
+ .array().length));
+ System.out.println("data length:" + atomicLong.get());
+ return ok().body(fromObject(atomicLong.toString()))
+ .block();
+ }))
+ .and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
+ .andNest(path("/actor"), route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest.bodyToMono(Actor.class)
+ .doOnNext(actors::add)
+ .then(ok().build())))
+ .filter((request, next) -> {
+ System.out.println("Before handler invocation: " + request.path());
+ return next.handle(request);
+ });
+
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java
index e96767145e..1656f70221 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/Spring5ReactiveApplication.java
@@ -1,11 +1,9 @@
package com.baeldung.reactive;
-import com.mongodb.reactivestreams.client.MongoClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
-import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
@SpringBootApplication
public class Spring5ReactiveApplication{
@@ -14,12 +12,4 @@ public class Spring5ReactiveApplication{
SpringApplication.run(Spring5ReactiveApplication.class, args);
}
- @Autowired
- MongoClient mongoClient;
-
- @Bean
- public ReactiveMongoTemplate reactiveMongoTemplate() {
- return new ReactiveMongoTemplate(mongoClient, "test");
- }
-
}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/SpringSecurity5Application.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/SpringSecurity5Application.java
new file mode 100644
index 0000000000..0e695ff596
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/SpringSecurity5Application.java
@@ -0,0 +1,34 @@
+package com.baeldung.reactive;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.http.server.reactive.HttpHandler;
+import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
+import org.springframework.web.reactive.config.EnableWebFlux;
+import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
+import reactor.ipc.netty.NettyContext;
+import reactor.ipc.netty.http.server.HttpServer;
+
+@ComponentScan(basePackages = {"com.baeldung.security"})
+@EnableWebFlux
+public class SpringSecurity5Application {
+
+ public static void main(String[] args) {
+ try (AnnotationConfigApplicationContext context =
+ new AnnotationConfigApplicationContext(SpringSecurity5Application.class)) {
+ context.getBean(NettyContext.class).onClose().block();
+ }
+ }
+
+ @Bean
+ public NettyContext nettyContext(ApplicationContext context) {
+ HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context)
+ .build();
+ ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
+ HttpServer httpServer = HttpServer.create("localhost", 8080);
+ return httpServer.newHandler(adapter).block();
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/WebSecurityConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/WebSecurityConfig.java
new file mode 100644
index 0000000000..427fd70a6c
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/WebSecurityConfig.java
@@ -0,0 +1,28 @@
+package com.baeldung.reactive.actuator;
+
+import org.springframework.boot.actuate.autoconfigure.security.reactive.EndpointRequest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+
+@Configuration
+@EnableWebFluxSecurity
+public class WebSecurityConfig {
+
+
+ @Bean
+ public SecurityWebFilterChain securitygWebFilterChain(
+ ServerHttpSecurity http) {
+ return http
+
+ .authorizeExchange()
+ .matchers(EndpointRequest.to(
+ FeaturesEndpoint.class
+ )).permitAll().and().csrf().disable().build();
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/annotated/CorsOnAnnotatedElementsApplication.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/annotated/CorsOnAnnotatedElementsApplication.java
index 87efe72a1b..d990928abe 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/annotated/CorsOnAnnotatedElementsApplication.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/annotated/CorsOnAnnotatedElementsApplication.java
@@ -4,16 +4,8 @@ import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
-import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration;
-import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
-import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
-@SpringBootApplication(exclude = { MongoAutoConfiguration.class,
- MongoDataAutoConfiguration.class,
- MongoReactiveDataAutoConfiguration.class,
- MongoReactiveAutoConfiguration.class }
-)
+@SpringBootApplication
public class CorsOnAnnotatedElementsApplication {
public static void main(String[] args) {
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/FurtherCorsConfigsController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/FurtherCorsConfigsController.java
index 4f9b9bd037..bc5b483935 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/FurtherCorsConfigsController.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/FurtherCorsConfigsController.java
@@ -8,8 +8,8 @@ import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
-@RestController
-@RequestMapping("/web-filter-and-more-on-annotated")
+//@RestController
+//@RequestMapping("/web-filter-and-more-on-annotated")
public class FurtherCorsConfigsController {
@DeleteMapping("/further-mixed-config-endpoint")
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/RegularRestController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/RegularRestController.java
index 6985810aa5..1d382b222a 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/RegularRestController.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/cors/webfilter/controllers/RegularRestController.java
@@ -7,8 +7,8 @@ import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
-@RestController
-@RequestMapping("/web-filter-on-annotated")
+//@RestController
+//@RequestMapping("/web-filter-on-annotated")
public class RegularRestController {
@PutMapping("/regular-put-endpoint")
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/security/GreetController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/security/GreetController.java
new file mode 100644
index 0000000000..99b79d88ea
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/security/GreetController.java
@@ -0,0 +1,37 @@
+package com.baeldung.reactive.security;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+import java.security.Principal;
+
+@RestController
+public class GreetController {
+
+ private GreetService greetService;
+
+ public GreetController(GreetService greetService) {
+ this.greetService = greetService;
+ }
+
+ @GetMapping("/")
+ public Mono greet(Mono principal) {
+ return principal
+ .map(Principal::getName)
+ .map(name -> String.format("Hello, %s", name));
+ }
+
+ @GetMapping("/admin")
+ public Mono greetAdmin(Mono principal) {
+ return principal
+ .map(Principal::getName)
+ .map(name -> String.format("Admin access: %s", name));
+ }
+
+ @GetMapping("/greetService")
+ public Mono greetService() {
+ return greetService.greet();
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/security/GreetService.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/security/GreetService.java
new file mode 100644
index 0000000000..93df64bced
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/security/GreetService.java
@@ -0,0 +1,15 @@
+package com.baeldung.reactive.security;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+@Service
+public class GreetService {
+
+ @PreAuthorize("hasRole('ADMIN')")
+ public Mono greet() {
+ return Mono.just("Hello from service!");
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/security/SecurityConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/security/SecurityConfig.java
new file mode 100644
index 0000000000..cb1e7d1312
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/security/SecurityConfig.java
@@ -0,0 +1,42 @@
+package com.baeldung.reactive.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+
+@EnableWebFluxSecurity
+@EnableReactiveMethodSecurity
+public class SecurityConfig {
+
+ @Bean
+ public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
+ return http.authorizeExchange()
+ .pathMatchers("/admin").hasAuthority("ROLE_ADMIN")
+ .anyExchange().permitAll()
+ .and().formLogin()
+ .and().build();
+ }
+
+ @Bean
+ public MapReactiveUserDetailsService userDetailsService() {
+ UserDetails user = User.withDefaultPasswordEncoder()
+ .username("user")
+ .password("password")
+ .roles("USER")
+ .build();
+
+ UserDetails admin = User.withDefaultPasswordEncoder()
+ .username("admin")
+ .password("password")
+ .roles("ADMIN")
+ .build();
+
+ return new MapReactiveUserDetailsService(user, admin);
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/Task.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/Task.java
new file mode 100644
index 0000000000..725fd931e1
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/Task.java
@@ -0,0 +1,28 @@
+package com.baeldung.web.reactive;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Task {
+
+ private final String name;
+
+ private final int id;
+
+ public Task(@JsonProperty("name") String name, @JsonProperty("id") int id) {
+ this.name = name;
+ this.id = id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public String toString() {
+ return "Task{" + "name='" + name + '\'' + ", id=" + id + '}';
+ }
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java
new file mode 100644
index 0000000000..a719259328
--- /dev/null
+++ b/spring-5-reactive/src/main/java/com/baeldung/web/reactive/client/WebClientController.java
@@ -0,0 +1,83 @@
+package com.baeldung.web.reactive.client;
+
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.springframework.http.*;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.BodyInserter;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.time.ZonedDateTime;
+import java.util.Collections;
+
+@RestController
+public class WebClientController {
+
+ @ResponseStatus(HttpStatus.OK)
+ @GetMapping("/resource")
+ public void getResource() {
+ }
+
+ public void demonstrateWebClient() {
+ // request
+ WebClient.UriSpec request1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST);
+ WebClient.UriSpec request2 = createWebClientWithServerURLAndDefaultValues().post();
+
+ // request body specifications
+ WebClient.RequestBodySpec uri1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST)
+ .uri("/resource");
+ WebClient.RequestBodySpec uri2 = createWebClientWithServerURLAndDefaultValues().post()
+ .uri(URI.create("/resource"));
+
+ // request header specification
+ WebClient.RequestHeadersSpec> requestSpec1 = uri1.body(BodyInserters.fromPublisher(Mono.just("data"), String.class));
+ WebClient.RequestHeadersSpec> requestSpec2 = uri2.body(BodyInserters.fromObject("data"));
+
+ // inserters
+ BodyInserter, ReactiveHttpOutputMessage> inserter1 = BodyInserters
+ .fromPublisher(Subscriber::onComplete, String.class);
+
+ LinkedMultiValueMap map = new LinkedMultiValueMap<>();
+ map.add("key1", "value1");
+ map.add("key2", "value2");
+
+ // BodyInserter, ClientHttpRequest> inserter2 = BodyInserters.fromMultipartData(map);
+ BodyInserter inserter3 = BodyInserters.fromObject("body");
+
+ // responses
+ WebClient.ResponseSpec response1 = uri1.body(inserter3)
+ .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
+ .acceptCharset(Charset.forName("UTF-8"))
+ .ifNoneMatch("*")
+ .ifModifiedSince(ZonedDateTime.now())
+ .retrieve();
+ WebClient.ResponseSpec response2 = requestSpec2.retrieve();
+
+ }
+
+ private WebClient createWebClient() {
+ return WebClient.create();
+ }
+
+ private WebClient createWebClientWithServerURL() {
+ return WebClient.create("http://localhost:8081");
+ }
+
+ private WebClient createWebClientWithServerURLAndDefaultValues() {
+ return WebClient.builder()
+ .baseUrl("http://localhost:8081")
+ .defaultCookie("cookieKey", "cookieValue")
+ .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
+ .build();
+ }
+
+}
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/Event.java b/spring-5-reactive/src/main/java/com/baeldung/websocket/Event.java
similarity index 79%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/Event.java
rename to spring-5-reactive/src/main/java/com/baeldung/websocket/Event.java
index 90f83a566f..5d0260928c 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/Event.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/websocket/Event.java
@@ -1,4 +1,4 @@
-package com.baeldung.reactive.websocket;
+package com.baeldung.websocket;
import lombok.AllArgsConstructor;
import lombok.Data;
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveJavaClientWebSocket.java
similarity index 95%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java
rename to spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveJavaClientWebSocket.java
index c9a333c044..efb4dc238a 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveJavaClientWebSocket.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveJavaClientWebSocket.java
@@ -1,4 +1,4 @@
-package com.baeldung.reactive.websocket;
+package com.baeldung.websocket;
import java.net.URI;
import java.time.Duration;
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketApplication.java b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketApplication.java
similarity index 88%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketApplication.java
rename to spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketApplication.java
index 43b5e50387..0e71673df6 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketApplication.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketApplication.java
@@ -1,4 +1,4 @@
-package com.baeldung.reactive.websocket;
+package com.baeldung.websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketConfiguration.java b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketConfiguration.java
similarity index 96%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketConfiguration.java
rename to spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketConfiguration.java
index 974def5a91..ef8d81d3c2 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketConfiguration.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketConfiguration.java
@@ -1,4 +1,4 @@
-package com.baeldung.reactive.websocket;
+package com.baeldung.websocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketHandler.java
similarity index 97%
rename from spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java
rename to spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketHandler.java
index 2e93c0c0dc..5adad6bf15 100644
--- a/spring-5-reactive/src/main/java/com/baeldung/reactive/websocket/ReactiveWebSocketHandler.java
+++ b/spring-5-reactive/src/main/java/com/baeldung/websocket/ReactiveWebSocketHandler.java
@@ -1,4 +1,4 @@
-package com.baeldung.reactive.websocket;
+package com.baeldung.websocket;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/spring-5-reactive/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java
new file mode 100644
index 0000000000..a7b951b930
--- /dev/null
+++ b/spring-5-reactive/src/test/java/com/baeldung/functional/FunctionalWebApplicationIntegrationTest.java
@@ -0,0 +1,141 @@
+package com.baeldung.functional;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.boot.web.server.WebServer;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.BodyInserters;
+
+import static org.springframework.web.reactive.function.BodyInserters.fromObject;
+import static org.springframework.web.reactive.function.BodyInserters.fromResource;
+
+public class FunctionalWebApplicationIntegrationTest {
+
+ private static WebTestClient client;
+ private static WebServer server;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ server = new FunctionalWebApplication().start();
+ client = WebTestClient.bindToServer()
+ .baseUrl("http://localhost:" + server.getPort())
+ .build();
+ }
+
+ @AfterClass
+ public static void destroy() {
+ server.stop();
+ }
+
+ @Test
+ public void givenRouter_whenGetTest_thenGotHelloWorld() throws Exception {
+ client.get()
+ .uri("/test")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .isEqualTo("helloworld");
+ }
+
+ @Test
+ public void givenIndexFilter_whenRequestRoot_thenRewrittenToTest() throws Exception {
+ client.get()
+ .uri("/")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .isEqualTo("helloworld");
+ }
+
+ @Test
+ public void givenLoginForm_whenPostValidToken_thenSuccess() throws Exception {
+ MultiValueMap formData = new LinkedMultiValueMap<>(1);
+ formData.add("user", "baeldung");
+ formData.add("token", "you_know_what_to_do");
+
+ client.post()
+ .uri("/login")
+ .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+ .body(BodyInserters.fromFormData(formData))
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .isEqualTo("welcome back!");
+ }
+
+ @Test
+ public void givenLoginForm_whenRequestWithInvalidToken_thenFail() throws Exception {
+ MultiValueMap formData = new LinkedMultiValueMap<>(2);
+ formData.add("user", "baeldung");
+ formData.add("token", "try_again");
+
+ client.post()
+ .uri("/login")
+ .contentType(MediaType.APPLICATION_FORM_URLENCODED)
+ .body(BodyInserters.fromFormData(formData))
+ .exchange()
+ .expectStatus()
+ .isBadRequest();
+ }
+
+ @Test
+ public void givenUploadForm_whenRequestWithMultipartData_thenSuccess() throws Exception {
+ Resource resource = new ClassPathResource("/baeldung-weekly.png");
+ client.post()
+ .uri("/upload")
+ .contentType(MediaType.MULTIPART_FORM_DATA)
+ .body(fromResource(resource))
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .isEqualTo(String.valueOf(resource.contentLength()));
+ }
+
+ @Test
+ public void givenActors_whenAddActor_thenAdded() throws Exception {
+ client.get()
+ .uri("/actor")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBodyList(Actor.class)
+ .hasSize(2);
+
+ client.post()
+ .uri("/actor")
+ .body(fromObject(new Actor("Clint", "Eastwood")))
+ .exchange()
+ .expectStatus()
+ .isOk();
+
+ client.get()
+ .uri("/actor")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBodyList(Actor.class)
+ .hasSize(3);
+ }
+
+ @Test
+ public void givenResources_whenAccess_thenGot() throws Exception {
+ client.get()
+ .uri("/files/hello.txt")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .isEqualTo("hello");
+ }
+
+}
diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/Spring5ReactiveServerClientIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/Spring5ReactiveServerClientIntegrationTest.java
new file mode 100644
index 0000000000..8707c27fb3
--- /dev/null
+++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/Spring5ReactiveServerClientIntegrationTest.java
@@ -0,0 +1,96 @@
+package com.baeldung.reactive;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.springframework.http.server.reactive.HttpHandler;
+import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import org.springframework.web.reactive.function.server.ServerResponse;
+
+import com.baeldung.web.reactive.Task;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.ipc.netty.NettyContext;
+import reactor.ipc.netty.http.server.HttpServer;
+
+import java.time.Duration;
+
+import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
+import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
+
+public class Spring5ReactiveServerClientIntegrationTest {
+
+ private static NettyContext nettyContext;
+
+ @BeforeAll
+ public static void setUp() throws Exception {
+ HttpServer server = HttpServer.create("localhost", 8080);
+ RouterFunction> route = RouterFunctions.route(POST("/task/process"), request -> ServerResponse.ok()
+ .body(request.bodyToFlux(Task.class)
+ .map(ll -> new Task("TaskName", 1)), Task.class))
+ .and(RouterFunctions.route(GET("/task"), request -> ServerResponse.ok()
+ .body(Mono.just("server is alive"), String.class)));
+ HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
+ ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
+ nettyContext = server.newHandler(adapter)
+ .block();
+ }
+
+ @AfterAll
+ public static void shutDown() {
+ nettyContext.dispose();
+ }
+
+ // @Test
+ // public void givenCheckTask_whenServerHandle_thenServerResponseALiveString() throws Exception {
+ // WebClient client = WebClient.create("http://localhost:8080");
+ // Mono result = client
+ // .get()
+ // .uri("/task")
+ // .exchange()
+ // .then(response -> response.bodyToMono(String.class));
+ //
+ // assertThat(result.block()).isInstanceOf(String.class);
+ // }
+
+ // @Test
+ // public void givenThreeTasks_whenServerHandleTheTasks_thenServerResponseATask() throws Exception {
+ // URI uri = URI.create("http://localhost:8080/task/process");
+ // ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());
+ // ClientRequest request = ClientRequest
+ // .method(HttpMethod.POST, uri)
+ // .body(BodyInserters.fromPublisher(getLatLngs(), Task.class))
+ // .build();
+ //
+ // Flux taskResponse = exchange
+ // .exchange(request)
+ // .flatMap(response -> response.bodyToFlux(Task.class));
+ //
+ // assertThat(taskResponse.blockFirst()).isInstanceOf(Task.class);
+ // }
+
+ // @Test
+ // public void givenCheckTask_whenServerHandle_thenOragicServerResponseALiveString() throws Exception {
+ // URI uri = URI.create("http://localhost:8080/task");
+ // ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());
+ // ClientRequest request = ClientRequest
+ // .method(HttpMethod.GET, uri)
+ // .body(BodyInserters.fromPublisher(getLatLngs(), Task.class))
+ // .build();
+ //
+ // Flux taskResponse = exchange
+ // .exchange(request)
+ // .flatMap(response -> response.bodyToFlux(String.class));
+ //
+ // assertThat(taskResponse.blockFirst()).isInstanceOf(String.class);
+ // }
+
+ private static Flux getLatLngs() {
+ return Flux.range(0, 3)
+ .zipWith(Flux.interval(Duration.ofSeconds(1)))
+ .map(x -> new Task("taskname", 1))
+ .doOnNext(ll -> System.out.println("Produced: {}" + ll));
+ }
+}
diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerIntegrationTest.java
similarity index 90%
rename from spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java
rename to spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerIntegrationTest.java
index 640f28c331..e8c8c25723 100644
--- a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java
+++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerIntegrationTest.java
@@ -15,13 +15,15 @@ import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
+import com.baeldung.reactive.Spring5ReactiveApplication;
+
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RunWith(SpringRunner.class)
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes=Spring5ReactiveApplication.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class EmployeeControllerUnitTest {
+public class EmployeeControllerIntegrationTest {
@Autowired
private WebTestClient testClient;
diff --git a/spring-5-reactive/src/test/java/com/baeldung/security/SecurityIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/security/SecurityIntegrationTest.java
new file mode 100644
index 0000000000..a59ef57db8
--- /dev/null
+++ b/spring-5-reactive/src/test/java/com/baeldung/security/SecurityIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.security;
+
+import com.baeldung.reactive.SpringSecurity5Application;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+@RunWith(SpringRunner.class)
+@ContextConfiguration(classes = SpringSecurity5Application.class)
+public class SecurityIntegrationTest {
+
+ @Autowired
+ ApplicationContext context;
+
+ private WebTestClient rest;
+
+ @Before
+ public void setup() {
+ this.rest = WebTestClient.bindToApplicationContext(this.context).configureClient().build();
+ }
+
+ @Test
+ public void whenNoCredentials_thenRedirectToLogin() {
+ this.rest.get().uri("/").exchange().expectStatus().is3xxRedirection();
+ }
+
+ @Test
+ @Ignore
+ @WithMockUser
+ public void whenHasCredentials_thenSeesGreeting() {
+ this.rest.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello, user");
+ }
+}
diff --git a/spring-5-reactive/src/test/java/com/baeldung/web/client/WebTestClientIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/web/client/WebTestClientIntegrationTest.java
new file mode 100644
index 0000000000..a1c0eecb8f
--- /dev/null
+++ b/spring-5-reactive/src/test/java/com/baeldung/web/client/WebTestClientIntegrationTest.java
@@ -0,0 +1,60 @@
+package com.baeldung.web.client;
+
+import com.baeldung.reactive.Spring5ReactiveApplication;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.web.reactive.function.server.RequestPredicates;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import org.springframework.web.server.WebHandler;
+import reactor.core.publisher.Mono;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Spring5ReactiveApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class WebTestClientIntegrationTest {
+
+ @LocalServerPort
+ private int port;
+
+ private final RouterFunction ROUTER_FUNCTION = RouterFunctions.route(RequestPredicates.GET("/resource"), request -> ServerResponse.ok()
+ .build());
+ private final WebHandler WEB_HANDLER = exchange -> Mono.empty();
+
+ @Test
+ public void testWebTestClientWithServerWebHandler() {
+ WebTestClient.bindToWebHandler(WEB_HANDLER)
+ .build();
+ }
+
+ @Test
+ public void testWebTestClientWithRouterFunction() {
+ WebTestClient.bindToRouterFunction(ROUTER_FUNCTION)
+ .build()
+ .get()
+ .uri("/resource")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody()
+ .isEmpty();
+ }
+
+ @Test
+ public void testWebTestClientWithServerURL() {
+ WebTestClient.bindToServer()
+ .baseUrl("http://localhost:" + port)
+ .build()
+ .get()
+ .uri("/resource")
+ .exchange()
+ .expectStatus()
+ .is3xxRedirection()
+ .expectBody();
+ }
+
+}