diff --git a/2020-08/spring-27/.gitignore b/2020-08/spring-27/.gitignore
new file mode 100644
index 00000000..4ea52072
--- /dev/null
+++ b/2020-08/spring-27/.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/2020-08/spring-27/pom.xml b/2020-08/spring-27/pom.xml
new file mode 100644
index 00000000..38000780
--- /dev/null
+++ b/2020-08/spring-27/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-27
+ 1.0
+
+ pom
+
+
+ spring-27-exercise
+ spring-27-solution
+
+
diff --git a/2020-08/spring-27/spring-27-exercise/pom.xml b/2020-08/spring-27/spring-27-exercise/pom.xml
new file mode 100644
index 00000000..a65ae37e
--- /dev/null
+++ b/2020-08/spring-27/spring-27-exercise/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-27-exercise
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.3.RELEASE
+
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-integration
+
+
+ org.springframework
+ spring-messaging
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2020-08/spring-27/spring-27-exercise/src/main/java/ru/otus/spring/integration/App.java b/2020-08/spring-27/spring-27-exercise/src/main/java/ru/otus/spring/integration/App.java
new file mode 100644
index 00000000..d4379d7e
--- /dev/null
+++ b/2020-08/spring-27/spring-27-exercise/src/main/java/ru/otus/spring/integration/App.java
@@ -0,0 +1,50 @@
+package ru.otus.spring.integration;
+
+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.IntegrationComponentScan;
+import org.springframework.integration.channel.QueueChannel;
+import org.springframework.integration.dsl.MessageChannels;
+import org.springframework.messaging.PollableChannel;
+import org.springframework.messaging.SubscribableChannel;
+import org.springframework.messaging.support.MessageBuilder;
+
+@SpringBootApplication
+@IntegrationComponentScan
+public class App {
+
+ public static void main( String[] args ) throws InterruptedException {
+ ConfigurableApplicationContext ctx = SpringApplication.run( App.class, args );
+
+ PollableChannel channel1 = ctx.getBean( "channel1", PollableChannel.class );
+ SubscribableChannel channel2 = ctx.getBean( "channel2", SubscribableChannel.class );
+
+ channel2.subscribe( System.out::println );
+ new Thread( () -> {
+ while ( true ) {
+ channel2.send( channel1.receive() );
+ }
+ } ).start();
+
+ channel1.send( MessageBuilder.withPayload( "Hello" ).build() );
+ channel1.send( MessageBuilder.withPayload( "Hello2" ).build() );
+
+ Thread.sleep( 2000 );
+
+ channel1.send( MessageBuilder.withPayload( "Hello3" ).build() );
+
+ Thread.sleep( 100000 );
+ }
+
+ @Bean
+ public PollableChannel channel1() {
+ return new QueueChannel( 100 );
+ }
+
+ @Bean
+ public SubscribableChannel channel2() {
+ return MessageChannels.direct( "channel2" ).get();
+ }
+}
diff --git a/2020-08/spring-27/spring-27-exercise/src/test/java/ru/otus/spring/integration/MessagesTest.java b/2020-08/spring-27/spring-27-exercise/src/test/java/ru/otus/spring/integration/MessagesTest.java
new file mode 100644
index 00000000..d973dfe3
--- /dev/null
+++ b/2020-08/spring-27/spring-27-exercise/src/test/java/ru/otus/spring/integration/MessagesTest.java
@@ -0,0 +1,97 @@
+package ru.otus.spring.integration;
+
+import org.junit.Test;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageHeaders;
+import org.springframework.messaging.support.ErrorMessage;
+import org.springframework.messaging.support.GenericMessage;
+import org.springframework.messaging.support.MessageBuilder;
+
+import java.util.Collections;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@SuppressWarnings("all")
+public class MessagesTest {
+
+ @Test
+ public void testCreateSimpleGenericMessage() {
+ // TODO: Создайте сообщение с payload-ом "Hello" с помощью конструктора
+ Message message = null;
+
+ assertNotNull(message);
+ assertEquals(GenericMessage.class, message.getClass());
+ assertNotNull(message.getPayload());
+ assertEquals("Hello", message.getPayload());
+ }
+
+ @Test
+ public void testCreateGenericMessage() {
+ // TODO: Создайте сообщение с пользователем с помощью конструктора
+ Message message = null;
+
+ assertNotNull(message);
+ assertEquals(GenericMessage.class, message.getClass());
+ assertNotNull(message.getPayload());
+ assertEquals(new User("John", 23), message.getPayload());
+ }
+
+ @Test
+ public void testGenericMessageWithHeaders() {
+ // TODO: Создайте сообщение с payload-ом "Hello" и header-ом "to":"World"
+ Map headers = null;
+ Message message = null;
+
+ assertNotNull(message);
+ assertEquals("Hello", message.getPayload());
+ assertEquals("World", message.getHeaders().get("to", String.class));
+ }
+
+ @Test
+ public void testGenericMessageWithMessageHeaders() {
+ // TODO: Создайте сообщение с payload-ом "Hello" и header-ом "to":"World"
+ MessageHeaders headers = null;
+ Message message = null;
+
+ assertNotNull(message);
+ assertEquals("Hello", message.getPayload());
+ assertEquals("World", message.getHeaders().get("to", String.class));
+ }
+
+ @Test
+ public void testErrorMessage() {
+ // TODO: Создайте сообщение об ошибки с объектом NullPointerException внутри
+ Message errorMessage = null;
+
+ assertNotNull(errorMessage);
+ assertEquals(ErrorMessage.class, errorMessage.getClass());
+ assertEquals(NullPointerException.class, errorMessage.getPayload().getClass());
+ }
+
+ @Test
+ public void testMessageBuilder() {
+ // TODO: Создайте сообщение с payload-ом "Hello" и header-ом "to":"World" с помощью MessageBuilder
+ Message message = null;
+
+ assertNotNull(message);
+ assertEquals("Hello", message.getPayload());
+ assertEquals("World", message.getHeaders().get("to", String.class));
+ }
+
+ @Test
+ public void testBuildFromMessage() {
+ Message original = MessageBuilder
+ .withPayload(new User("Kate", 30))
+ .setHeader("processor", "userService")
+ .build();
+
+ // TODO: Создайте новое сообщение с теми же payload и header-ами c помощью MessageBuilder
+ Message newMessage = null;
+
+ assertNotNull(newMessage);
+ assertEquals(original.getPayload(), newMessage.getPayload());
+ assertEquals(original.getHeaders().get("processor"), newMessage.getHeaders().get("processor"));
+ }
+}
diff --git a/2020-08/spring-27/spring-27-exercise/src/test/java/ru/otus/spring/integration/User.java b/2020-08/spring-27/spring-27-exercise/src/test/java/ru/otus/spring/integration/User.java
new file mode 100644
index 00000000..e0ebbfa4
--- /dev/null
+++ b/2020-08/spring-27/spring-27-exercise/src/test/java/ru/otus/spring/integration/User.java
@@ -0,0 +1,36 @@
+package ru.otus.spring.integration;
+
+import java.util.Objects;
+
+public class User {
+
+ private final String name;
+ private final int age;
+
+ public User(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof User)) return false;
+ User user = (User) o;
+ return age == user.age &&
+ Objects.equals(name, user.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, age);
+ }
+}
diff --git a/2020-08/spring-27/spring-27-solution/pom.xml b/2020-08/spring-27/spring-27-solution/pom.xml
new file mode 100644
index 00000000..daea33bf
--- /dev/null
+++ b/2020-08/spring-27/spring-27-solution/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-27-solution
+ 1.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.3.RELEASE
+
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-integration
+
+
+ org.springframework
+ spring-messaging
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2020-08/spring-27/spring-27-solution/src/main/java/ru/otus/spring/integration/App.java b/2020-08/spring-27/spring-27-solution/src/main/java/ru/otus/spring/integration/App.java
new file mode 100644
index 00000000..b1ac9a1b
--- /dev/null
+++ b/2020-08/spring-27/spring-27-solution/src/main/java/ru/otus/spring/integration/App.java
@@ -0,0 +1,52 @@
+package ru.otus.spring.integration;
+
+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.IntegrationComponentScan;
+import org.springframework.integration.channel.QueueChannel;
+
+import org.springframework.integration.dsl.MessageChannels;
+import org.springframework.messaging.PollableChannel;
+import org.springframework.messaging.SubscribableChannel;
+import org.springframework.messaging.support.MessageBuilder;
+
+@SpringBootApplication
+@IntegrationComponentScan
+public class App {
+
+ public static void main(String[] args) throws InterruptedException {
+ ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
+
+ PollableChannel channel1 = ctx.getBean("channel1", PollableChannel.class);
+ SubscribableChannel channel2 = ctx.getBean("channel2", SubscribableChannel.class);
+
+ channel2.subscribe(System.out::println);
+
+ new Thread(() -> {
+ while (true) {
+ channel2.send(channel1.receive());
+ }
+ }).start();
+
+ channel1.send(MessageBuilder.withPayload("Hello").build());
+ channel1.send(MessageBuilder.withPayload("Hello2").build());
+
+ Thread.sleep(2000);
+
+ channel1.send(MessageBuilder.withPayload("Hello3").build());
+
+ Thread.sleep(100000);
+ }
+
+ @Bean
+ public PollableChannel channel1() {
+ return new QueueChannel(100);
+ }
+
+ @Bean
+ public SubscribableChannel channel2() {
+ return MessageChannels.direct("channel2").get();
+ }
+}
diff --git a/2020-08/spring-27/spring-27-solution/src/test/java/ru/otus/spring/integration/MessagesTest.java b/2020-08/spring-27/spring-27-solution/src/test/java/ru/otus/spring/integration/MessagesTest.java
new file mode 100644
index 00000000..67a4e4b2
--- /dev/null
+++ b/2020-08/spring-27/spring-27-solution/src/test/java/ru/otus/spring/integration/MessagesTest.java
@@ -0,0 +1,99 @@
+package ru.otus.spring.integration;
+
+import org.junit.Test;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageHeaders;
+import org.springframework.messaging.support.ErrorMessage;
+import org.springframework.messaging.support.GenericMessage;
+import org.springframework.messaging.support.MessageBuilder;
+
+import java.util.Collections;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@SuppressWarnings("all")
+public class MessagesTest {
+
+ @Test
+ public void testCreateSimpleGenericMessage() {
+ // TODO: Создайте сообщение с payload-ом "Hello" с помощью конструктора
+ Message message = new GenericMessage("Hello");
+
+ assertNotNull(message);
+ assertEquals(GenericMessage.class, message.getClass());
+ assertNotNull(message.getPayload());
+ assertEquals("Hello", message.getPayload());
+ }
+
+ @Test
+ public void testCreateGenericMessage() {
+ // TODO: Создайте сообщение с пользователем с помощью конструктора
+ Message message = new GenericMessage(new User("John", 23));
+
+ assertNotNull(message);
+ assertEquals(GenericMessage.class, message.getClass());
+ assertNotNull(message.getPayload());
+ assertEquals(new User("John", 23), message.getPayload());
+ }
+
+ @Test
+ public void testGenericMessageWithHeaders() {
+ // TODO: Создайте сообщение с payload-ом "Hello" и header-ом "to":"World"
+ Map headers = Collections.singletonMap("to", "World");
+ Message message = new GenericMessage<>("Hello", headers);
+
+ assertNotNull(message);
+ assertEquals("Hello", message.getPayload());
+ assertEquals("World", message.getHeaders().get("to", String.class));
+ }
+
+ @Test
+ public void testGenericMessageWithMessageHeaders() {
+ // TODO: Создайте сообщение с payload-ом "Hello" и header-ом "to":"World"
+ MessageHeaders headers = new MessageHeaders(Collections.singletonMap("to", "World"));
+ Message message = new GenericMessage<>("Hello", headers);
+
+ assertNotNull(message);
+ assertEquals("Hello", message.getPayload());
+ assertEquals("World", message.getHeaders().get("to", String.class));
+ }
+
+ @Test
+ public void testErrorMessage() {
+ // TODO: Создайте сообщение об ошибки с объектом NullPointerException внутри
+ Message errorMessage = new ErrorMessage(new NullPointerException());
+
+ assertNotNull(errorMessage);
+ assertEquals(ErrorMessage.class, errorMessage.getClass());
+ assertEquals(NullPointerException.class, errorMessage.getPayload().getClass());
+ }
+
+ @Test
+ public void testMessageBuilder() {
+ // TODO: Создайте сообщение с payload-ом "Hello" и header-ом "to":"World" с помощью MessageBuilder
+ Message message = MessageBuilder.withPayload("Hello")
+ .setHeader("to", "World")
+ .build();
+
+ assertNotNull(message);
+ assertEquals("Hello", message.getPayload());
+ assertEquals("World", message.getHeaders().get("to", String.class));
+ }
+
+ @Test
+ public void testBuildFromMessage() {
+ Message original = MessageBuilder
+ .withPayload(new User("Kate", 30))
+ .setHeader("processor", "userService")
+ .build();
+
+ // TODO: Создайте новое сообщение с теми же payload и header-ами c помощью MessageBuilder
+ Message newMessage = MessageBuilder.fromMessage(original).build();
+
+ assertNotNull(newMessage);
+ assertEquals(original.getPayload(), newMessage.getPayload());
+ assertEquals(original.getHeaders().get("processor"), newMessage.getHeaders().get("processor"));
+ }
+}
diff --git a/2020-08/spring-27/spring-27-solution/src/test/java/ru/otus/spring/integration/User.java b/2020-08/spring-27/spring-27-solution/src/test/java/ru/otus/spring/integration/User.java
new file mode 100644
index 00000000..e0ebbfa4
--- /dev/null
+++ b/2020-08/spring-27/spring-27-solution/src/test/java/ru/otus/spring/integration/User.java
@@ -0,0 +1,36 @@
+package ru.otus.spring.integration;
+
+import java.util.Objects;
+
+public class User {
+
+ private final String name;
+ private final int age;
+
+ public User(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof User)) return false;
+ User user = (User) o;
+ return age == user.age &&
+ Objects.equals(name, user.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, age);
+ }
+}