2

I have modified my RabbitMQ from a previous post (spring-rabbit JSON deserialization for ArrayList contents) to now use a DirectMessageListener with MessagePostProcessors to GZip and GUnzip the message payloads.

However, it doesn't appear to be working as the breakpoints are not activated, but also because my RabbitListeners are no longer receiving messages, whereas they did with a SimpleMessageFactoryListenerContainer. Also, it appears the SimpleMessageListenerContainer(?) is still being used. On a side-note, I am autowiring the DirectMessageListenerContainer so I can dynamically set the queues I used.


spring-rabbit: 2.0.3.RELEASE. spring-boot: 2.0.1.RELEASE.


RabbitMQ configuration:

@Configuration
@EnableRabbit
public class MessagingConfiguration implements ShutdownListener {

    @Autowired
    private RabbitListenerEndpointRegistry registry;

    @Autowired
    private DirectMessageListenerContainer container;

    @Bean
    public DirectMessageListenerContainer messageListenerContainer(final ConnectionFactory connectionFactory) {
        final DirectMessageListenerContainer listenerContainer = new DirectMessageListenerContainer();
        listenerContainer.setConnectionFactory(connectionFactory);
        listenerContainer.setMessageConverter(jsonConverter()); // i.e.@RabbitListener to use Jackson2JsonMessageConverter
        listenerContainer.setAutoStartup(false);
        // container.setErrorHandler(errorHandler);
        final MessageListenerAdapter messageListener = new MessageListenerAdapter(new Object() {
            @SuppressWarnings("unused")
            public String handleMessage(final String message) {
                return message.toUpperCase();
            }
        });
        messageListener.setBeforeSendReplyPostProcessors(new GZipPostProcessor());
        listenerContainer.setMessageListener(messageListener);
        listenerContainer.setAfterReceivePostProcessors(new GUnzipPostProcessor());
        return listenerContainer;
    }

    @EventListener(ApplicationDatabaseReadyEvent.class)
    public void onApplicationDatabaseReadyEvent() {
        log.info("Starting all RabbitMQ Listeners..."); //$NON-NLS-1$
        for (final MessageListenerContainer listenerContainer : registry.getListenerContainers()) {
            listenerContainer.start();
        }
        log.info("Register is running: {}", registry.isRunning()); //$NON-NLS-1$
        log.info("Started all RabbitMQ Listeners."); //$NON-NLS-1$
    }

    @Bean
    public List<Declarable> bindings() {
    final List<Declarable> declarations = new ArrayList<>();
        final FanoutExchange exchange = new FanoutExchange("fx", true, false);
        final Queue queue = QueueBuilder.durable("orders").build();
        declarations.add(exchange);
        declarations.add(queue);
        declarations.add(BindingBuilder.bind(queue).to(exchange));
        List<String> q = new ArrayList<>();
        q.add(queue.getName());
    container.addQueueNames(q.toArray(new String[queues.size()]));

        return declarations;
    }

    @Bean
    public Jackson2JsonMessageConverter jsonConverter() {
        final Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
        converter.setClassMapper(classMapper());
        return converter;
    }

    private static DefaultJackson2JavaTypeMapper classMapper() {
        final DefaultJackson2JavaTypeMapper classMapper = new DefaultJackson2JavaTypeMapper();
        classMapper.setTrustedPackages("*"); //$NON-NLS-1$  //TODO add trusted packages
        return classMapper;
    }

    @ConditionalOnProperty(name = "consumer", havingValue = "true")
    @Bean
    public ConsumerListener listenerConsumer() {
        return new ConsumerListener();
    }

    @ConditionalOnProperty(name = "producer", havingValue = "true")
    @Bean
    public ProducerListener listenerProducer() {
        return new ProducerListener();
    }

    @Bean
    public RabbitAdmin rabbitAdmin(final CachingConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(jsonConverter()); // convert all sent messages to JSON
        rabbitTemplate.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
        rabbitTemplate.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
        return rabbitTemplate;
    }

    @Override
    public void shutdownCompleted(final ShutdownSignalException arg0) {
    }
}
Harry
  • 75
  • 1
  • 6

2 Answers2

2

