Removed the manual post-processor BeanPostProcessor messaging configuration in favour of a more stream-lined, extendable, approach.

This commit is contained in:
Hanelore.Ianoseck 2023-11-06 22:41:52 +02:00
parent 69c1c17c71
commit 0b3cf27f13
2 changed files with 57 additions and 32 deletions

View File

@ -2,11 +2,16 @@ 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;
@ -17,47 +22,58 @@ 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<SimpleMessageListenerContainer>}
* 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() {
public ContainerCustomizer<SimpleMessageListenerContainer> simpleMessageListenerContainerCustomizer(
ObjectProvider<SimpleMessageListenerContainerCustomizer> 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;
};
}
}

View File

@ -0,0 +1,9 @@
package com.knecon.fforesight.tenantcommons;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
@FunctionalInterface
public interface SimpleMessageListenerContainerCustomizer {
void customize(SimpleMessageListenerContainer simpleMessageListenerContainer);
}