This commit is contained in:
Florian THIERRY
2021-04-30 15:44:28 +02:00
commit df3b76f166
27 changed files with 1052 additions and 0 deletions

137
kafka-producer/pom.xml Normal file
View File

@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ippon.trainning.kafkaintegrationtest</groupId>
<artifactId>kafka-integration-test-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>kafka-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
<jupiter.version>5.7.0</jupiter.version>
<build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
<integration-test.source.directory>src/integration-test/java</integration-test.source.directory>
<integration-test.resources.directory>src/integration-test/resources</integration-test.resources.directory>
<maven-failsafe-plugin.version>2.22.2</maven-failsafe-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.ippon.trainning.kafkaintegrationtest</groupId>
<artifactId>kafka-common-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Mandatory for executable jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
<!-- Tests d'intégrations, ajout des dossiers sources + exécution -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>${build-helper-maven-plugin.version}</version>
<executions>
<execution>
<id>add-integration-test</id>
<goals>
<goal>add-test-source</goal>
<goal>add-test-resource</goal>
</goals>
<configuration>
<sources>
<source>${integration-test.source.directory}</source>
</sources>
<resources>
<resource>
<directory>${integration-test.resources.directory}</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,96 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaproducer.producer;
import com.ippon.trainning.kafkaintegrationtest.kafkacommontest.extension.EmbeddedKafkaExtension;
import com.ippon.trainning.kafkaintegrationtest.kafkacommontest.utils.KafkaTopicListener;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.test.context.EmbeddedKafka;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.HashMap;
import java.util.Map;
import static com.ippon.trainning.kafkaintegrationtest.kafkacommontest.assertor.ConsumerRecordAssert.assertThat;
@ExtendWith({
SpringExtension.class,
EmbeddedKafkaExtension.class
})
@SpringBootTest(classes = {
ProducerIT.ProducerITConfiguration.class
}, properties = "spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}")
@EmbeddedKafka
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ActiveProfiles({"test"})
public class ProducerIT {
@TestConfiguration
public static class ProducerITConfiguration {
@Bean("message-container-factory")
public ConcurrentKafkaListenerContainerFactory<String, String> containerFactory(
@Value("${server.kafka.bootstrapAddress}") String bootstrapAddress
) {
var factory = new ConcurrentKafkaListenerContainerFactory<String, String>();
factory.setConsumerFactory(consumerFactory(bootstrapAddress));
factory.setConcurrency(1);
return factory;
}
private ConsumerFactory<String, String> consumerFactory(String bootstrapAddress) {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
return new DefaultKafkaConsumerFactory<>(props);
}
@Bean
public KafkaTopicListener<String, String> messageKafkaTopicListener() {
return new KafkaTopicListener<>() {
@KafkaListener(
topics = "${server.kafka.topic}",
groupId = "${server.kafka.groupId}",
containerFactory = "message-container-factory"
)
@Override
public void listen(ConsumerRecord<String, String> message) {
super.listen(message);
}
};
}
}
@Autowired
private KafkaProducer producer;
@Autowired
@Qualifier("messageKafkaTopicListener")
private KafkaTopicListener<String, String> messageKafkaTopicListener;
@Test
void should_send_a_message_into_kafka_topic() {
// given
String message = "A test message to send into topic";
// when
producer.sendMessage(message);
// then
ConsumerRecord<String, String> messageConsumed = messageKafkaTopicListener.getMessage();
assertThat(messageConsumed)
.hasValue(message);
}
}

View File

@@ -0,0 +1,5 @@
server:
kafka:
bootstrapAddress: "${spring.embedded.kafka.brokers:}"
topic: topic-test
groupId: groupId-test

View File

@@ -0,0 +1,11 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaproducer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class KafkaProducerApplication {
public static void main(String[] args) {
SpringApplication.run(KafkaProducerApplication.class, args);
}
}

View File

@@ -0,0 +1,37 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaproducer.config;
import org.apache.kafka.common.serialization.StringSerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import java.util.Map;
import static org.apache.kafka.clients.producer.ProducerConfig.*;
@Configuration
public class KafkaProducerConfiguration {
private final String bootstrapAddress;
public KafkaProducerConfiguration(@Value("${server.kafka.bootstrapAddress}") String bootstrapAddress) {
this.bootstrapAddress = bootstrapAddress;
}
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> configProperties = Map.of(
BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress,
KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class,
VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class
);
return new DefaultKafkaProducerFactory<>(configProperties);
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}

View File

@@ -0,0 +1,30 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaproducer.controller;
import com.ippon.trainning.kafkaintegrationtest.kafkaproducer.producer.KafkaProducer;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
@RestController
public class MessageController {
private final KafkaProducer kafkaProducer;
public MessageController(KafkaProducer kafkaProducer) {
this.kafkaProducer = kafkaProducer;
}
@PostMapping("/messages")
public ResponseEntity<String> sendMessage(@RequestBody String message) {
ResponseEntity<String> response;
try {
kafkaProducer.sendMessage(message);
response = ResponseEntity.ok("Message sent.");
} catch(Exception ex) {
response = ResponseEntity.status(INTERNAL_SERVER_ERROR).body("Message no sent.");
}
return response;
}
}

View File

@@ -0,0 +1,23 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaproducer.producer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
public class KafkaProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
private final String topic;
public KafkaProducer(KafkaTemplate<String, String> kafkaTemplate,
@Value("${server.kafka.topic}") String topic) {
this.kafkaTemplate = kafkaTemplate;
this.topic = topic;
}
public void sendMessage(String message) {
kafkaTemplate.send(topic, UUID.randomUUID().toString(), message);
}
}

View File

@@ -0,0 +1,5 @@
server:
port: 8082
kafka:
bootstrapAddress: "localhost:9092"
topic: topic-test

View File

@@ -0,0 +1,13 @@
package com.ippon.trainning.kafkaintegrationtest.kafkaproducer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class KafkaProducerApplicationTests {
@Test
void contextLoads() {
}
}