diff --git a/2025-11/spring-30-endpoints-flow-components/.gitignore b/2025-11/spring-30-endpoints-flow-components/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/.gitignore
@@ -0,0 +1,24 @@
+target/
+
+### 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/
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/pom.xml b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/pom.xml
new file mode 100644
index 00000000..6c20857a
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+
+ ru.otus
+ endpoints-flow-components-exercise
+ 1.0-SNAPSHOT
+
+
+ ru.otus
+ endpoints-flow-components
+ 1.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-integration
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/App.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/App.java
new file mode 100644
index 00000000..f4f51ce9
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/App.java
@@ -0,0 +1,12 @@
+package ru.otus.spring.integration;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+
+@SpringBootApplication
+public class App {
+ public static void main(String[] args) {
+ SpringApplication.run(App.class, args);
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/config/AppRunner.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/config/AppRunner.java
new file mode 100644
index 00000000..f431ff12
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/config/AppRunner.java
@@ -0,0 +1,19 @@
+package ru.otus.spring.integration.config;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+import ru.otus.spring.integration.services.OrderService;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AppRunner implements CommandLineRunner {
+ final OrderService orderService;
+
+ @Override
+ public void run(String... args) {
+ orderService.startGenerateOrdersLoop();
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/config/IntegrationConfig.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/config/IntegrationConfig.java
new file mode 100644
index 00000000..3abcf8de
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/config/IntegrationConfig.java
@@ -0,0 +1,32 @@
+package ru.otus.spring.integration.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.dsl.MessageChannelSpec;
+import org.springframework.integration.dsl.MessageChannels;
+
+@SuppressWarnings("unused")
+@Configuration
+public class IntegrationConfig {
+ @Bean
+ public MessageChannelSpec, ?> itemsChannel() {
+ return MessageChannels.queue(10);
+ }
+
+ @Bean
+ public MessageChannelSpec, ?> foodChannel() {
+ return MessageChannels.publishSubscribe();
+ }
+
+ // TODO: create default poller
+
+ @Bean
+ public IntegrationFlow cafeFlow() {
+ return IntegrationFlow.from(itemsChannel())
+ // TODO: cook OrderItem in the kitchen
+ // TODO*: add splitter and aggregator
+ // TODO: forward it to the publish subscriber channel
+ .get();
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/domain/Food.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/domain/Food.java
new file mode 100644
index 00000000..f27d5047
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/domain/Food.java
@@ -0,0 +1,6 @@
+package ru.otus.spring.integration.domain;
+
+
+public record Food(String name) {
+
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/domain/OrderItem.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/domain/OrderItem.java
new file mode 100644
index 00000000..69d9da98
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/domain/OrderItem.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.integration.domain;
+
+public record OrderItem(String itemName) {
+
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/CafeGateway.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/CafeGateway.java
new file mode 100644
index 00000000..58c24077
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/CafeGateway.java
@@ -0,0 +1,12 @@
+package ru.otus.spring.integration.services;
+
+
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+// TODO: add messaging gateway annotation
+public interface CafeGateway {
+
+ // TODO: add gateway annotation with required channels
+ Food process(OrderItem orderItem);
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/KitchenService.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/KitchenService.java
new file mode 100644
index 00000000..2ae92af2
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/KitchenService.java
@@ -0,0 +1,9 @@
+package ru.otus.spring.integration.services;
+
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+public interface KitchenService {
+
+ Food cook(OrderItem orderItem);
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/KitchenServiceImpl.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/KitchenServiceImpl.java
new file mode 100644
index 00000000..08d3ddc2
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/KitchenServiceImpl.java
@@ -0,0 +1,28 @@
+package ru.otus.spring.integration.services;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+@Service
+@Slf4j
+public class KitchenServiceImpl implements KitchenService {
+
+ @Override
+ public Food cook(OrderItem orderItem) {
+ log.info("Cooking {}", orderItem.itemName());
+ delay();
+ log.info("Cooking {} done", orderItem.itemName());
+
+ return new Food(orderItem.itemName());
+ }
+
+ private static void delay() {
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/OrderService.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/OrderService.java
new file mode 100644
index 00000000..c5da0ac9
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/OrderService.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.integration.services;
+
+public interface OrderService {
+ void startGenerateOrdersLoop();
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/OrderServiceImpl.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/OrderServiceImpl.java
new file mode 100644
index 00000000..dae05628
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/java/ru/otus/spring/integration/services/OrderServiceImpl.java
@@ -0,0 +1,43 @@
+package ru.otus.spring.integration.services;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.stereotype.Service;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+@Service
+@Slf4j
+public class OrderServiceImpl implements OrderService {
+ private static final String[] MENU = {"coffee", "tea", "smoothie", "whiskey", "beer", "cola", "water"};
+
+ private final CafeGateway cafe;
+
+ public OrderServiceImpl(CafeGateway cafe) {
+ this.cafe = cafe;
+ }
+
+ @Override
+ public void startGenerateOrdersLoop() {
+ for (int i = 0; i < 10; i++) {
+ OrderItem orderItem = generateOrderItem();
+ int num = i + 1;
+ log.info("{}, New orderItem: {}", num, orderItem.itemName());
+ Food food = cafe.process(orderItem);
+ log.info("{}, Ready food: {}", num, food.name());
+ delay();
+ }
+ }
+
+ private static OrderItem generateOrderItem() {
+ return new OrderItem(MENU[RandomUtils.nextInt(0, MENU.length)]);
+ }
+
+ private void delay() {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/resources/application.yml b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/resources/application.yml
new file mode 100644
index 00000000..1a9aeb48
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-exercise/src/main/resources/application.yml
@@ -0,0 +1,5 @@
+logging:
+ level:
+ root: info
+
+ org.springframework.integration: debug
\ No newline at end of file
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/pom.xml b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/pom.xml
new file mode 100644
index 00000000..fd184690
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ ru.otus
+ endpoints-flow-components-solution
+ 1.0-SNAPSHOT
+
+
+ ru.otus
+ endpoints-flow-components
+ 1.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-integration
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ ru.otus.spring.integration.App
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${checkstyle-plugin.version}
+
+
+ com.puppycrawl.tools
+ checkstyle
+ ${checkstyle.version}
+
+
+
+ ${checkstyle.config.url}
+
+
+
+
+ check
+
+
+
+
+
+
+
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/App.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/App.java
new file mode 100644
index 00000000..1b68561b
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/App.java
@@ -0,0 +1,14 @@
+package ru.otus.spring.integration;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@Slf4j
+@SpringBootApplication
+public class App {
+
+ public static void main(String[] args) {
+ SpringApplication.run(App.class, args);
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/config/AppRunner.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/config/AppRunner.java
new file mode 100644
index 00000000..f431ff12
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/config/AppRunner.java
@@ -0,0 +1,19 @@
+package ru.otus.spring.integration.config;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+import ru.otus.spring.integration.services.OrderService;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AppRunner implements CommandLineRunner {
+ final OrderService orderService;
+
+ @Override
+ public void run(String... args) {
+ orderService.startGenerateOrdersLoop();
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/config/IntegrationConfig.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/config/IntegrationConfig.java
new file mode 100644
index 00000000..59d9a13b
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/config/IntegrationConfig.java
@@ -0,0 +1,38 @@
+package ru.otus.spring.integration.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.dsl.*;
+import org.springframework.integration.scheduling.PollerMetadata;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.services.KitchenService;
+
+@Configuration
+public class IntegrationConfig {
+
+ @Bean
+ public MessageChannelSpec, ?> itemsChannel() {
+ return MessageChannels.queue(10);
+ }
+
+ @Bean
+ public MessageChannelSpec, ?> foodChannel() {
+ return MessageChannels.publishSubscribe();
+ }
+
+ @Bean(name = PollerMetadata.DEFAULT_POLLER)
+ public PollerSpec poller() {
+ return Pollers.fixedRate(100).maxMessagesPerPoll(2);
+ }
+
+ @Bean
+ public IntegrationFlow cafeFlow(KitchenService kitchenService) {
+ return IntegrationFlow.from(itemsChannel())
+ .split()
+ .handle(kitchenService, "cook")
+ .transform(f -> new Food(f.name().toUpperCase()))
+ .aggregate()
+ .channel(foodChannel())
+ .get();
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/domain/Food.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/domain/Food.java
new file mode 100644
index 00000000..f27d5047
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/domain/Food.java
@@ -0,0 +1,6 @@
+package ru.otus.spring.integration.domain;
+
+
+public record Food(String name) {
+
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/domain/OrderItem.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/domain/OrderItem.java
new file mode 100644
index 00000000..69d9da98
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/domain/OrderItem.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.integration.domain;
+
+public record OrderItem(String itemName) {
+
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/CafeGateway.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/CafeGateway.java
new file mode 100644
index 00000000..85cae973
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/CafeGateway.java
@@ -0,0 +1,16 @@
+package ru.otus.spring.integration.services;
+
+
+import org.springframework.integration.annotation.Gateway;
+import org.springframework.integration.annotation.MessagingGateway;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+import java.util.Collection;
+
+@MessagingGateway
+public interface CafeGateway {
+
+ @Gateway(requestChannel = "itemsChannel", replyChannel = "foodChannel")
+ Collection process(Collection orderItem);
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/KitchenService.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/KitchenService.java
new file mode 100644
index 00000000..2ae92af2
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/KitchenService.java
@@ -0,0 +1,9 @@
+package ru.otus.spring.integration.services;
+
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+public interface KitchenService {
+
+ Food cook(OrderItem orderItem);
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/KitchenServiceImpl.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/KitchenServiceImpl.java
new file mode 100644
index 00000000..8ad7c653
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/KitchenServiceImpl.java
@@ -0,0 +1,27 @@
+package ru.otus.spring.integration.services;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+@Service
+@Slf4j
+public class KitchenServiceImpl implements KitchenService {
+
+ @Override
+ public Food cook(OrderItem orderItem) {
+ log.info("Cooking {}", orderItem.itemName());
+ delay();
+ log.info("Cooking {} done", orderItem.itemName());
+ return new Food(orderItem.itemName());
+ }
+
+ private static void delay() {
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/OrderService.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/OrderService.java
new file mode 100644
index 00000000..c5da0ac9
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/OrderService.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.integration.services;
+
+public interface OrderService {
+ void startGenerateOrdersLoop();
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/OrderServiceImpl.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/OrderServiceImpl.java
new file mode 100644
index 00000000..d3a85e7e
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/integration/services/OrderServiceImpl.java
@@ -0,0 +1,65 @@
+package ru.otus.spring.integration.services;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.stereotype.Service;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ForkJoinPool;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+public class OrderServiceImpl implements OrderService {
+ private static final String[] MENU = {"coffee", "tea", "smoothie", "whiskey", "beer", "cola", "water"};
+
+ private final CafeGateway cafe;
+
+ public OrderServiceImpl(CafeGateway cafe) {
+ this.cafe = cafe;
+ }
+
+ @Override
+ public void startGenerateOrdersLoop() {
+ ForkJoinPool pool = ForkJoinPool.commonPool();
+ for (int i = 0; i < 10; i++) {
+ int num = i + 1;
+ pool.execute(() -> {
+ Collection items = generateOrderItems();
+ log.info("{}, New orderItems: {}", num,
+ items.stream().map(OrderItem::itemName)
+ .collect(Collectors.joining(",")));
+ Collection food = cafe.process(items);
+ log.info("{}, Ready food: {}", num, food.stream()
+ .map(Food::name)
+ .collect(Collectors.joining(",")));
+ });
+ delay();
+ }
+ }
+
+ private static OrderItem generateOrderItem() {
+ return new OrderItem(MENU[RandomUtils.nextInt(0, MENU.length)]);
+ }
+
+ private static Collection generateOrderItems() {
+ List items = new ArrayList<>();
+ for (int i = 0; i < RandomUtils.nextInt(1, 5); ++i) {
+ items.add(generateOrderItem());
+ }
+ return items;
+ }
+
+ private void delay() {
+ try {
+ Thread.sleep(7000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/bridge/BridgeApp.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/bridge/BridgeApp.java
new file mode 100644
index 00000000..ba8b27d9
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/bridge/BridgeApp.java
@@ -0,0 +1,58 @@
+package ru.otus.spring.test.bridge;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.annotation.Gateway;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.dsl.MessageChannels;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+@SuppressWarnings("unused")
+@SpringBootApplication
+@IntegrationComponentScan
+@Slf4j
+public class BridgeApp {
+ public static void main(String[] args) {
+ ConfigurableApplicationContext ctx = SpringApplication.run(BridgeApp.class, args);
+ Map channels = ctx.getBeansOfType(MessageChannel.class);
+ log.warn("CHANNELS:");
+ int i = 0;
+ for (Map.Entry entry : channels.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+ log.warn("HANDLERS:");
+ i = 0;
+ Map endpoints = ctx.getBeansOfType(MessageHandler.class);
+ for (Map.Entry entry : endpoints.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+
+ Bridge bean = ctx.getBean(Bridge.class);
+ List strings = List.of("TEST1", "end");
+ Collection result = bean.send(strings);
+ log.warn("Bridge send: {}, receive: {}", strings, result);
+ }
+
+
+ @MessagingGateway
+ public interface Bridge {
+ @Gateway(requestChannel = "flow.input"/*, replyChannel = "p2pChannel"*/)
+ Collection send(Collection strings);
+ }
+
+ @Bean
+ public IntegrationFlow flow() {
+ return f -> f
+ .channel(MessageChannels.queue("p2pChannel", 10));
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/gateway/GatewayApp.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/gateway/GatewayApp.java
new file mode 100644
index 00000000..74666244
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/gateway/GatewayApp.java
@@ -0,0 +1,76 @@
+package ru.otus.spring.test.gateway;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.annotation.Gateway;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+@SuppressWarnings("unused")
+@SpringBootApplication
+@IntegrationComponentScan
+@Slf4j
+public class GatewayApp {
+ public static void main(String[] args) {
+ ConfigurableApplicationContext ctx = SpringApplication.run(GatewayApp.class, args);
+ Map channels = ctx.getBeansOfType(MessageChannel.class);
+ log.warn("CHANNELS:");
+ int i = 0;
+ for (Map.Entry entry : channels.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+ log.warn("HANDLERS:");
+ i = 0;
+ Map endpoints = ctx.getBeansOfType(MessageHandler.class);
+ for (Map.Entry entry : endpoints.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+ Upcase upcase = ctx.getBean(Upcase.class);
+ Collection result = upcase.upcase(Arrays.asList("test", "new", "last"));
+ log.warn("Upcase result: {}", result);
+
+ }
+
+ @MessagingGateway
+ public interface Upcase {
+ @Gateway(requestChannel = "upcase.input")
+ Collection upcase(Collection strings);
+ }
+
+ @Bean
+ public IntegrationFlow upcase() {
+ return f -> f//.channel("from-input-to-split")
+ .split()
+// .split(list -> list.getObject().spliterator())
+// .split(getCustomSplitter(), "split")
+ .channel("from-split-to-transformer")
+ .transform(String::toUpperCase)
+ .channel("from-transformer-to-aggregate")
+ .aggregate();
+// .>filter(source -> source.stream().anyMatch(s -> s.startsWith("a")))
+ }
+
+
+// @Bean
+// CustomSplitter getCustomSplitter() {
+// return new CustomSplitter();
+// }
+//
+// public static class CustomSplitter {
+// public Collection split(Message> message) {
+// return message.getPayload().stream().skip(1).collect(Collectors.toList());
+// }
+// }
+
+
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/polling/PollingApp.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/polling/PollingApp.java
new file mode 100644
index 00000000..8aa04973
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/polling/PollingApp.java
@@ -0,0 +1,92 @@
+package ru.otus.spring.test.polling;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.annotation.Gateway;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.dsl.MessageChannelSpec;
+import org.springframework.integration.dsl.MessageChannels;
+import org.springframework.integration.endpoint.PollingConsumer;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+
+import java.util.Map;
+
+import static java.lang.Thread.sleep;
+
+@SuppressWarnings("unused")
+@SpringBootApplication
+@IntegrationComponentScan
+@Slf4j
+public class PollingApp {
+ public static void main(String[] args) throws InterruptedException {
+ ConfigurableApplicationContext ctx = SpringApplication.run(PollingApp.class, args);
+ log.warn("POLLER:");
+ Map pollers = ctx.getBeansOfType(PollingConsumer.class);
+ int i = 0;
+ for (Map.Entry entry : pollers.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+ log.warn("CHANNELS:");
+ Map channels = ctx.getBeansOfType(MessageChannel.class);
+ i = 0;
+ for (Map.Entry entry : channels.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+ log.warn("HANDLERS:");
+ i = 0;
+ Map endpoints = ctx.getBeansOfType(MessageHandler.class);
+ for (Map.Entry entry : endpoints.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+
+ Polling polling = ctx.getBean(Polling.class);
+ String result = polling.send("test");
+ log.warn("Polling result: {}", result);
+
+ sleep(5000);
+ ctx.close();
+ }
+
+ @MessagingGateway
+ public interface Polling {
+ @Gateway(requestChannel = "flow.input", replyChannel = "pubSub")
+ String send(String value);
+ }
+
+// @Bean(name = PollerMetadata.DEFAULT_POLLER)
+// public PollerMetadata defaultPoller() {
+
+ /// / return Pollers.fixedRate(10_000).get();
+// PollerMetadata pollerMetadata = new PollerMetadata();
+// pollerMetadata.setMaxMessagesPerPoll(5);
+// pollerMetadata.setTrigger(new PeriodicTrigger(Duration.ofSeconds(3)));
+// return pollerMetadata;
+// }
+ @Bean
+ public IntegrationFlow flow() {
+ return f -> f
+ .channel("pubSub")
+ .channel("p2p");
+ }
+
+ @Bean
+ public MessageChannelSpec, ?> p2p() {
+ return MessageChannels.queue("p2p", 10);
+ }
+
+ @Bean
+ public MessageChannelSpec, ?> p2p2() {
+ return MessageChannels.priority("p2p2").capacity(10);
+ }
+
+ @Bean
+ public MessageChannelSpec, ?> pubSub() {
+ return MessageChannels.publishSubscribe("pubSub");
+ }
+}
diff --git a/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/transform/TransformApp.java b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/transform/TransformApp.java
new file mode 100644
index 00000000..c47be87b
--- /dev/null
+++ b/2025-11/spring-30-endpoints-flow-components/endpoints-flow-components-solution/src/main/java/ru/otus/spring/test/transform/TransformApp.java
@@ -0,0 +1,91 @@
+package ru.otus.spring.test.transform;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.integration.annotation.Gateway;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.dsl.Transformers;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+@SuppressWarnings("unused")
+@SpringBootApplication
+@IntegrationComponentScan
+@Slf4j
+public class TransformApp {
+ public static void main(String[] args) {
+ ConfigurableApplicationContext ctx = SpringApplication.run(TransformApp.class, args);
+ Map channels = ctx.getBeansOfType(MessageChannel.class);
+ log.warn("CHANNELS:");
+ int i = 0;
+ for (Map.Entry entry : channels.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+ log.warn("HANDLERS:");
+ i = 0;
+ Map endpoints = ctx.getBeansOfType(MessageHandler.class);
+ for (Map.Entry entry : endpoints.entrySet()) {
+ log.warn("{}. {}/{} -> {}", ++i, entry.getKey(), entry.getValue().getClass().getSimpleName(), entry.getValue());
+ }
+ Upcase upcase = ctx.getBean(Upcase.class);
+ Collection- result = upcase.upcase(Arrays.asList(new Item("test"), new Item("new"), new Item("last")));
+// Collection result = upcase.upcase(Arrays.asList("test", "new", "last"));
+ log.warn("Upcase result: {}", result);
+
+ }
+
+ @Getter
+ public static class Item {
+ public Item() {
+ }
+
+ public Item(String value) {
+ this.value = value;
+ }
+
+ String value;
+ String name;
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "Item{" +
+ "value='" + value + '\'' +
+ ", name='" + name + '\'' +
+ '}';
+ }
+ }
+
+ @MessagingGateway
+ public interface Upcase {
+ @Gateway(requestChannel = "upcase.input")
+ Collection
- upcase(Collection
- strings);
+ }
+
+ @Bean
+ public IntegrationFlow upcase() {
+ return f -> f
+ .split()
+ .transform(Transformers.toMap())
+ .