Merge pull request #8125 from eugenp/revert-8119-BAEL-3275-2

Revert "BAEL-3275: Using blocking queue for pub-sub"
This commit is contained in:
Eric Martin
2019-10-31 20:43:47 -05:00
committed by GitHub
parent db85c8f275
commit 3225470df5
20543 changed files with 1642750 additions and 0 deletions
@@ -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;
}
}
@@ -0,0 +1,40 @@
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<ServerResponse> 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<ServerResponse> handleUpload(ServerRequest request) {
return request.body(toDataBuffers())
.collectList()
.flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString())));
}
private AtomicLong extractData(List<DataBuffer> dataBuffers) {
AtomicLong atomicLong = new AtomicLong(0);
dataBuffers.forEach(d -> atomicLong.addAndGet(d.readableByteCount()));
return atomicLong;
}
}
@@ -0,0 +1,72 @@
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.core.io.ClassPathResource;
import org.springframework.http.server.reactive.HttpHandler;
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<Actor> actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
private RouterFunction<ServerResponse> routingFunction() {
FormHandler formHandler = new FormHandler();
RouterFunction<ServerResponse> 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;
}
public static void main(String[] args) {
SpringApplication.run(FunctionalSpringBootApplication.class, args);
}
}
@@ -0,0 +1,82 @@
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.Wrapper;
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<Actor> actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
private RouterFunction<ServerResponse> routingFunction() {
FormHandler formHandler = new FormHandler();
RouterFunction<ServerResponse> 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);
Wrapper servletWrapper = Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
servletWrapper.setAsyncSupported(true);
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();
}
}
}
@@ -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<Void> 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);
}
}
@@ -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<Actor> 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);
});
}
}
@@ -0,0 +1,13 @@
package com.baeldung.reactive;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Spring5ReactiveApplication{
public static void main(String[] args) {
SpringApplication.run(Spring5ReactiveApplication.class, args);
}
}
@@ -0,0 +1,46 @@
package com.baeldung.reactive.controller;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import java.time.Duration;
import java.util.Random;
import java.util.stream.Stream;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.baeldung.reactive.model.Foo;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
@RestController
public class FooReactiveController {
@GetMapping("/foos/{id}")
public Mono<Foo> getFoo(@PathVariable("id") long id) {
return Mono.just(new Foo(id, randomAlphabetic(6)));
}
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE, value = "/foos")
public Flux<Foo> getAllFoos2() {
final Flux<Foo> foosFlux = Flux.fromStream(Stream.generate(() -> new Foo(new Random().nextLong(), randomAlphabetic(6))));
final Flux<Long> emmitFlux = Flux.interval(Duration.ofSeconds(1));
return Flux.zip(foosFlux, emmitFlux).map(Tuple2::getT1);
}
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE, value = "/foos2")
public Flux<Foo> getAllFoos() {
final Flux<Foo> flux = Flux.<Foo> create(fluxSink -> {
while (true) {
fluxSink.next(new Foo(new Random().nextLong(), randomAlphabetic(6)));
}
}).sample(Duration.ofSeconds(1)).log();
return flux;
}
}
@@ -0,0 +1,39 @@
package com.baeldung.reactive.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PathPatternController {
@GetMapping("/spring5/{*id}")
public String URIVariableHandler(@PathVariable String id) {
return id;
}
@GetMapping("/s?ring5")
public String wildcardTakingExactlyOneChar() {
return "/s?ring5";
}
@GetMapping("/spring5/*id")
public String wildcardTakingZeroOrMoreChar() {
return "/spring5/*id";
}
@GetMapping("/resources/**")
public String wildcardTakingZeroOrMorePathSegments() {
return "/resources/**";
}
@GetMapping("/{baeldung:[a-z]+}")
public String regexInPathVariable(@PathVariable String baeldung) {
return baeldung;
}
@GetMapping("/{var1}_{var2}")
public String multiplePathVariablesInSameSegment(@PathVariable String var1, @PathVariable String var2) {
return "Two variables are var1=" + var1 + " and var2=" + var2;
}
}
@@ -0,0 +1,25 @@
package com.baeldung.reactive.cors.annotated;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication
public class CorsOnAnnotatedElementsApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CorsOnAnnotatedElementsApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8081"));
app.run(args);
}
@Bean
public SecurityWebFilterChain corsAnnotatedSpringSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
return http.build();
}
}
@@ -0,0 +1,49 @@
package com.baeldung.reactive.cors.annotated.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@CrossOrigin(value = { "http://allowed-origin.com" }, allowedHeaders = { "Baeldung-Another-Allowed" }, maxAge = 900)
@RestController
@RequestMapping("/cors-on-controller")
public class CorsOnClassController {
@PutMapping("/regular-endpoint")
public Mono<String> corsDisabledEndpoint() {
return Mono.just("Regular endpoint");
}
@CrossOrigin
@PutMapping("/cors-enabled-endpoint")
public Mono<String> corsEnabledEndpoint() {
return Mono.just("CORS enabled endpoint");
}
@CrossOrigin({ "http://another-allowed-origin.com" })
@PutMapping("/cors-enabled-origin-restrictive-endpoint")
public Mono<String> corsEnabledOriginRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Origin Restrictive");
}
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed" })
@PutMapping("/cors-enabled-header-restrictive-endpoint")
public Mono<String> corsEnabledHeaderRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Header Restrictive");
}
@CrossOrigin(exposedHeaders = { "Baeldung-Exposed" })
@PutMapping("/cors-enabled-exposed-header-endpoint")
public Mono<String> corsEnabledExposedHeadersEndpoint() {
return Mono.just("CORS enabled endpoint - Exposed Header");
}
@PutMapping("/cors-enabled-mixed-config-endpoint")
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed", "Baeldung-Other-Allowed" }, exposedHeaders = { "Baeldung-Allowed", "Baeldung-Exposed" }, maxAge = 3600)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
return Mono.just("CORS enabled endpoint - Mixed Config");
}
}
@@ -0,0 +1,48 @@
package com.baeldung.reactive.cors.annotated.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/cors-on-methods")
public class CorsOnMethodsController {
@PutMapping("/cors-disabled-endpoint")
public Mono<String> corsDisabledEndpoint() {
return Mono.just("CORS disabled endpoint");
}
@CrossOrigin
@PutMapping("/cors-enabled-endpoint")
public Mono<String> corsEnabledEndpoint() {
return Mono.just("CORS enabled endpoint");
}
@CrossOrigin({ "http://allowed-origin.com" })
@PutMapping("/cors-enabled-origin-restrictive-endpoint")
public Mono<String> corsEnabledOriginRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Origin Restrictive");
}
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed" })
@PutMapping("/cors-enabled-header-restrictive-endpoint")
public Mono<String> corsEnabledHeaderRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Header Restrictive");
}
@CrossOrigin(exposedHeaders = { "Baeldung-Exposed" })
@PutMapping("/cors-enabled-exposed-header-endpoint")
public Mono<String> corsEnabledExposedHeadersEndpoint() {
return Mono.just("CORS enabled endpoint - Exposed Header");
}
@PutMapping("/cors-enabled-mixed-config-endpoint")
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed", "Baeldung-Other-Allowed" }, exposedHeaders = { "Baeldung-Allowed", "Baeldung-Exposed" }, maxAge = 3600)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
return Mono.just("CORS enabled endpoint - Mixed Config");
}
}
@@ -0,0 +1,33 @@
package com.baeldung.reactive.cors.global;
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;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class }
)
public class CorsGlobalConfigApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CorsGlobalConfigApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8082"));
app.run(args);
}
@Bean
public SecurityWebFilterChain corsGlobalSpringSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
return http.build();
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.cors.global.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
@Configuration
@EnableWebFlux
public class CorsGlobalConfiguration implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
corsRegistry.addMapping("/**")
.allowedOrigins("http://allowed-origin.com")
.allowedMethods("PUT")
.allowedHeaders("Baeldung-Allowed", "Baledung-Another-Allowed")
.exposedHeaders("Baeldung-Allowed", "Baeldung-Exposed")
.maxAge(3600);
}
}
@@ -0,0 +1,24 @@
package com.baeldung.reactive.cors.global.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController("FurtherCorsConfigsController-cors-on-global-config-and-more")
@RequestMapping("/cors-on-global-config-and-more")
public class FurtherCorsConfigsController {
@DeleteMapping("/further-mixed-config-endpoint")
@CrossOrigin(methods = { RequestMethod.DELETE },
allowedHeaders = { "Baeldung-Other-Allowed" },
exposedHeaders = { "Baeldung-Other-Exposed" }
)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
return Mono.just("CORS Global Configured + Request Configs.");
}
}
@@ -0,0 +1,23 @@
package com.baeldung.reactive.cors.global.controllers;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController("RegularRestController-cors-on-global-config")
@RequestMapping("/cors-on-global-config")
public class RegularRestController {
@PutMapping("/regular-put-endpoint")
public Mono<String> regularPutEndpoint() {
return Mono.just("Regular PUT endpoint");
}
@DeleteMapping("/regular-delete-endpoint")
public Mono<String> regularDeleteEndpoint() {
return Mono.just("Regular DELETE endpoint");
}
}
@@ -0,0 +1,18 @@
package com.baeldung.reactive.cors.global.functional.handlers;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class CorsGlobalFunctionalHandler {
public Mono<ServerResponse> useHandler(final ServerRequest request) {
final String responseMessage = "CORS GLOBAL CONFIG IS NOT EFFECTIVE ON FUNCTIONAL ENDPOINTS!!!";
return ServerResponse.ok()
.body(Mono.just(responseMessage), String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.cors.global.functional.routers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 com.baeldung.reactive.cors.global.functional.handlers.CorsGlobalFunctionalHandler;
@Configuration
public class CorsRouterFunctions {
@Bean
public RouterFunction<ServerResponse> corsGlobalRouter(@Autowired CorsGlobalFunctionalHandler handler) {
return RouterFunctions.route(RequestPredicates.PUT("/global-config-on-functional/cors-disabled-functional-endpoint"), handler::useHandler);
}
}
@@ -0,0 +1,34 @@
package com.baeldung.reactive.cors.webfilter;
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;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class }
)
public class CorsWebFilterApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CorsWebFilterApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8083"));
app.run(args);
}
@Bean
public SecurityWebFilterChain corsWebfilterSpringSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
return http.build();
}
}
@@ -0,0 +1,29 @@
package com.baeldung.reactive.cors.webfilter.config;
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsWebFilterConfig {
@Bean
CorsWebFilter corsWebFilter() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowedOrigins(Arrays.asList("http://allowed-origin.com"));
corsConfig.setMaxAge(8000L);
corsConfig.addAllowedMethod("PUT");
corsConfig.addAllowedHeader("Baeldung-Allowed");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", corsConfig);
return new CorsWebFilter(source);
}
}
@@ -0,0 +1,26 @@
package com.baeldung.reactive.cors.webfilter.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
//@RestController
//@RequestMapping("/web-filter-and-more-on-annotated")
public class FurtherCorsConfigsController {
@DeleteMapping("/further-mixed-config-endpoint")
@CrossOrigin(methods = { RequestMethod.DELETE },
allowedHeaders = { "Baeldung-Other-Allowed" },
exposedHeaders = { "Baeldung-Other-Exposed" }
)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
final String responseMessage = "CORS @CrossOrigin IS NOT EFFECTIVE with WebFilter!!!";
return Mono.just(responseMessage);
}
}
@@ -0,0 +1,23 @@
package com.baeldung.reactive.cors.webfilter.controllers;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
//@RestController
//@RequestMapping("/web-filter-on-annotated")
public class RegularRestController {
@PutMapping("/regular-put-endpoint")
public Mono<String> regularPutEndpoint() {
return Mono.just("Regular PUT endpoint");
}
@DeleteMapping("/regular-delete-endpoint")
public Mono<String> regularDeleteEndpoint() {
return Mono.just("Regular DELETE endpoint");
}
}
@@ -0,0 +1,16 @@
package com.baeldung.reactive.cors.webfilter.functional.handlers;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class CorsWithWebFilterHandler {
public Mono<ServerResponse> useHandler(final ServerRequest request) {
return ServerResponse.ok()
.body(Mono.just("Functional Endpoint"), String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.cors.webfilter.functional.routers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 com.baeldung.reactive.cors.webfilter.functional.handlers.CorsWithWebFilterHandler;
@Configuration
public class CorsWithWebFilterRouterFunctions {
@Bean
public RouterFunction<ServerResponse> corsWebfilterRouter(@Autowired CorsWithWebFilterHandler handler) {
return RouterFunctions.route(RequestPredicates.PUT("/web-filter-on-functional/functional-endpoint"), handler::useHandler);
}
}
@@ -0,0 +1,55 @@
package com.baeldung.reactive.errorhandling;
import java.util.Map;
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
@Component
public class GlobalErrorAttributes extends DefaultErrorAttributes{
private HttpStatus status = HttpStatus.BAD_REQUEST;
private String message = "please provide a name";
public GlobalErrorAttributes() {
super(false);
}
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(request, includeStackTrace);
map.put("status", getStatus());
map.put("message", getMessage());
return map;
}
/**
* @return the status
*/
public HttpStatus getStatus() {
return status;
}
/**
* @param status the status to set
*/
public void setStatus(HttpStatus status) {
this.status = status;
}
/**
* @return the message
*/
public String getMessage() {
return message;
}
/**
* @param message the message to set
*/
public void setMessage(String message) {
this.message = message;
}
}
@@ -0,0 +1,47 @@
package com.baeldung.reactive.errorhandling;
import java.util.Map;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
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.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext,
ServerCodecConfigurer serverCodecConfigurer) {
super(g, new ResourceProperties(), applicationContext);
super.setMessageWriters(serverCodecConfigurer.getWriters());
super.setMessageReaders(serverCodecConfigurer.getReaders());
}
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(final ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {
final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(errorPropertiesMap));
}
}
@@ -0,0 +1,12 @@
package com.baeldung.reactive.errorhandling;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
public class NameRequiredException extends ResponseStatusException {
public NameRequiredException(HttpStatus status, String message, Throwable e) {
super(status, message, e);
}
}
@@ -0,0 +1,29 @@
package com.baeldung.reactive.errorhandling.handlers;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class Handler1 {
public Mono<ServerResponse> handleRequest1(ServerRequest request) {
return sayHello(request).onErrorReturn("Hello, Stranger")
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s));
}
private Mono<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name")
.get());
} catch (Exception e) {
return Mono.error(e);
}
}
}
@@ -0,0 +1,38 @@
package com.baeldung.reactive.errorhandling.handlers;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class Handler2 {
public Mono<ServerResponse> handleRequest2(ServerRequest request) {
return
sayHello(request)
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s))
.onErrorResume(e -> sayHelloFallback()
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s)));
}
private Mono<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name")
.get());
} catch (Exception e) {
return Mono.error(e);
}
}
private Mono<String> sayHelloFallback() {
return Mono.just("Hello, Stranger");
}
}
@@ -0,0 +1,34 @@
package com.baeldung.reactive.errorhandling.handlers;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class Handler3 {
public Mono<ServerResponse> handleRequest3(ServerRequest request) {
return
sayHello(request)
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s))
.onErrorResume(e -> (Mono.just("Hi, I looked around for your name but found: " +
e.getMessage())).flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s)));
}
private Mono<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name")
.get());
} catch (Exception e) {
return Mono.error(e);
}
}
}
@@ -0,0 +1,30 @@
package com.baeldung.reactive.errorhandling.handlers;
import com.baeldung.reactive.errorhandling.NameRequiredException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class Handler4 {
public Mono<ServerResponse> handleRequest4(ServerRequest request) {
return ServerResponse.ok()
.body(sayHello(request)
.onErrorResume(e ->
Mono.error(new NameRequiredException(
HttpStatus.BAD_REQUEST, "please provide a name", e))), String.class);
}
private Mono<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name").get());
} catch (Exception e) {
return Mono.error(e);
}
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.errorhandling.handlers;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class Handler5 {
public Mono<ServerResponse> handleRequest5(ServerRequest request) {
return ServerResponse.ok()
.body(sayHello(request), String.class);
}
private Mono<String> sayHello(ServerRequest request) {
return Mono.just("Hello, " + request.queryParam("name").get());
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.errorhandling.routers;
import com.baeldung.reactive.errorhandling.handlers.Handler1;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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;
@Component
public class Router1 {
@Bean
public RouterFunction<ServerResponse> routeRequest1(Handler1 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint1")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest1);
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.errorhandling.routers;
import com.baeldung.reactive.errorhandling.handlers.Handler2;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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;
@Component
public class Router2 {
@Bean
public RouterFunction<ServerResponse> routeRequest2(Handler2 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint2")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest2);
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.errorhandling.routers;
import com.baeldung.reactive.errorhandling.handlers.Handler3;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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;
@Component
public class Router3 {
@Bean
public RouterFunction<ServerResponse> routeRequest3(Handler3 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint3")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest3);
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.errorhandling.routers;
import com.baeldung.reactive.errorhandling.handlers.Handler4;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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;
@Component
public class Router4 {
@Bean
public RouterFunction<ServerResponse> routeRequest4(Handler4 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint4")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest4);
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.errorhandling.routers;
import com.baeldung.reactive.errorhandling.handlers.Handler5;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
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;
@Component
public class Router5 {
@Bean
public RouterFunction<ServerResponse> routeRequest5(Handler5 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint5")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest5);
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.filters;
import org.springframework.web.reactive.function.server.HandlerFilterFunction;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import static org.springframework.http.HttpStatus.FORBIDDEN;
public class ExampleHandlerFilterFunction implements HandlerFilterFunction<ServerResponse, ServerResponse> {
@Override
public Mono<ServerResponse> filter(ServerRequest serverRequest, HandlerFunction<ServerResponse> handlerFunction) {
if (serverRequest.pathVariable("name").equalsIgnoreCase("test")) {
return ServerResponse.status(FORBIDDEN).build();
}
return handlerFunction.handle(serverRequest);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.reactive.filters;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Component
public class ExampleWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
serverWebExchange.getResponse().getHeaders().add("web-filter", "web-filter-test");
return webFilterChain.filter(serverWebExchange);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.reactive.filters;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@Component
class PlayerHandler {
Mono<ServerResponse> getName(ServerRequest request) {
Mono<String> name = Mono.just(request.pathVariable("name"));
return ok().body(name, String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.filters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 static org.springframework.web.reactive.function.server.RequestPredicates.GET;
@Configuration
public class PlayerRouter {
@Bean
public RouterFunction<ServerResponse> route(PlayerHandler playerHandler) {
return RouterFunctions
.route(GET("/players/{name}"), playerHandler::getName)
.filter(new ExampleHandlerFilterFunction());
}
}
@@ -0,0 +1,15 @@
package com.baeldung.reactive.filters;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class UserController {
@GetMapping(path = "/users/{name}")
public Mono<String> getName(@PathVariable String name) {
return Mono.just(name);
}
}
@@ -0,0 +1,13 @@
package com.baeldung.reactive.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class Foo {
private long id;
private String name;
}
@@ -0,0 +1,35 @@
package com.baeldung.reactive.responseheaders.controllers;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/response-header")
public class ResponseHeaderController {
@GetMapping("/response-entity")
public Mono<ResponseEntity<String>> usingResponseEntityBuilder() {
String responseHeaderKey = "Baeldung-Example-Header";
String responseHeaderValue = "Value-ResponseEntityBuilder";
String responseBody = "Response with header using ResponseEntity (builder)";
return Mono.just(ResponseEntity.ok()
.header(responseHeaderKey, responseHeaderValue)
.body(responseBody));
}
@GetMapping("/server-http-response")
public Mono<String> usingServerHttpResponse(ServerHttpResponse response) {
String responseHeaderKey = "Baeldung-Example-Header";
String responseHeaderValue = "Value-ServerHttpResponse";
String responseBody = "Response with header using ServerHttpResponse";
response.getHeaders().add(responseHeaderKey, responseHeaderValue);
return Mono.just(responseBody);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.responseheaders.filter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Component
public class AddResponseHeaderWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
exchange.getResponse()
.getHeaders()
.add("Baeldung-Example-Filter-Header", "Value-Filter");
return chain.filter(exchange);
}
}
@@ -0,0 +1,21 @@
package com.baeldung.reactive.responseheaders.functional.handlers;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class ResponseHeaderHandler {
public Mono<ServerResponse> useHandler(final ServerRequest request) {
String responseHeaderKey = "Baeldung-Example-Header";
String responseHeaderValue = "Value-Handler";
String responseBody = "Response with header using Handler";
return ServerResponse.ok()
.header(responseHeaderKey, responseHeaderValue)
.body(Mono.just(responseBody),String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.responseheaders.functional.routers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 com.baeldung.reactive.responseheaders.functional.handlers.ResponseHeaderHandler;
@Configuration
public class ResponseHeadersRouterFunctions {
@Bean
public RouterFunction<ServerResponse> responseHeaderRoute(@Autowired ResponseHeaderHandler handler) {
return RouterFunctions.route(RequestPredicates.GET("/functional-response-header/single-handler"), handler::useHandler);
}
}
@@ -0,0 +1,31 @@
package com.baeldung.reactive.serversentevents.consumer;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { RedisReactiveAutoConfiguration.class })
@EnableAsync
public class ConsumerSSEApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ConsumerSSEApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8082"));
app.run(args);
}
@Bean
public SecurityWebFilterChain sseConsumerSpringSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.anyExchange()
.permitAll();
return http.build();
}
}
@@ -0,0 +1,83 @@
package com.baeldung.reactive.serversentevents.consumer.controller;
import java.time.LocalTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/sse-consumer")
public class ClientController {
private static Logger logger = LoggerFactory.getLogger(ClientController.class);
private WebClient client = WebClient.create("http://localhost:8081/sse-server");
@GetMapping("/launch-sse-client")
public String launchSSEFromSSEWebClient() {
consumeSSE();
return "LAUNCHED EVENT CLIENT!!! Check the logs...";
}
@GetMapping("/launch-flux-client")
public String launchcFluxFromSSEWebClient() {
consumeFlux();
return "LAUNCHED EVENT CLIENT!!! Check the logs...";
}
@GetMapping("/launch-sse-from-flux-endpoint-client")
public String launchFluxFromFluxWebClient() {
consumeSSEFromFluxEndpoint();
return "LAUNCHED EVENT CLIENT!!! Check the logs...";
}
@Async
public void consumeSSE() {
ParameterizedTypeReference<ServerSentEvent<String>> type = new ParameterizedTypeReference<ServerSentEvent<String>>() {
};
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-sse")
.retrieve()
.bodyToFlux(type);
eventStream.subscribe(content -> logger.info("Current time: {} - Received SSE: name[{}], id [{}], content[{}] ", LocalTime.now(), content.event(), content.id(), content.data()), error -> logger.error("Error receiving SSE: {}", error),
() -> logger.info("Completed!!!"));
}
@Async
public void consumeFlux() {
Flux<String> stringStream = client.get()
.uri("/stream-flux")
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(String.class);
stringStream.subscribe(content -> logger.info("Current time: {} - Received content: {} ", LocalTime.now(), content), error -> logger.error("Error retrieving content: {}", error), () -> logger.info("Completed!!!"));
}
@Async
public void consumeSSEFromFluxEndpoint() {
ParameterizedTypeReference<ServerSentEvent<String>> type = new ParameterizedTypeReference<ServerSentEvent<String>>() {
};
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-flux")
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(type);
eventStream.subscribe(content -> logger.info("Current time: {} - Received SSE: name[{}], id [{}], content[{}] ", LocalTime.now(), content.event(), content.id(), content.data()), error -> logger.error("Error receiving SSE: {}", error),
() -> logger.info("Completed!!!"));
}
}
@@ -0,0 +1,29 @@
package com.baeldung.reactive.serversentevents.server;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { RedisReactiveAutoConfiguration.class })
public class ServerSSEApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ServerSSEApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8081"));
app.run(args);
}
@Bean
public SecurityWebFilterChain sseServerSpringSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.anyExchange()
.permitAll();
return http.build();
}
}
@@ -0,0 +1,35 @@
package com.baeldung.reactive.serversentevents.server.controllers;
import java.time.Duration;
import java.time.LocalTime;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/sse-server")
public class ServerController {
@GetMapping("/stream-sse")
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String> builder()
.id(String.valueOf(sequence))
.event("periodic-event")
.data("SSE - " + LocalTime.now()
.toString())
.build());
}
@GetMapping(path = "/stream-flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamFlux() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> "Flux - " + LocalTime.now()
.toString());
}
}
@@ -0,0 +1,23 @@
package com.baeldung.reactive.urlmatch;
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;
}
}
@@ -0,0 +1,63 @@
package com.baeldung.reactive.urlmatch;
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.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
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;
public class ExploreSpring5URLPatternUsingRouterFunctions {
private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/p?ths"), serverRequest -> ok().body(fromObject("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("id"))))
.andRoute(GET("/*card"), serverRequest -> ok().body(fromObject("/*card path was accessed")))
.andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2"))))
.andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromObject("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung"))))
.and(RouterFunctions.resources("/files/{*filepaths}", new ClassPathResource("files/")));
}
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);
Wrapper servletWrapper = Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
servletWrapper.setAsyncSupported(true);
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();
}
}
}
@@ -0,0 +1,41 @@
package com.baeldung.reactive.urlmatch;
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<ServerResponse> 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<ServerResponse> handleUpload(ServerRequest request) {
return request.body(toDataBuffers())
.collectList()
.flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString())));
}
private AtomicLong extractData(List<DataBuffer> dataBuffers) {
AtomicLong atomicLong = new AtomicLong(0);
dataBuffers.forEach(d -> atomicLong.addAndGet(d.asByteBuffer()
.array().length));
return atomicLong;
}
}
@@ -0,0 +1,80 @@
package com.baeldung.reactive.urlmatch;
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<Actor> actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
private RouterFunction<ServerResponse> routingFunction() {
FormHandler formHandler = new FormHandler();
RouterFunction<ServerResponse> 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();
}
}
}
@@ -0,0 +1,27 @@
package com.baeldung.reactive.urlmatch;
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<Void> 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())
.contextPath(request.getPath()
.toString())
.path("/test"))
.build());
}
return webFilterChain.filter(serverWebExchange);
}
}
@@ -0,0 +1,15 @@
package com.baeldung.reactive.util;
import com.sun.management.OperatingSystemMXBean;
import java.lang.management.ManagementFactory;
public class CpuUtils {
private static OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
static Double getUsage() {
return (operatingSystemMXBean.getSystemCpuLoad() / operatingSystemMXBean.getAvailableProcessors()) * 100;
}
}
@@ -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 + '}';
}
}
@@ -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<WebClient.RequestBodySpec> request1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST);
WebClient.UriSpec<WebClient.RequestBodySpec> 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<Publisher<String>, ReactiveHttpOutputMessage> inserter1 = BodyInserters
.fromPublisher(Subscriber::onComplete, String.class);
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("key1", "value1");
map.add("key2", "value2");
// BodyInserter<MultiValueMap<String, ?>, ClientHttpRequest> inserter2 = BodyInserters.fromMultipartData(map);
BodyInserter<String, ReactiveHttpOutputMessage> 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();
}
}
@@ -0,0 +1,15 @@
package com.baeldung.websession;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {"com.baeldung"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.websession.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
@Configuration
//@EnableRedisWebSession
public class RedisConfig {
/**
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
*/
}
@@ -0,0 +1,20 @@
package com.baeldung.websession.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.ReactiveMapSessionRepository;
import org.springframework.session.ReactiveSessionRepository;
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
@EnableSpringWebSession
public class SessionConfig {
@Bean
public ReactiveSessionRepository reactiveSessionRepository() {
return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
}
}
@@ -0,0 +1,20 @@
package com.baeldung.websession.configuration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
@Configuration
@EnableWebFlux
public class WebFluxConfig implements ApplicationContextAware, WebFluxConfigurer {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
}
@@ -0,0 +1,56 @@
package com.baeldung.websession.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
@Configuration
@EnableWebFluxSecurity
public class WebFluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails admin = User
.withUsername("admin")
.password(encoder().encode("password"))
.roles("ADMIN")
.build();
UserDetails user = User
.withUsername("user")
.password(encoder().encode("password"))
.roles("USER")
.build();
return new MapReactiveUserDetailsService(admin, user);
}
@Bean
public SecurityWebFilterChain webSessionSpringSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.anyExchange().authenticated()
.and()
.httpBasic()
.securityContextRepository(new WebSessionServerSecurityContextRepository())
.and()
.formLogin();
http.csrf().disable();
return http.build();
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
@@ -0,0 +1,42 @@
package com.baeldung.websession.controller;
import com.baeldung.websession.transfer.CustomResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
@RestController
public class SessionController {
@GetMapping("/websession/test")
public Mono<CustomResponse> testWebSessionByParam(
@RequestParam(value = "id") int id,
@RequestParam(value = "note") String note,
WebSession session) {
session.getAttributes().put("id", id);
session.getAttributes().put("note", note);
CustomResponse r = new CustomResponse();
r.setId((int) session.getAttributes().get("id"));
r.setNote((String) session.getAttributes().get("note"));
return Mono.just(r);
}
@GetMapping("/websession")
public Mono<CustomResponse> getSession(WebSession session) {
session.getAttributes().putIfAbsent("id", 0);
session.getAttributes().putIfAbsent("note", "Howdy Cosmic Spheroid!");
CustomResponse r = new CustomResponse();
r.setId((int) session.getAttributes().get("id"));
r.setNote((String) session.getAttributes().get("note"));
return Mono.just(r);
}
}
@@ -0,0 +1,31 @@
package com.baeldung.websession.transfer;
public class CustomResponse {
private int id;
private String note;
public CustomResponse() {}
public CustomResponse(int id, String note) {
this.id = id;
this.note = note;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
@@ -0,0 +1,11 @@
package com.baeldung.websocket;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Event {
private String eventId;
private String eventDt;
}
@@ -0,0 +1,26 @@
package com.baeldung.websocket;
import java.net.URI;
import java.time.Duration;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
import reactor.core.publisher.Mono;
public class ReactiveJavaClientWebSocket {
public static void main(String[] args) throws InterruptedException {
WebSocketClient client = new ReactorNettyWebSocketClient();
client.execute(
URI.create("ws://localhost:8080/event-emitter"),
session -> session.send(
Mono.just(session.textMessage("event-spring-reactive-client-websocket")))
.thenMany(session.receive()
.map(WebSocketMessage::getPayloadAsText)
.log())
.then())
.block(Duration.ofSeconds(10L));
}
}
@@ -0,0 +1,11 @@
package com.baeldung.websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ReactiveWebSocketApplication {
public static void main(String[] args) {
SpringApplication.run(ReactiveWebSocketApplication.class, args);
}
}
@@ -0,0 +1,37 @@
package com.baeldung.websocket;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
@Configuration
public class ReactiveWebSocketConfiguration {
@Autowired
@Qualifier("ReactiveWebSocketHandler")
private WebSocketHandler webSocketHandler;
@Bean
public HandlerMapping webSocketHandlerMapping() {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/event-emitter", webSocketHandler);
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(1);
handlerMapping.setUrlMap(map);
return handlerMapping;
}
@Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}
@@ -0,0 +1,41 @@
package com.baeldung.websocket;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import static java.time.LocalDateTime.now;
import static java.util.UUID.randomUUID;
@Component("ReactiveWebSocketHandler")
public class ReactiveWebSocketHandler implements WebSocketHandler {
private static final ObjectMapper json = new ObjectMapper();
private Flux<String> eventFlux = Flux.generate(sink -> {
Event event = new Event(randomUUID().toString(), now().toString());
try {
sink.next(json.writeValueAsString(event));
} catch (JsonProcessingException e) {
sink.error(e);
}
});
private Flux<String> intervalFlux = Flux.interval(Duration.ofMillis(1000L))
.zipWith(eventFlux, (time, event) -> event);
@Override
public Mono<Void> handle(WebSocketSession webSocketSession) {
return webSocketSession.send(intervalFlux
.map(webSocketSession::textMessage))
.and(webSocketSession.receive()
.map(WebSocketMessage::getPayloadAsText).log());
}
}