diff --git a/src/main/java/com/knecon/fforesight/tenantcommons/MultiTenancyMessagingConfiguration.java b/src/main/java/com/knecon/fforesight/tenantcommons/MultiTenancyMessagingConfiguration.java index 61a0338..b402fbe 100644 --- a/src/main/java/com/knecon/fforesight/tenantcommons/MultiTenancyMessagingConfiguration.java +++ b/src/main/java/com/knecon/fforesight/tenantcommons/MultiTenancyMessagingConfiguration.java @@ -2,14 +2,20 @@ package com.knecon.fforesight.tenantcommons; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory; +import org.springframework.amqp.core.MessagePostProcessor; +import org.springframework.amqp.rabbit.config.ContainerCustomizer; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.amqp.RabbitTemplateCustomizer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import static com.knecon.fforesight.tenantcommons.ForwardTenantInterceptor.TENANT_HEADER_NAME; @@ -17,47 +23,59 @@ import static com.knecon.fforesight.tenantcommons.ForwardTenantInterceptor.TENAN @ConditionalOnClass(RabbitTemplate.class) public class MultiTenancyMessagingConfiguration { + /** + * Spring boot-autoconfigure 3.14 only allows for one {@link ContainerCustomizer} + * to be defined. If 2 or more are defined instead, nothing ends up customizing the {@link SimpleRabbitListenerContainerFactory} + * (for more details, see RabbitAnnotationDrivenConfiguration::simpleRabbitListenerContainerFactory). + * To bypass this limitation, we define and inject our own {@link SimpleMessageListenerContainerCustomizer} which allows + * us to define as many container customization points as needed. + */ @Bean - public Jackson2JsonMessageConverter producerJackson2MessageConverter() { + @Primary + public ContainerCustomizer simpleMessageListenerContainerCustomizer( + ObjectProvider customizers) { + return container -> customizers.orderedStream().forEach((customizer) -> customizer.customize(container)); + } + @Bean + public MessageConverter producerJackson2MessageConverter() { ObjectMapper mapper = new ObjectMapper().findAndRegisterModules(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return new Jackson2JsonMessageConverter(mapper); } + @Bean + public RabbitTemplateCustomizer rabbitTemplatePublishTenantIdHeaderCustomizer( + @Qualifier("tenantIdSetterPostProcessor") MessagePostProcessor messagePostProcessor) { + return template -> template.addBeforePublishPostProcessors(messagePostProcessor); + } @Bean - public static BeanPostProcessor multitenancyBeanPostProcessor() { + public SimpleMessageListenerContainerCustomizer rabbitInterceptTenantIdHeaderCustomizer( + @Qualifier("tenantIdGetterPostProcessor") MessagePostProcessor messagePostProcessor) { + return container -> container.addAfterReceivePostProcessors(messagePostProcessor); + } - return new BeanPostProcessor() { - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - - if (bean instanceof RabbitTemplate) { - - ((RabbitTemplate) bean).setBeforePublishPostProcessors(m -> { - m.getMessageProperties().setHeader(TENANT_HEADER_NAME, TenantContext.getTenantId()); - return m; - }); - - } else if (bean instanceof AbstractRabbitListenerContainerFactory) { - - ((AbstractRabbitListenerContainerFactory) bean).setAfterReceivePostProcessors(m -> { - String tenant = m.getMessageProperties().getHeader(TENANT_HEADER_NAME); - - if (tenant != null) { - TenantContext.setTenantId(tenant); - } else { - throw new RuntimeException("No Tenant is set queue message"); - } - return m; - }); - } - return bean; - } + @Bean + public MessagePostProcessor tenantIdSetterPostProcessor() { + return message -> { + message.getMessageProperties().setHeader(TENANT_HEADER_NAME, TenantContext.getTenantId()); + return message; }; } + @Bean + public MessagePostProcessor tenantIdGetterPostProcessor() { + return message -> { + String tenant = message.getMessageProperties().getHeader(TENANT_HEADER_NAME); + + if (tenant != null) { + TenantContext.setTenantId(tenant); + } else { + throw new RuntimeException("No Tenant is set queue message"); + } + return message; + }; + } } diff --git a/src/main/java/com/knecon/fforesight/tenantcommons/SimpleMessageListenerContainerCustomizer.java b/src/main/java/com/knecon/fforesight/tenantcommons/SimpleMessageListenerContainerCustomizer.java new file mode 100644 index 0000000..9fe3143 --- /dev/null +++ b/src/main/java/com/knecon/fforesight/tenantcommons/SimpleMessageListenerContainerCustomizer.java @@ -0,0 +1,9 @@ +package com.knecon.fforesight.tenantcommons; + +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; + +@FunctionalInterface +public interface SimpleMessageListenerContainerCustomizer { + + void customize(SimpleMessageListenerContainer simpleMessageListenerContainer); +}