diff --git a/spring-rabbitmq/.docker/docker-compose.yaml b/spring-rabbitmq/.docker/docker-compose.yaml new file mode 100644 index 0000000..a6b8737 --- /dev/null +++ b/spring-rabbitmq/.docker/docker-compose.yaml @@ -0,0 +1,29 @@ +services: + backend: + build: backend + ports: + - 8080:8080 + environment: + - RABBITMQ_HOST=rabbitmq + - RABBITMQ_QUEUE_NAME=springboot-queue + - RABBITMQ_FANOUT_EXCHANGE=springboot-exchange + networks: + - spring-rabbitmq + depends_on: + - rabbitmq + rabbitmq: + image: rabbitmq + restart: always + volumes: + - rabbitmq-data:/var/lib/rabbitmq + networks: + - spring-rabbitmq + environment: + - RABBITMQ_DEFAULT_USER=guest + - RABBITMQ_DEFAULT_PASS=guest + expose: + - 5672 +volumes: + rabbitmq-data: +networks: + spring-rabbitmq: diff --git a/spring-rabbitmq/README.md b/spring-rabbitmq/README.md new file mode 100644 index 0000000..687be93 --- /dev/null +++ b/spring-rabbitmq/README.md @@ -0,0 +1,62 @@ +## Compose sample application + +### Java application with Spring framework and Rabbitmq + +Project structure: +``` +. +├── backend +│   ├── Dockerfile +│   └── ... +├── compose.yaml +└── README.md + +``` + +[_compose.yaml_](compose.yaml) +``` +services: + backend: + build: backend + ports: + - 8080:8080 + rabbitmq: + image: rabbitmq + rabbitmq:3.9-management + ... +``` +The compose file defines an application with two services `backend` and `rabbitmq`. +When deploying the application, docker compose maps port 8080 of the backend service container to port 8080 of the host as specified in the file. +Make sure port 8080 on the host is not already being in use. + +## Deploy with docker compose + +``` +$ docker compose up -d +``` + +## Expected result + +Listing containers must show two containers running and the port mapping as below: +``` +$ docker ps +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +86f6bb2a1585 spring-rabbitmq-backend "java -cp app:app/li…" 6 minutes ago Up 6 minutes 0.0.0.0:8080->8080/tcp spring-rabbitmq-backend-1 +0c4522d3f075 rabbitmq "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 4369/tcp, 5671-5672/tcp, 15691-15692/tcp, 25672/tcp spring-rabbitmq-rabbitmq-1 +``` + +After the application starts, you can send and receive data to rabbitmq with this api : + +`POST`: http://localhost:8080/sendMessage + +Body: +``` +{ + "title":""Hello, rabbitmq", + "text":"This is my test messag", + "sender":"admin" +} +``` + +`GET`: http://localhost:8080/getMessage + diff --git a/spring-rabbitmq/backend/Dockerfile b/spring-rabbitmq/backend/Dockerfile new file mode 100644 index 0000000..9ab1943 --- /dev/null +++ b/spring-rabbitmq/backend/Dockerfile @@ -0,0 +1,39 @@ +# syntax=docker/dockerfile:1.4 + +FROM --platform=$BUILDPLATFORM maven:3.8.5-eclipse-temurin-17 AS builder +WORKDIR /workdir/server +COPY pom.xml /workdir/server/pom.xml +RUN mvn dependency:go-offline + +COPY src /workdir/server/src +RUN mvn install + +FROM builder AS dev-envs +RUN < + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.0.1 + + + com.company + spring-rabbitmq + 0.0.1 + spring-rabbitmq + Demo project for Spring Boot and Rabbitmq + + 17 + + + + org.springframework.boot + spring-boot-starter-amqp + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.amqp + spring-rabbit-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/spring-rabbitmq/backend/src/main/java/com/company/project/Application.java b/spring-rabbitmq/backend/src/main/java/com/company/project/Application.java new file mode 100644 index 0000000..24a9edd --- /dev/null +++ b/spring-rabbitmq/backend/src/main/java/com/company/project/Application.java @@ -0,0 +1,12 @@ +package com.company.project; + +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-rabbitmq/backend/src/main/java/com/company/project/config/QueueConsumer.java b/spring-rabbitmq/backend/src/main/java/com/company/project/config/QueueConsumer.java new file mode 100644 index 0000000..bfa6782 --- /dev/null +++ b/spring-rabbitmq/backend/src/main/java/com/company/project/config/QueueConsumer.java @@ -0,0 +1,33 @@ +package com.company.project.config; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.company.project.model.Message; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class QueueConsumer { + + private final RabbitTemplate rabbitTemplate; + + @Value("${queue.name}") + private String queueName; + + @Autowired + public QueueConsumer(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + } + + private String receiveMessage() { + String message = (String) rabbitTemplate.receiveAndConvert(queueName); + return message; + } + + public Message processMessage() throws JsonProcessingException { + String message = receiveMessage(); + return new ObjectMapper().readValue(message, Message.class); + } +} diff --git a/spring-rabbitmq/backend/src/main/java/com/company/project/config/QueueProducer.java b/spring-rabbitmq/backend/src/main/java/com/company/project/config/QueueProducer.java new file mode 100644 index 0000000..52317b8 --- /dev/null +++ b/spring-rabbitmq/backend/src/main/java/com/company/project/config/QueueProducer.java @@ -0,0 +1,29 @@ +package com.company.project.config; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.company.project.model.Message; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class QueueProducer { + + @Value("${fanout.exchange}") + private String fanoutExchange; + + private final RabbitTemplate rabbitTemplate; + + @Autowired + public QueueProducer(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + } + + public void produce(Message message) throws JsonProcessingException { + + rabbitTemplate.setExchange(fanoutExchange); + rabbitTemplate.convertAndSend(new ObjectMapper().writeValueAsString(message)); + } +} \ No newline at end of file diff --git a/spring-rabbitmq/backend/src/main/java/com/company/project/config/RabbitConfiguration.java b/spring-rabbitmq/backend/src/main/java/com/company/project/config/RabbitConfiguration.java new file mode 100644 index 0000000..e91534e --- /dev/null +++ b/spring-rabbitmq/backend/src/main/java/com/company/project/config/RabbitConfiguration.java @@ -0,0 +1,33 @@ +package com.company.project.config; + +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.FanoutExchange; +import org.springframework.amqp.core.Queue; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitConfiguration { + @Value("${fanout.exchange}") + private String fanoutExchange; + + @Value("${queue.name}") + private String queueName; + + @Bean + Queue queue() { + return new Queue(queueName, true); + } + + @Bean + FanoutExchange fanoutExchange() { + return new FanoutExchange(fanoutExchange); + } + + @Bean + Binding binding(Queue queue, FanoutExchange fanoutExchange) { + return BindingBuilder.bind(queue).to(fanoutExchange); + } +} diff --git a/spring-rabbitmq/backend/src/main/java/com/company/project/controller/MessageController.java b/spring-rabbitmq/backend/src/main/java/com/company/project/controller/MessageController.java new file mode 100644 index 0000000..b99d38d --- /dev/null +++ b/spring-rabbitmq/backend/src/main/java/com/company/project/controller/MessageController.java @@ -0,0 +1,36 @@ +package com.company.project.controller; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.company.project.config.QueueConsumer; +import com.company.project.config.QueueProducer; +import com.company.project.model.Message; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +@Controller +@RequestMapping("/") +public class MessageController { + + private final QueueProducer queueProducer; + private final QueueConsumer queueConsumer; + + public MessageController(QueueProducer queueProducer, QueueConsumer queueConsumer) { + this.queueProducer = queueProducer; + this.queueConsumer = queueConsumer; + } + + @GetMapping("getMessage") + public ResponseEntity getMessage() throws JsonProcessingException { + Message message = queueConsumer.processMessage(); + return new ResponseEntity(message, HttpStatus.OK); + } + + @PostMapping("sendMessage") + public ResponseEntity sendMessage(@RequestBody Message message) throws JsonProcessingException { + queueProducer.produce(message); + return new ResponseEntity(HttpStatus.CREATED); + } +} diff --git a/spring-rabbitmq/backend/src/main/java/com/company/project/model/Message.java b/spring-rabbitmq/backend/src/main/java/com/company/project/model/Message.java new file mode 100644 index 0000000..a2c7045 --- /dev/null +++ b/spring-rabbitmq/backend/src/main/java/com/company/project/model/Message.java @@ -0,0 +1,32 @@ +package com.company.project.model; + +public class Message { + + private String title; + private String text; + private String sender; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getSender() { + return sender; + } + + public void setSender(String sender) { + this.sender = sender; + } +} \ No newline at end of file diff --git a/spring-rabbitmq/backend/src/main/resources/application.properties b/spring-rabbitmq/backend/src/main/resources/application.properties new file mode 100644 index 0000000..a1aefa5 --- /dev/null +++ b/spring-rabbitmq/backend/src/main/resources/application.properties @@ -0,0 +1,9 @@ +server.port= 8080 +spring.main.banner-mode= off +spring.rabbitmq.host= ${RABBITMQ_HOST:localhost} +spring.rabbitmq.port= 5672 +spring.rabbitmq.username= guest +spring.rabbitmq.password= guest +queue.name= ${RABBITMQ_QUEUE_NAME:springboot-queue} +fanout.exchange= ${RABBITMQ_FANOUT_EXCHANGE:springboot-exchange} + diff --git a/spring-rabbitmq/compose.yaml b/spring-rabbitmq/compose.yaml new file mode 100644 index 0000000..a6b8737 --- /dev/null +++ b/spring-rabbitmq/compose.yaml @@ -0,0 +1,29 @@ +services: + backend: + build: backend + ports: + - 8080:8080 + environment: + - RABBITMQ_HOST=rabbitmq + - RABBITMQ_QUEUE_NAME=springboot-queue + - RABBITMQ_FANOUT_EXCHANGE=springboot-exchange + networks: + - spring-rabbitmq + depends_on: + - rabbitmq + rabbitmq: + image: rabbitmq + restart: always + volumes: + - rabbitmq-data:/var/lib/rabbitmq + networks: + - spring-rabbitmq + environment: + - RABBITMQ_DEFAULT_USER=guest + - RABBITMQ_DEFAULT_PASS=guest + expose: + - 5672 +volumes: + rabbitmq-data: +networks: + spring-rabbitmq: