diff --git a/spring-reactive-modules/pom.xml b/spring-reactive-modules/pom.xml index 61a3c3d17d..a16046ee48 100644 --- a/spring-reactive-modules/pom.xml +++ b/spring-reactive-modules/pom.xml @@ -28,6 +28,7 @@ spring-reactive-data-couchbase spring-reactive spring-reactive-exceptions + spring-reactor spring-webflux-amqp diff --git a/spring-reactive-modules/spring-reactive-performance/.gitignore b/spring-reactive-modules/spring-reactive-performance/.gitignore new file mode 100644 index 0000000000..82eca336e3 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/README.md b/spring-reactive-modules/spring-reactive-performance/README.md new file mode 100644 index 0000000000..16aac3d419 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/README.md @@ -0,0 +1,3 @@ +## Spring Reactive Performance + +This module contains articles about reactive Spring Boot. diff --git a/spring-reactive-modules/spring-reactive-performance/pom.xml b/spring-reactive-modules/spring-reactive-performance/pom.xml new file mode 100644 index 0000000000..d7a69560dd --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + com.baeldung.spring + spring-reactive-performance + 1.0.0-SNAPSHOT + spring-reactive-performance + jar + Spring Reactive Performance + + + com.baeldung.spring.reactive + spring-reactive-modules + 1.0.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 21 + 21 + + false + + + + org.apache.maven.plugins + maven-surefire-plugin + + --enable-preview + + + + + + + 21 + 3.2.0 + + \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/Application.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/Application.java new file mode 100644 index 0000000000..284ddfce30 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/Application.java @@ -0,0 +1,13 @@ +package com.baeldung.spring.reactive.performance; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/KafkaTemplate.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/KafkaTemplate.java new file mode 100644 index 0000000000..cf8684690e --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/KafkaTemplate.java @@ -0,0 +1,14 @@ +package com.baeldung.spring.reactive.performance; + +import java.util.concurrent.CompletableFuture; + +public class KafkaTemplate { + + // For simplicity in this example and article, an actual Kafka client isn't utilized. + // The focus remains on demonstrating the basic principles without the complexities of a full Kafka client setup. + + public CompletableFuture send(String topic, K key, V value) { + System.out.println("Sending message to topic: " + topic + " with value: " + value); + return CompletableFuture.completedFuture(null); + } +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/ProductAddedToCartEvent.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/ProductAddedToCartEvent.java new file mode 100644 index 0000000000..08743d109c --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/ProductAddedToCartEvent.java @@ -0,0 +1,6 @@ +package com.baeldung.spring.reactive.performance; + +import java.math.BigDecimal; + +public record ProductAddedToCartEvent(String productId, BigDecimal price, String currency, String cartId) { +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Price.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Price.java new file mode 100644 index 0000000000..271ca26c96 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Price.java @@ -0,0 +1,9 @@ +package com.baeldung.spring.reactive.performance.model; + +import java.math.BigDecimal; + +public record Price(BigDecimal value, String currency) { + public Price applyDiscount(BigDecimal discount) { + return new Price(value.subtract(discount), currency); + } +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Product.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Product.java new file mode 100644 index 0000000000..d4aa751089 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/model/Product.java @@ -0,0 +1,43 @@ +package com.baeldung.spring.reactive.performance.model; + +import java.math.BigDecimal; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document(collation = "products") +public record Product ( + @Id + String id, + String name, + BigDecimal basePriceValue, + String currency, + Category category +) { + + public Price basePrice() { + return new Price(basePriceValue, currency); + } + + public enum Category { + ELECTRONICS(false), + CLOTHING(true), + ACCESSORIES(false), + GARDENING(false), + SPORTS(true); + + private final boolean eligibleForPromotion; + + Category(boolean eligibleForPromotion) { + this.eligibleForPromotion = eligibleForPromotion; + } + + public boolean isEligibleForDiscount() { + return eligibleForPromotion; + } + + public boolean notEligibleForPromotion() { + return !eligibleForPromotion; + } + } +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/DiscountService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/DiscountService.java new file mode 100644 index 0000000000..89554396b2 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/DiscountService.java @@ -0,0 +1,11 @@ +package com.baeldung.spring.reactive.performance.virtualthreads; + +import java.math.BigDecimal; + +class DiscountService { + + public BigDecimal discountForProduct(String productId) { + return BigDecimal.valueOf(10); + } + +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductRepository.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductRepository.java new file mode 100644 index 0000000000..f0ebff5cfd --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductRepository.java @@ -0,0 +1,8 @@ +package com.baeldung.spring.reactive.performance.virtualthreads; + +import org.springframework.data.mongodb.repository.MongoRepository; + +import com.baeldung.spring.reactive.performance.model.Product; + +interface ProductRepository extends MongoRepository { +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductService.java new file mode 100644 index 0000000000..5395770d7e --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/virtualthreads/ProductService.java @@ -0,0 +1,47 @@ +package com.baeldung.spring.reactive.performance.virtualthreads; + +import java.math.BigDecimal; + +import com.baeldung.spring.reactive.performance.KafkaTemplate; +import com.baeldung.spring.reactive.performance.ProductAddedToCartEvent; +import com.baeldung.spring.reactive.performance.model.Price; +import com.baeldung.spring.reactive.performance.model.Product; + +class ProductService { + private final String PRODUCT_ADDED_TO_CART_TOPIC = "product-added-to-cart"; + + private final ProductRepository repository; + private final DiscountService discountService; + private final KafkaTemplate kafkaTemplate; + + public ProductService(ProductRepository repository, DiscountService discountService) { + this.repository = repository; + this.discountService = discountService; + this.kafkaTemplate = new KafkaTemplate<>(); + } + + public void addProductToCart(String productId, String cartId) { + Thread.startVirtualThread(() -> computePriceAndPublishMessage(productId, cartId)); + } + + private void computePriceAndPublishMessage(String productId, String cartId) { + Product product = repository.findById(productId) + .orElseThrow(() -> new IllegalArgumentException("not found!")); + + Price price = computePrice(productId, product); + + var event = new ProductAddedToCartEvent(productId, price.value(), price.currency(), cartId); + kafkaTemplate.send(PRODUCT_ADDED_TO_CART_TOPIC, cartId, event); + } + + private Price computePrice(String productId, Product product) { + if (product.category() + .isEligibleForDiscount()) { + BigDecimal discount = discountService.discountForProduct(productId); + return product.basePrice() + .applyDiscount(discount); + } + return product.basePrice(); + } + +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/DiscountService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/DiscountService.java new file mode 100644 index 0000000000..ebe6b9a8a4 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/DiscountService.java @@ -0,0 +1,11 @@ +package com.baeldung.spring.reactive.performance.webflux; + +import java.math.BigDecimal; + +import reactor.core.publisher.Mono; + +class DiscountService { + public Mono discountForProduct(String productId) { + return Mono.just(BigDecimal.valueOf(10)); + } +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductRepository.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductRepository.java new file mode 100644 index 0000000000..5ab4497a27 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductRepository.java @@ -0,0 +1,8 @@ +package com.baeldung.spring.reactive.performance.webflux; + +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; + +import com.baeldung.spring.reactive.performance.model.Product; + +interface ProductRepository extends ReactiveMongoRepository { +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductService.java b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductService.java new file mode 100644 index 0000000000..0e950b501e --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/java/com/baeldung/spring/reactive/performance/webflux/ProductService.java @@ -0,0 +1,40 @@ +package com.baeldung.spring.reactive.performance.webflux; + +import com.baeldung.spring.reactive.performance.KafkaTemplate; +import com.baeldung.spring.reactive.performance.ProductAddedToCartEvent; +import com.baeldung.spring.reactive.performance.model.Price; +import com.baeldung.spring.reactive.performance.model.Product; + +import reactor.core.publisher.Mono; + +class ProductService { + private final String PRODUCT_ADDED_TO_CART_TOPIC = "product-added-to-cart"; + + private final ProductRepository repository; + private final DiscountService discountService; + private final KafkaTemplate kafkaTemplate; + + public ProductService(ProductRepository repository, DiscountService discountService) { + this.repository = repository; + this.discountService = discountService; + this.kafkaTemplate = new KafkaTemplate<>(); + } + + public void addProductToCart(String productId, String cartId) { + repository.findById(productId) + .switchIfEmpty(Mono.error(() -> new IllegalArgumentException("not found!"))) + .flatMap(this::computePrice) + .map(price -> new ProductAddedToCartEvent(productId, price.value(), price.currency(), cartId)) + .subscribe(event -> kafkaTemplate.send(PRODUCT_ADDED_TO_CART_TOPIC, cartId, event)); + } + + private Mono computePrice(Product product) { + if (product.category() + .isEligibleForDiscount()) { + return discountService.discountForProduct(product.id()) + .map(product.basePrice()::applyDiscount); + } + return Mono.just(product.basePrice()); + } + +} \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/resources/application.yml b/spring-reactive-modules/spring-reactive-performance/src/main/resources/application.yml new file mode 100644 index 0000000000..335ae17af1 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/resources/application.yml @@ -0,0 +1,5 @@ + + + + + diff --git a/spring-reactive-modules/spring-reactive-performance/src/main/resources/logback.xml b/spring-reactive-modules/spring-reactive-performance/src/main/resources/logback.xml new file mode 100644 index 0000000000..7d900d8ea8 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/spring-reactive-modules/spring-reactive-performance/src/test/java/com/baeldung/spring/reactive/performance/ApplicationUnitTest.java b/spring-reactive-modules/spring-reactive-performance/src/test/java/com/baeldung/spring/reactive/performance/ApplicationUnitTest.java new file mode 100644 index 0000000000..df69e7eb72 --- /dev/null +++ b/spring-reactive-modules/spring-reactive-performance/src/test/java/com/baeldung/spring/reactive/performance/ApplicationUnitTest.java @@ -0,0 +1,12 @@ +package com.baeldung.spring.reactive.performance; + +import org.junit.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class ApplicationUnitTest { + + @Test + public void whenSpringContextIsBootstrapped_thenNoExceptions() { + } +} diff --git a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/AmqpReactiveController.java b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/AmqpReactiveController.java similarity index 99% rename from spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/AmqpReactiveController.java rename to spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/AmqpReactiveController.java index b71c32bd05..6249c280d7 100644 --- a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/AmqpReactiveController.java +++ b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/AmqpReactiveController.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.amqp; +package com.baeldung.spring.reactive.performance; import java.time.Duration; diff --git a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/DestinationsConfig.java b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/DestinationsConfig.java similarity index 96% rename from spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/DestinationsConfig.java rename to spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/DestinationsConfig.java index 0f9a0d890f..2e830dba01 100644 --- a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/DestinationsConfig.java +++ b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/DestinationsConfig.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.amqp; +package com.baeldung.spring.reactive.performance; import java.util.HashMap; import java.util.Map; diff --git a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/MessageListenerContainerFactory.java b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/MessageListenerContainerFactory.java similarity index 94% rename from spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/MessageListenerContainerFactory.java rename to spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/MessageListenerContainerFactory.java index d868e6afa8..b1735ab205 100644 --- a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/MessageListenerContainerFactory.java +++ b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/MessageListenerContainerFactory.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.amqp; +package com.baeldung.spring.reactive.performance; import org.springframework.amqp.core.AcknowledgeMode; import org.springframework.amqp.rabbit.connection.ConnectionFactory; diff --git a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/SpringWebfluxAmqpApplication.java b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/SpringWebfluxAmqpApplication.java similarity index 95% rename from spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/SpringWebfluxAmqpApplication.java rename to spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/SpringWebfluxAmqpApplication.java index d6a7a84d30..73747ccc43 100644 --- a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/SpringWebfluxAmqpApplication.java +++ b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/reactive/performance/SpringWebfluxAmqpApplication.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.amqp; +package com.baeldung.spring.reactive.performance; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/amqp/SpringWebfluxAmqpLiveTest.java b/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/reactive/performance/SpringWebfluxAmqpLiveTest.java similarity index 91% rename from spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/amqp/SpringWebfluxAmqpLiveTest.java rename to spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/reactive/performance/SpringWebfluxAmqpLiveTest.java index 81782ce575..1222bb4569 100644 --- a/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/amqp/SpringWebfluxAmqpLiveTest.java +++ b/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/reactive/performance/SpringWebfluxAmqpLiveTest.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.amqp; +package com.baeldung.spring.reactive.performance; import org.junit.Test; import org.springframework.test.web.reactive.server.WebTestClient;