It doesn't work that way, you can't autowire containers for @RabbitListeners; they are not beans; they are created by the container factory and registered in the registry. Instead you have to retrieve them from the registry (by id).

However, since you have autoStartup set to false, it shouldn't be "stealing" messages from your @RabbitListener.

Generally, DEBUG logging should help.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • I just added my missing config which starts up my containers upon another event, this way: ` for (final MessageListenerContainer listenerContainer : registry.getListenerContainers()) { listenerContainer.start(); } – Harry May 07 '18 at 21:19
  • Right but that won't start the `messageListenerContainer` `@Bean` - that one is a bean rather than being created for a `@RabbitListener`. You have to add the post processors to the listener container factory, not that container bean, if you want them applied to annotated listeners. It looks like you might be confusing `DirectMessageListenerContainer` with `DirectRabbitListenerContainerFactory` which is what is used to create one of the former for each listener. – Gary Russell May 07 '18 at 21:34
  • Fixed. I am now using the DirectRabbitListenerContainerFactory and have set my post processors on it. They're now being used. Big thanks Gary. – Harry May 07 '18 at 22:03
  • Gary, I got an exception: `Caused by: java.io.UnsupportedEncodingException: gzip:UTF-8` ` at java.lang.StringCoding.decode(StringCoding.java:190) ~[?:1.8.0_152]` ` at java.lang.String.(String.java:426) ~[?:1.8.0_152]` ` at java.lang.String.(String.java:491) ~[?:1.8.0_152]` ` at org.springframework.amqp.support.converter.Jackson2JsonMessageConverter.convertBytesToObject(Jackson2JsonMessageConverter.java:215)` ` at org.springframework.amqp.support.converter.Jackson2JsonMessageConverter.fromMessage(Jackson2JsonMessageConverter.java:179) ` – Harry May 07 '18 at 22:18
  • Don't put stuff like that in comments; it's hard to read. I suggest you start a new question, showing your current code/configuration. It sounds like the uncompress MPP is not being applied. The Gzip MPP prefixes the `contentEncoding` message property with `gzip:` and the Unzip MPP strips the prefix off. The fact that the prefix is still there when the JSON converter is called indicates it was not unzipped. – Gary Russell May 07 '18 at 22:32
0

The official guide explains it at https://docs.spring.io/spring-amqp/reference/html/#post-processing

Basically there are couple of options i.e.

  1. Configure Message convertor like below

    public class CompressionMessageConverter {
    public static MessageConverter messageConverter() {
        final MessageConverter simpleMessageConverter = new SimpleMessageConverter();
        return new MessageConverter() {
            @Override
            public Message toMessage(Object messagePayload, MessageProperties messageProperties)
                    throws MessageConversionException {
                final Message m = simpleMessageConverter.toMessage(messagePayload, messageProperties);
                final ByteArrayOutputStream os = new ByteArrayOutputStream();
                try (GZIPOutputStream gz = new GZIPOutputStream(os)) {
                    gz.write(m.getBody());
                } catch (IOException e) {
                    throw new MessageConversionException(e.getMessage());
                }
    
                return new Message(os.toByteArray(), messageProperties);
            }
    
            @Override
            public Object fromMessage(Message messagePayload) throws MessageConversionException {
                final ByteArrayOutputStream os = new ByteArrayOutputStream();
                try (GZIPInputStream is = new GZIPInputStream(new ByteArrayInputStream(messagePayload.getBody()))) {
                    byte[] b = new byte[2048];
                    int r;
                    while ((r = is.read(b)) != -1) {
                        os.write(b, 0, r);
                    }
                } catch (IOException ioException) {
                    throw new RuntimeException(ioException);
                }
    
                return simpleMessageConverter.fromMessage(new Message(os.toByteArray(), messagePayload.getMessageProperties()));
            }
        };
    }
    

Use it in RabbitTemplate as

rabbitTemplate.setMessageConverter(CompressionMessageConverter .messageConverter());
  1. Use post processors

    rabbitTemplate.setBeforePublishPostProcessors(new GZipPostProcessor()); rabbitTemplate.setAfterReceivePostProcessors(new GUnzipPostProcessor());

Shirishkumar Bari
  • 2,692
  • 1
  • 28
  • 36