JAVA-24463 Rollback the changes in the spring-reactive and spring-5-reactive (#14845)
Co-authored-by: timis1 <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
timis1
parent
ef3c6c0e0b
commit
bcfb4881fd
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Baeldung: Static Content in Spring WebFlux</title>
|
||||
</head>
|
||||
<body>
|
||||
Example Spring Web Flux and web resources configuration
|
||||
</body>
|
||||
</html>
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
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.fromValue;
|
||||
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(fromValue(extractData(dataBuffers).toString())));
|
||||
}
|
||||
|
||||
private AtomicLong extractData(List<DataBuffer> dataBuffers) {
|
||||
AtomicLong atomicLong = new AtomicLong(0);
|
||||
dataBuffers.forEach(d -> atomicLong.addAndGet(d.readableByteCount()));
|
||||
return atomicLong;
|
||||
}
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
package com.baeldung.functional;
|
||||
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
|
||||
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(fromValue("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);
|
||||
}
|
||||
}
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
package com.baeldung.functional;
|
||||
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromValue;
|
||||
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.accept;
|
||||
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.MediaType;
|
||||
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("/actor"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/actor"), serverRequest -> serverRequest.bodyToMono(Actor.class)
|
||||
.doOnNext(actors::add)
|
||||
.then(ok().build()));
|
||||
|
||||
return route(GET("/test"), serverRequest -> ok().body(fromValue("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
|
||||
.andRoute(POST("/upload"), formHandler::handleUpload)
|
||||
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
|
||||
.andNest(accept(MediaType.APPLICATION_JSON), 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
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.fromValue;
|
||||
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(fromValue("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(fromValue(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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package com.baeldung.reactive;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
|
||||
@SpringBootApplication(exclude = { RedisAutoConfiguration.class })
|
||||
public class Spring5ReactiveApplication{
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Spring5ReactiveApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.baeldung.reactive.concurrency;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Please note we assume Mongo and Kafka are running in the local machine and on default configuration.
|
||||
* Additionally, if you want to experiment with Tomcat/Jetty instead of Netty, just uncomment the lines in pom.xml and rebuild.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package com.baeldung.reactive.concurrency;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||
import org.apache.kafka.clients.producer.ProducerConfig;
|
||||
import org.apache.kafka.clients.producer.ProducerRecord;
|
||||
import org.apache.kafka.common.serialization.IntegerDeserializer;
|
||||
import org.apache.kafka.common.serialization.IntegerSerializer;
|
||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||
import org.apache.kafka.common.serialization.StringSerializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import reactor.kafka.receiver.KafkaReceiver;
|
||||
import reactor.kafka.receiver.ReceiverOptions;
|
||||
import reactor.kafka.receiver.ReceiverRecord;
|
||||
import reactor.kafka.sender.KafkaSender;
|
||||
import reactor.kafka.sender.SenderOptions;
|
||||
import reactor.kafka.sender.SenderRecord;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
public class Controller {
|
||||
|
||||
@Autowired
|
||||
private PersonRepository personRepository;
|
||||
|
||||
private Scheduler scheduler = Schedulers.newBoundedElastic(5, 10, "MyThreadGroup");
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(Controller.class);
|
||||
|
||||
@GetMapping("/threads/webflux")
|
||||
public Flux<String> getThreadsWebflux() {
|
||||
return Flux.fromIterable(getThreads());
|
||||
}
|
||||
|
||||
@GetMapping("/threads/webclient")
|
||||
public Flux<String> getThreadsWebClient() {
|
||||
WebClient.create("http://localhost:8080/index")
|
||||
.get()
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.subscribeOn(scheduler)
|
||||
.publishOn(scheduler)
|
||||
.doOnNext(s -> logger.info("Response: {}", s))
|
||||
.subscribe();
|
||||
return Flux.fromIterable(getThreads());
|
||||
}
|
||||
|
||||
@GetMapping("/threads/rxjava")
|
||||
public Observable<String> getIndexRxJava() {
|
||||
Observable.fromIterable(Arrays.asList("Hello", "World"))
|
||||
.map(s -> s.toUpperCase())
|
||||
.observeOn(io.reactivex.schedulers.Schedulers.trampoline())
|
||||
.doOnNext(s -> logger.info("String: {}", s))
|
||||
.subscribe();
|
||||
return Observable.fromIterable(getThreads());
|
||||
}
|
||||
|
||||
@GetMapping("/threads/mongodb")
|
||||
public Flux<String> getIndexMongo() {
|
||||
personRepository.findAll()
|
||||
.doOnNext(p -> logger.info("Person: {}", p))
|
||||
.subscribe();
|
||||
return Flux.fromIterable(getThreads());
|
||||
}
|
||||
|
||||
@GetMapping("/threads/reactor-kafka")
|
||||
public Flux<String> getIndexKafka() {
|
||||
Map<String, Object> producerProps = new HashMap<>();
|
||||
producerProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
|
||||
producerProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class);
|
||||
producerProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
|
||||
SenderOptions<Integer, String> senderOptions = SenderOptions.create(producerProps);
|
||||
KafkaSender<Integer, String> sender = KafkaSender.create(senderOptions);
|
||||
Flux<SenderRecord<Integer, String, Integer>> outboundFlux = Flux.range(1, 10)
|
||||
.map(i -> SenderRecord.create(new ProducerRecord<>("reactive-test", i, "Message_" + i), i));
|
||||
sender.send(outboundFlux)
|
||||
.subscribe();
|
||||
|
||||
Map<String, Object> consumerProps = new HashMap<>();
|
||||
consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
|
||||
consumerProps.put(ConsumerConfig.CLIENT_ID_CONFIG, "my-consumer");
|
||||
consumerProps.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group");
|
||||
consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, IntegerDeserializer.class);
|
||||
consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||
consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
|
||||
ReceiverOptions<Integer, String> receiverOptions = ReceiverOptions.create(consumerProps);
|
||||
receiverOptions.subscription(Collections.singleton("reactive-test"));
|
||||
KafkaReceiver<Integer, String> receiver = KafkaReceiver.create(receiverOptions);
|
||||
Flux<ReceiverRecord<Integer, String>> inboundFlux = receiver.receive();
|
||||
inboundFlux.subscribe(r -> {
|
||||
logger.info("Received message: {}", r.value());
|
||||
r.receiverOffset()
|
||||
.acknowledge();
|
||||
});
|
||||
return Flux.fromIterable(getThreads());
|
||||
}
|
||||
|
||||
@GetMapping("/index")
|
||||
public Mono<String> getIndex() {
|
||||
return Mono.just("Hello world!");
|
||||
}
|
||||
|
||||
private List<String> getThreads() {
|
||||
return Thread.getAllStackTraces()
|
||||
.keySet()
|
||||
.stream()
|
||||
.map(t -> String.format("%-20s \t %s \t %d \t %s\n", t.getName(), t.getState(), t.getPriority(), t.isDaemon() ? "Daemon" : "Normal"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.baeldung.reactive.concurrency;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
|
||||
@Document
|
||||
public class Person {
|
||||
@Id
|
||||
String id;
|
||||
|
||||
public Person(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" + "id='" + id + '\'' + '}';
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.baeldung.reactive.concurrency;
|
||||
|
||||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
|
||||
|
||||
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.baeldung.reactive.debugging.consumer;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import reactor.core.publisher.Hooks;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@SpringBootApplication(exclude = MongoReactiveAutoConfiguration.class)
|
||||
@EnableScheduling
|
||||
public class ConsumerDebuggingApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Hooks.onOperatorDebug();
|
||||
SpringApplication app = new SpringApplication(ConsumerDebuggingApplication.class);
|
||||
app.setDefaultProperties(Collections.singletonMap("server.port", "8082"));
|
||||
app.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain debuggingConsumerSpringSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange(exchanges -> exchanges
|
||||
.anyExchange()
|
||||
.permitAll());
|
||||
http.csrf(csrf -> csrf.disable());
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.baeldung.reactive.debugging.consumer.controllers;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Hooks;
|
||||
|
||||
@RestController
|
||||
public class ReactiveConfigsToggleRestController {
|
||||
|
||||
@GetMapping("/debug-hook-on")
|
||||
public String setReactiveDebugOn() {
|
||||
Hooks.onOperatorDebug();
|
||||
return "DEBUG HOOK ON";
|
||||
}
|
||||
|
||||
@GetMapping("/debug-hook-off")
|
||||
public String setReactiveDebugOff() {
|
||||
Hooks.resetOnOperatorDebug();
|
||||
return "DEBUG HOOK OFF";
|
||||
}
|
||||
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
package com.baeldung.reactive.debugging.consumer.cronjobs;
|
||||
|
||||
import com.baeldung.reactive.debugging.consumer.model.Foo;
|
||||
import com.baeldung.reactive.debugging.consumer.model.FooDto;
|
||||
import com.baeldung.reactive.debugging.consumer.service.FooService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Component
|
||||
public class CronJobs {
|
||||
|
||||
final Logger logger = LoggerFactory.getLogger(CronJobs.class);
|
||||
final WebClient client = WebClient.create("http://localhost:8081");
|
||||
|
||||
@Autowired
|
||||
private FooService service;
|
||||
|
||||
@Scheduled(fixedRate = 10000)
|
||||
public void consumeInfiniteFlux() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 1 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
|
||||
int random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 3);
|
||||
|
||||
switch (random) {
|
||||
case 0:
|
||||
logger.info("process 1 with approach 1");
|
||||
service.processFoo(fluxFoo);
|
||||
break;
|
||||
case 1:
|
||||
logger.info("process 1 with approach 1 EH");
|
||||
service.processUsingApproachOneWithErrorHandling(fluxFoo);
|
||||
break;
|
||||
default:
|
||||
logger.info("process 1 with approach 2");
|
||||
service.processFooInAnotherScenario(fluxFoo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFlux2() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 2 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
|
||||
int random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 3);
|
||||
|
||||
switch (random) {
|
||||
case 0:
|
||||
logger.info("process 2 with approach 1");
|
||||
service.processFoo(fluxFoo);
|
||||
break;
|
||||
case 1:
|
||||
logger.info("process 2 with approach 1 EH");
|
||||
service.processUsingApproachOneWithErrorHandling(fluxFoo);
|
||||
break;
|
||||
default:
|
||||
logger.info("process 2 with approach 2");
|
||||
service.processFooInAnotherScenario(fluxFoo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFlux3() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 3 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
logger.info("process 3 with approach 3");
|
||||
service.processUsingApproachThree(fluxFoo);
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFluxWithCheckpoint4() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 4 with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
logger.info("process 4 with approach 4");
|
||||
service.processUsingApproachFourWithCheckpoint(fluxFoo);
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFluxWitParallelScheduler() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 5-parallel with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
logger.info("process 5-parallel with approach 5-parallel");
|
||||
service.processUsingApproachFivePublishingToDifferentParallelThreads(fluxFoo);
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 20000)
|
||||
public void consumeFiniteFluxWithSingleSchedulers() {
|
||||
Flux<Foo> fluxFoo = client.get()
|
||||
.uri("/functional-reactive/periodic-foo-2")
|
||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||
.retrieve()
|
||||
.bodyToFlux(FooDto.class)
|
||||
.delayElements(Duration.ofMillis(100))
|
||||
.map(dto -> {
|
||||
logger.debug("process 5-single with dto id {} name{}", dto.getId(), dto.getName());
|
||||
return new Foo(dto);
|
||||
});
|
||||
logger.info("process 5-single with approach 5-single");
|
||||
service.processUsingApproachFivePublishingToDifferentSingleThreads(fluxFoo);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.baeldung.reactive.debugging.consumer.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Foo {
|
||||
|
||||
private Integer id;
|
||||
private String formattedName;
|
||||
private Integer quantity;
|
||||
|
||||
public Foo(FooDto dto) {
|
||||
this.id = randomId() == 0 ? null : dto.getId();
|
||||
this.formattedName = dto.getName();
|
||||
this.quantity = randomQuantity();
|
||||
}
|
||||
|
||||
private static int randomId() {
|
||||
return ThreadLocalRandom.current().nextInt(0, 100);
|
||||
}
|
||||
|
||||
private static int randomQuantity() {
|
||||
return ThreadLocalRandom.current().nextInt(0, 10);
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.baeldung.reactive.debugging.consumer.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FooDto {
|
||||
|
||||
private Integer id;
|
||||
private String name;
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.baeldung.reactive.debugging.consumer.service;
|
||||
|
||||
import com.baeldung.reactive.debugging.consumer.model.Foo;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class FooNameHelper {
|
||||
|
||||
public static Foo concatAndSubstringFooName(Foo foo) {
|
||||
Foo concat = concatFooName(foo);
|
||||
return substringFooName(concat);
|
||||
}
|
||||
|
||||
public static Foo concatFooName(Foo foo) {
|
||||
|
||||
int random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 80);
|
||||
|
||||
String processedName = (random != 0)
|
||||
? foo.getFormattedName()
|
||||
: foo.getFormattedName() + "-bael";
|
||||
|
||||
foo.setFormattedName(processedName);
|
||||
return foo;
|
||||
}
|
||||
|
||||
public static Foo substringFooName(Foo foo) {
|
||||
int random = ThreadLocalRandom.current()
|
||||
.nextInt(0, 100);
|
||||
|
||||
String processedName = (random == 0)
|
||||
? foo.getFormattedName().substring(10, 15)
|
||||
: foo.getFormattedName().substring(0, 5);
|
||||
|
||||
foo.setFormattedName(processedName);
|
||||
|
||||
return foo;
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.baeldung.reactive.debugging.consumer.service;
|
||||
|
||||
import com.baeldung.reactive.debugging.consumer.model.Foo;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class FooQuantityHelper {
|
||||
|
||||
public static Foo processFooReducingQuantity(Foo foo) {
|
||||
int random = ThreadLocalRandom.current().nextInt(0, 90);
|
||||
int result = (random == 0) ? 0 : foo.getQuantity() + 2;
|
||||
foo.setQuantity(result);
|
||||
|
||||
return divideFooQuantity(foo);
|
||||
}
|
||||
|
||||
public static Foo divideFooQuantity(Foo foo) {
|
||||
|
||||
Integer result = (int) Math.round(5.0 / foo.getQuantity());
|
||||
foo.setQuantity(result);
|
||||
return foo;
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.baeldung.reactive.debugging.consumer.service;
|
||||
|
||||
import com.baeldung.reactive.debugging.consumer.model.Foo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class FooReporter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FooReporter.class);
|
||||
|
||||
public static Foo reportResult(Foo foo, String approach) {
|
||||
if (foo.getId() == null) {
|
||||
throw new IllegalArgumentException("Null id is not valid!");
|
||||
}
|
||||
LOGGER.info("Reporting for approach {}: Foo with id '{}' name '{}' and quantity '{}'",
|
||||
approach, foo.getId(), foo.getFormattedName(), foo.getQuantity());
|
||||
|
||||
return foo;
|
||||
}
|
||||
|
||||
public static Foo reportResult(Foo input) {
|
||||
return reportResult(input, "default");
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package com.baeldung.reactive.debugging.consumer.service;
|
||||
|
||||
import com.baeldung.reactive.debugging.consumer.model.Foo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import static com.baeldung.reactive.debugging.consumer.service.FooReporter.reportResult;
|
||||
|
||||
@Component
|
||||
public class FooService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FooService.class);
|
||||
|
||||
public void processFoo(Flux<Foo> flux) {
|
||||
flux.map(FooNameHelper::concatFooName)
|
||||
.map(FooNameHelper::substringFooName)
|
||||
.log()
|
||||
.map(FooReporter::reportResult)
|
||||
.doOnError(error -> LOGGER.error("The following error happened on processFoo method!", error))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void processFooInAnotherScenario(Flux<Foo> flux) {
|
||||
flux.map(FooNameHelper::substringFooName)
|
||||
.map(FooQuantityHelper::divideFooQuantity)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachOneWithErrorHandling(Flux<Foo> flux) {
|
||||
LOGGER.info("starting approach one w error handling!");
|
||||
|
||||
flux.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.map(FooNameHelper::substringFooName)
|
||||
.map(FooQuantityHelper::processFooReducingQuantity)
|
||||
.map(FooQuantityHelper::processFooReducingQuantity)
|
||||
.map(FooQuantityHelper::processFooReducingQuantity)
|
||||
.map(FooReporter::reportResult)
|
||||
.doOnError(error -> LOGGER.error("Approach 1 with Error Handling failed!", error))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachThree(Flux<Foo> flux) {
|
||||
LOGGER.info("starting approach three!");
|
||||
|
||||
flux.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.map(foo -> reportResult(foo, "THREE"))
|
||||
.doOnError(error -> LOGGER.error("Approach 3 failed!", error))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachFourWithCheckpoint(Flux<Foo> flux) {
|
||||
LOGGER.info("starting approach four!");
|
||||
|
||||
flux.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.checkpoint("CHECKPOINT 1")
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.map(FooQuantityHelper::divideFooQuantity)
|
||||
.checkpoint("CHECKPOINT 2", true)
|
||||
.map(foo -> reportResult(foo, "FOUR"))
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.doOnError(error -> LOGGER.error("Approach 4 failed!", error))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachFourWithInitialCheckpoint(Flux<Foo> flux) {
|
||||
LOGGER.info("starting approach four!");
|
||||
|
||||
flux.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.checkpoint("CHECKPOINT 1", true)
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.map(FooQuantityHelper::divideFooQuantity)
|
||||
.map(foo -> reportResult(foo, "FOUR"))
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.doOnError(error -> LOGGER.error("Approach 4-2 failed!", error))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachFivePublishingToDifferentParallelThreads(Flux<Foo> flux) {
|
||||
LOGGER.info("starting approach five-parallel!");
|
||||
|
||||
flux.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.publishOn(Schedulers.newParallel("five-parallel-foo"))
|
||||
.log()
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.map(foo -> reportResult(foo, "FIVE-PARALLEL"))
|
||||
.publishOn(Schedulers.newSingle("five-parallel-bar"))
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.doOnError(error -> LOGGER.error("Approach 5-parallel failed!", error))
|
||||
.subscribeOn(Schedulers.newParallel("five-parallel-starter"))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void processUsingApproachFivePublishingToDifferentSingleThreads(Flux<Foo> flux) {
|
||||
LOGGER.info("starting approach five-single!");
|
||||
|
||||
flux.log()
|
||||
.subscribeOn(Schedulers.newSingle("five-single-starter"))
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.publishOn(Schedulers.newSingle("five-single-foo"))
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.map(FooQuantityHelper::divideFooQuantity)
|
||||
.map(foo -> reportResult(foo, "FIVE-SINGLE"))
|
||||
.publishOn(Schedulers.newSingle("five-single-bar"))
|
||||
.map(FooNameHelper::concatAndSubstringFooName)
|
||||
.doOnError(error -> LOGGER.error("Approach 5-single failed!", error))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.baeldung.reactive.debugging.server;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
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;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@EnableWebFlux
|
||||
@SpringBootApplication(exclude = MongoReactiveAutoConfiguration.class)
|
||||
public class ServerDebuggingApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication app = new SpringApplication(ServerDebuggingApplication.class);
|
||||
app.setDefaultProperties(Collections.singletonMap("server.port", "8081"));
|
||||
app.run(args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain debuggingServerSpringSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange(exchanges -> exchanges
|
||||
.anyExchange()
|
||||
.permitAll());
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.baeldung.reactive.debugging.server.handlers;
|
||||
|
||||
import com.baeldung.reactive.debugging.server.model.Foo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Component
|
||||
public class ServerHandler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ServerHandler.class);
|
||||
|
||||
public Mono<ServerResponse> useHandler(final ServerRequest request) {
|
||||
// there are chances that something goes wrong here...
|
||||
return ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_EVENT_STREAM)
|
||||
.body(getFlux(), Foo.class);
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> useHandlerFinite(final ServerRequest request) {
|
||||
return ServerResponse.ok()
|
||||
.contentType(MediaType.TEXT_EVENT_STREAM)
|
||||
.body(Flux.range(0, 50)
|
||||
.map(sequence -> new Foo(new Long(sequence), "theFooNameNumber" + sequence)
|
||||
), Foo.class);
|
||||
}
|
||||
|
||||
private static Flux<Foo> getFlux() {
|
||||
return Flux.interval(Duration.ofSeconds(1))
|
||||
.map(sequence -> {
|
||||
LOGGER.info("retrieving Foo. Sequence: {}", sequence);
|
||||
if (ThreadLocalRandom.current().nextInt(0, 50) == 1) {
|
||||
throw new RuntimeException("There was an error retrieving the Foo!");
|
||||
}
|
||||
return new Foo(sequence, "name" + sequence);
|
||||
});
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
package com.baeldung.reactive.model;
|
||||
package com.baeldung.reactive.debugging.server.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Foo {
|
||||
|
||||
private long id;
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
}
|
||||
+9
-8
@@ -1,5 +1,6 @@
|
||||
package com.baeldung.reactive.responseheaders.functional.routers;
|
||||
package com.baeldung.reactive.debugging.server.routers;
|
||||
|
||||
import com.baeldung.reactive.debugging.server.handlers.ServerHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -8,13 +9,13 @@ 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 {
|
||||
public class ServerRouter {
|
||||
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> responseRoute(@Autowired ServerHandler handler) {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/functional-reactive/periodic-foo"), handler::useHandler)
|
||||
.andRoute(RequestPredicates.GET("/functional-reactive/periodic-foo-2"), handler::useHandlerFinite);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> responseHeaderRoute(@Autowired ResponseHeaderHandler handler) {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/functional-response-header/single-handler"), handler::useHandler);
|
||||
}
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user