diff --git a/boot-rabbitmq-thymeleaf/README.md b/boot-rabbitmq-thymeleaf/README.md index 720d62c4f..c411068f6 100644 --- a/boot-rabbitmq-thymeleaf/README.md +++ b/boot-rabbitmq-thymeleaf/README.md @@ -12,7 +12,16 @@ Demonstrates a message producer and consumer setup with RabbitMQ, plus a simple --- -Source Code : https://sivalabs.in/2018/02/springboot-messaging-rabbitmq/ +[Source Code](https://sivalabs.in/2018/02/springboot-messaging-rabbitmq/) + +--- + +## Important links + +* Home Page : http://localhost:8080 +* RabbitMq : http://localhost:15672 (guest/guest default) + +--- ## Installing Rabbit MQ @@ -22,8 +31,3 @@ Windows https://www.rabbitmq.com/which-erlang.html http://www.erlang.org/downloads Video - https://www.youtube.com/watch?v=gKzKUmtOwR4 - -# Important links - -Home Page : http://localhost:8080 -RabbitMq : http://localhost:15672 (guest/guest default) diff --git a/boot-rabbitmq-thymeleaf/docker-compose.yml b/boot-rabbitmq-thymeleaf/docker-compose.yml index 5e4b57847..edc37a49d 100644 --- a/boot-rabbitmq-thymeleaf/docker-compose.yml +++ b/boot-rabbitmq-thymeleaf/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.2" services: rabbitmq: container_name: rabbitmq @@ -20,6 +19,25 @@ services: retries: 10 networks: - rabbitmq_go_net + + postgresqldb: + image: postgres:17.2-alpine + hostname: postgresqldb + extra_hosts: [ 'host.docker.internal:host-gateway' ] + environment: + - POSTGRES_USER=appuser + - POSTGRES_PASSWORD=secret + - POSTGRES_DB=appdb + healthcheck: + test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"] + interval: 10s + timeout: 5s + retries: 5 + ports: + - "5432:5432" + networks: + - rabbitmq_go_net + networks: rabbitmq_go_net: driver: bridge \ No newline at end of file diff --git a/boot-rabbitmq-thymeleaf/pom.xml b/boot-rabbitmq-thymeleaf/pom.xml index 75d6d57da..6ab711adc 100644 --- a/boot-rabbitmq-thymeleaf/pom.xml +++ b/boot-rabbitmq-thymeleaf/pom.xml @@ -63,6 +63,11 @@ h2 runtime + + org.postgresql + postgresql + runtime + org.springframework.boot spring-boot-devtools @@ -74,11 +79,6 @@ org.webjars webjars-locator-core - - org.glassfish.jaxb - jaxb-runtime - provided - org.webjars bootstrap @@ -90,10 +90,11 @@ 3.7.1 - org.projectlombok - lombok - true + org.webjars + popper.js + 2.11.7 + org.springframework.boot @@ -115,6 +116,11 @@ rabbitmq test + + org.testcontainers + postgresql + test + diff --git a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitListenerConfig.java b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitListenerConfig.java new file mode 100644 index 000000000..76662167a --- /dev/null +++ b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitListenerConfig.java @@ -0,0 +1,23 @@ +package com.poc.boot.rabbitmq.config; + +import org.springframework.amqp.rabbit.annotation.EnableRabbit; +import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer; +import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory; + +@EnableRabbit +@Configuration(proxyBeanMethods = false) +public class RabbitListenerConfig implements RabbitListenerConfigurer { + + private final MessageHandlerMethodFactory messageHandlerMethodFactory; + + public RabbitListenerConfig(MessageHandlerMethodFactory messageHandlerMethodFactory) { + this.messageHandlerMethodFactory = messageHandlerMethodFactory; + } + + @Override + public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) { + registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory); + } +} diff --git a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitMQConfig.java b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitMQConfig.java index 94e8c05db..2ea556673 100644 --- a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitMQConfig.java +++ b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitMQConfig.java @@ -1,6 +1,5 @@ package com.poc.boot.rabbitmq.config; -import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; @@ -8,11 +7,8 @@ import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; -import org.springframework.amqp.rabbit.annotation.EnableRabbit; -import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -22,10 +18,8 @@ import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.RetryTemplate; -@EnableRabbit -@Configuration -@Slf4j -public class RabbitMQConfig implements RabbitListenerConfigurer { +@Configuration(proxyBeanMethods = false) +public class RabbitMQConfig { public static final String DLX_ORDERS_EXCHANGE = "DLX.ORDERS.EXCHANGE"; @@ -39,7 +33,7 @@ public class RabbitMQConfig implements RabbitListenerConfigurer { private final RabbitTemplateConfirmCallback rabbitTemplateConfirmCallback; - public RabbitMQConfig(RabbitTemplateConfirmCallback rabbitTemplateConfirmCallback) { + RabbitMQConfig(RabbitTemplateConfirmCallback rabbitTemplateConfirmCallback) { this.rabbitTemplateConfirmCallback = rabbitTemplateConfirmCallback; } @@ -57,10 +51,8 @@ DirectExchange ordersExchange() { /* Binding between Exchange and Queue using routing key */ @Bean - Binding bindingMessages() { - return BindingBuilder.bind(ordersQueue()) - .to(ordersExchange()) - .with(ROUTING_KEY_ORDERS_QUEUE); + Binding bindingMessages(DirectExchange ordersExchange, Queue ordersQueue) { + return BindingBuilder.bind(ordersQueue).to(ordersExchange).with(ROUTING_KEY_ORDERS_QUEUE); } @Bean @@ -83,8 +75,8 @@ Queue deadLetterQueue() { /* Binding between Exchange and Queue for Dead Letter */ @Bean - Binding deadLetterBinding() { - return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()); + Binding deadLetterBinding(Queue deadLetterQueue, FanoutExchange deadLetterExchange) { + return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange); } /* Bean for rabbitTemplate */ @@ -116,15 +108,11 @@ MappingJackson2MessageConverter consumerJackson2MessageConverter() { } @Bean - MessageHandlerMethodFactory messageHandlerMethodFactory() { + MessageHandlerMethodFactory messageHandlerMethodFactory( + MappingJackson2MessageConverter consumerJackson2MessageConverter) { DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory(); - messageHandlerMethodFactory.setMessageConverter(consumerJackson2MessageConverter()); + messageHandlerMethodFactory.setMessageConverter(consumerJackson2MessageConverter); return messageHandlerMethodFactory; } - - @Override - public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) { - registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory()); - } } diff --git a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitTemplateConfirmCallback.java b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitTemplateConfirmCallback.java index 5f56bf37d..8ec414778 100644 --- a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitTemplateConfirmCallback.java +++ b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/config/RabbitTemplateConfirmCallback.java @@ -2,20 +2,23 @@ import com.poc.boot.rabbitmq.entities.TrackingState; import com.poc.boot.rabbitmq.repository.TrackingStateRepository; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.stereotype.Component; import org.springframework.util.Assert; -@Slf4j @Component -@RequiredArgsConstructor public class RabbitTemplateConfirmCallback implements RabbitTemplate.ConfirmCallback { + private static final Logger log = LoggerFactory.getLogger(RabbitTemplateConfirmCallback.class); private final TrackingStateRepository trackingStateRepository; + public RabbitTemplateConfirmCallback(TrackingStateRepository trackingStateRepository) { + this.trackingStateRepository = trackingStateRepository; + } + @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { Assert.notNull(correlationData, () -> "correlationData can't be null"); @@ -27,6 +30,8 @@ public void confirm(CorrelationData correlationData, boolean ack, String cause) log.debug( "persisted correlationId in db : {}", this.trackingStateRepository.save( - new TrackingState(null, correlationData.getId(), "processed"))); + new TrackingState() + .setCorrelationId(correlationData.getId()) + .setStatus("processed"))); } } diff --git a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/controller/MessageController.java b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/controller/MessageController.java index 88c25b68e..3f7464348 100644 --- a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/controller/MessageController.java +++ b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/controller/MessageController.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.poc.boot.rabbitmq.model.Order; import com.poc.boot.rabbitmq.service.OrderMessageSender; -import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; @@ -12,14 +11,16 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes; @Controller -@RequiredArgsConstructor -public class MessageController { +class MessageController { private final OrderMessageSender orderMessageSender; + MessageController(OrderMessageSender orderMessageSender) { + this.orderMessageSender = orderMessageSender; + } + @PostMapping("/sendMsg") - public String handleMessage( - @ModelAttribute Order order, RedirectAttributes redirectAttributes) { + String handleMessage(@ModelAttribute Order order, RedirectAttributes redirectAttributes) { try { this.orderMessageSender.sendOrder(order); redirectAttributes.addFlashAttribute("message", "Order message sent successfully"); diff --git a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/entities/TrackingState.java b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/entities/TrackingState.java index 7b10b1158..eb0dd3e9c 100644 --- a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/entities/TrackingState.java +++ b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/entities/TrackingState.java @@ -1,14 +1,12 @@ package com.poc.boot.rabbitmq.entities; -import jakarta.persistence.*; -import lombok.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; @Entity -@AllArgsConstructor -@NoArgsConstructor -@ToString -@Setter -@Getter public class TrackingState { @Id @@ -19,4 +17,33 @@ public class TrackingState { private String correlationId; private String status = "processed"; + + public TrackingState() {} + + public Long getId() { + return id; + } + + public TrackingState setId(Long id) { + this.id = id; + return this; + } + + public String getCorrelationId() { + return correlationId; + } + + public TrackingState setCorrelationId(String correlationId) { + this.correlationId = correlationId; + return this; + } + + public String getStatus() { + return status; + } + + public TrackingState setStatus(String status) { + this.status = status; + return this; + } } diff --git a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/OrderMessageListener.java b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/OrderMessageListener.java index 0f4d6aa5c..e6080851b 100644 --- a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/OrderMessageListener.java +++ b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/OrderMessageListener.java @@ -5,16 +5,17 @@ import com.poc.boot.rabbitmq.model.Order; import com.poc.boot.rabbitmq.repository.TrackingStateRepository; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.Message; -@Slf4j @Configuration(proxyBeanMethods = false) public class OrderMessageListener { + private static final Logger log = LoggerFactory.getLogger(OrderMessageListener.class); private final TrackingStateRepository trackingStateRepository; public OrderMessageListener(TrackingStateRepository trackingStateRepository) { diff --git a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/impl/OrderMessageSenderImpl.java b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/impl/OrderMessageSenderImpl.java index 22dbcf5dd..78f66df7b 100644 --- a/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/impl/OrderMessageSenderImpl.java +++ b/boot-rabbitmq-thymeleaf/src/main/java/com/poc/boot/rabbitmq/service/impl/OrderMessageSenderImpl.java @@ -6,7 +6,6 @@ import com.poc.boot.rabbitmq.model.Order; import com.poc.boot.rabbitmq.service.OrderMessageSender; import java.util.UUID; -import lombok.RequiredArgsConstructor; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageBuilder; import org.springframework.amqp.core.MessageProperties; @@ -15,13 +14,18 @@ import org.springframework.stereotype.Service; @Service -@RequiredArgsConstructor public class OrderMessageSenderImpl implements OrderMessageSender { private final RabbitTemplate templateWithConfirmsEnabled; private final ObjectMapper objectMapper; + public OrderMessageSenderImpl( + RabbitTemplate templateWithConfirmsEnabled, ObjectMapper objectMapper) { + this.templateWithConfirmsEnabled = templateWithConfirmsEnabled; + this.objectMapper = objectMapper; + } + @Override public void sendOrder(Order order) throws JsonProcessingException { // this.rabbitTemplate.convertAndSend(RabbitConfig.QUEUE_ORDERS, order); diff --git a/boot-rabbitmq-thymeleaf/src/main/resources/application.properties b/boot-rabbitmq-thymeleaf/src/main/resources/application.properties index ec5dc9a51..84ef385bc 100644 --- a/boot-rabbitmq-thymeleaf/src/main/resources/application.properties +++ b/boot-rabbitmq-thymeleaf/src/main/resources/application.properties @@ -1,3 +1,5 @@ +spring.application.name=boot-rabbitmq-theymeleaf + logging.level.com.poc.boot.rabbitmq=debug #spring.rabbitmq.host=localhost @@ -14,6 +16,35 @@ spring.rabbitmq.listener.simple.retry.initial-interval=1s spring.rabbitmq.listener.simple.retry.max-attempts=3 spring.rabbitmq.listener.simple.retry.multiplier=2 spring.rabbitmq.listener.simple.retry.max-interval=2s +spring.rabbitmq.listener.simple.acknowledge-mode=auto +spring.rabbitmq.listener.simple.observation-enabled=true + +spring.mvc.problemdetails.enabled=true +spring.threads.virtual.enabled=true + +spring.testcontainers.beans.startup=parallel + +################ Actuator ##################### +management.endpoints.web.exposure.include=configprops,env,health,info,logfile,loggers,metrics,prometheus,sbom +management.endpoint.health.show-details=always + +################ Database ##################### +spring.jpa.show-sql=false +spring.jpa.open-in-view=false +spring.data.jpa.repositories.bootstrap-mode=deferred +spring.datasource.hikari.auto-commit=false +spring.datasource.hikari.pool-name=HikariPool-${spring.application.name} +spring.datasource.hikari.data-source-properties.ApplicationName=${spring.application.name} +spring.jpa.hibernate.ddl-auto=validate +#spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.jdbc.time_zone=UTC +spring.jpa.properties.hibernate.generate_statistics=false +spring.jpa.properties.hibernate.jdbc.batch_size=25 +spring.jpa.properties.hibernate.order_inserts=true +spring.jpa.properties.hibernate.order_updates=true +spring.jpa.properties.hibernate.query.fail_on_pagination_over_collection_fetch=true +spring.jpa.properties.hibernate.query.in_clause_parameter_padding=true +spring.jpa.properties.hibernate.query.plan_cache_max_size=4096 +spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true -spring.mvc.problemdetails.enabled=true -spring.threads.virtual.enabled=true \ No newline at end of file diff --git a/boot-rabbitmq-thymeleaf/src/main/resources/db/changelog/01-create-tables.xml b/boot-rabbitmq-thymeleaf/src/main/resources/db/changelog/01-create-tables.xml index 82e1b2d2d..3f48ac73d 100644 --- a/boot-rabbitmq-thymeleaf/src/main/resources/db/changelog/01-create-tables.xml +++ b/boot-rabbitmq-thymeleaf/src/main/resources/db/changelog/01-create-tables.xml @@ -1,9 +1,12 @@ + https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"> + + + @@ -23,10 +26,10 @@ - + - + diff --git a/boot-rabbitmq-thymeleaf/src/test/java/com/poc/boot/rabbitmq/common/ContainerConfiguration.java b/boot-rabbitmq-thymeleaf/src/test/java/com/poc/boot/rabbitmq/common/ContainerConfiguration.java index fd8f3bbac..97e88b7fc 100644 --- a/boot-rabbitmq-thymeleaf/src/test/java/com/poc/boot/rabbitmq/common/ContainerConfiguration.java +++ b/boot-rabbitmq-thymeleaf/src/test/java/com/poc/boot/rabbitmq/common/ContainerConfiguration.java @@ -4,6 +4,7 @@ import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.context.annotation.Bean; +import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.containers.RabbitMQContainer; import org.testcontainers.utility.DockerImageName; @@ -16,4 +17,10 @@ public class ContainerConfiguration { RabbitMQContainer rabbitMQContainer() { return new RabbitMQContainer(DockerImageName.parse("rabbitmq").withTag("4.0.5-management")); } + + @Bean + @ServiceConnection + PostgreSQLContainer postgreSQLContainer() { + return new PostgreSQLContainer<>(DockerImageName.parse("postgres").withTag("17.2-alpine")); + } }