Search code examples
javaspring-jms

Listener method using Spring and ActiveMQ throws "Property name cannot be null" exceptions repeatedly


I've attempted to implement ActiveMQ using Spring in two places. Both implementations have had this issue. Sending either an HTTP Request using postman or directly entering a message in the ActiveMQ console causes the following Error to be repeated infinitely:

2024-02-02T17:08:56.317-06:00 ERROR 2264 --- [ntContainer#0-1] c.j.a.config.JmsConfig$JMSErrorHandler   : Error in listener
org.springframework.jms.listener.adapter.ListenerExecutionFailedException: Listener method 'public void com.jackhodge.activemqlearning.consumer.component.MessageConsumer.messageListener(com.jackhodge.activemqlearning.model.SystemMessage)' threw exception
  at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:118) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:84) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:783) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:741) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:719) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:333) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:270) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1258) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1248) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1141) ~[spring-jms-6.1.3.jar:6.1.3]
  at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]
Caused by: java.lang.NullPointerException: Property name cannot be null
  at org.apache.activemq.command.ActiveMQMessage.getObjectProperty(ActiveMQMessage.java:575) ~[activemq-client-jakarta-5.18.3.jar:5.18.3]
  at org.apache.activemq.command.ActiveMQMessage.getStringProperty(ActiveMQMessage.java:683) ~[activemq-client-jakarta-5.18.3.jar:5.18.3]
  at org.springframework.jms.support.converter.MappingJackson2MessageConverter.getJavaTypeForMessage(MappingJackson2MessageConverter.java:456) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.support.converter.MappingJackson2MessageConverter.fromMessage(MappingJackson2MessageConverter.java:241) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:250) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.adapter.AbstractAdaptableMessageListener$MessagingMessageConverterAdapter.extractPayload(AbstractAdaptableMessageListener.java:472) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.adapter.AbstractAdaptableMessageListener$MessagingMessageConverterAdapter$LazyResolutionMessage.unwrapPayload(AbstractAdaptableMessageListener.java:539) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.adapter.AbstractAdaptableMessageListener$MessagingMessageConverterAdapter$LazyResolutionMessage.getPayload(AbstractAdaptableMessageListener.java:521) ~[spring-jms-6.1.3.jar:6.1.3]
  at org.springframework.messaging.handler.annotation.support.PayloadMethodArgumentResolver.resolveArgument(PayloadMethodArgumentResolver.java:122) ~[spring-messaging-6.1.3.jar:6.1.3]
  at org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:118) ~[spring-messaging-6.1.3.jar:6.1.3]
  at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:147) ~[spring-messaging-6.1.3.jar:6.1.3]
  at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:115) ~[spring-messaging-6.1.3.jar:6.1.3]
  at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:110) ~[spring-jms-6.1.3.jar:6.1.3]
  ... 10 common frames omitted

What stands out is Error in listener [...] Property name cannot be null.

The error still occurs when I don't do anything in my listener, and Logs/Breakpoints in the messageListener aren't sent nor activated.

Here's the simple app in which the error is occurring:

JmsConfig

@Configuration
@EnableJms
public class JmsConfig {

    Logger logger = LoggerFactory.getLogger(JMSErrorHandler.class);
    
    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
            ConnectionFactory connectionFactory,
            DefaultJmsListenerContainerFactoryConfigurer configurer,
            JMSErrorHandler defaultErrorHandler){
        DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
    
        jmsListenerContainerFactory.setConnectionFactory(connectionFactory);
        jmsListenerContainerFactory.setConcurrency("1"); // start w/ 5 consumers; auto-scale to 10 consumers as necessary
    
        jmsListenerContainerFactory.setErrorHandler(defaultErrorHandler);
        jmsListenerContainerFactory.setMessageConverter(this.jacksonJmsMessageConverter());
    
        configurer.configure(jmsListenerContainerFactory, connectionFactory);
        return jmsListenerContainerFactory;
    
    }
    
    
    @Service
    public class JMSErrorHandler implements ErrorHandler {
        @Override
        public void handleError(Throwable t) {
            logger.error("Error in listener ", t);
        }
    }
    
    @Bean
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setObjectMapper(new ObjectMapper());
        return converter;
    }

**PublishController**

```java
package com.jackhodge.activemqlearning.controller;

import com.jackhodge.activemqlearning.model.SystemMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestControllerpublic class PublishController {// helperclass for sending/receiving messages
    // Spring JMS abstraction API: Distills and simplifies process; abstracts away boilerplate code
    private JmsTemplate jmsTemplate;

    @Autowired
    public PublishController(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    // post method to trigger publishing of messages
    // Requests to here at sent to the Messaging Broker
    @PostMapping("/publishMessage")
    public ResponseEntity<String> publishMessage(@RequestBody SystemMessage systemMessage){
        try{
            jmsTemplate.convertAndSend("jackhodge-queue", systemMessage.toString());
            return new ResponseEntity<>("I, Jack Hodge, sent your message.", HttpStatus.OK);
        } catch (Exception e){
            return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }

    }
}

SystemMessage

package com.jackhodge.activemqlearning.model;

import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
//import java.io.Serializable;

@Setter
public class SystemMessage {
    private String source;
    private String message;
    
    public SystemMessage(String source, String message) {
    
        this.source = source;
        this.message = message;
    }
    
    
    @Override
    public String toString() {
        return "SystemMessage{" +
                "source='" + source + '\'' +
                ", message='" + message + '\'' +
                '}';
    }

}

MessageConsumer

package com.jackhodge.activemqlearning.consumer.component;

import com.jackhodge.activemqlearning.model.SystemMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

@Componentpublic class MessageConsumer {

    public static final Logger LOGGER = LoggerFactory.getLogger(MessageConsumer.class);
    
    // Consumes from the Messaging broker
    @JmsListener(destination = "jackhodge-queue")
    public void messageListener(SystemMessage systemMessage){
        LOGGER.info("Message Received {}", systemMessage);
    }

}

I'm somewhat new to Swing and ActiveMQ and this problem has stumped me -- every avenue of breakpoints/logging/message sources as far as I'm capable of has been tried. Thank you!

Sending this request via both Postman and the ActiveMQ console both resulted in the same Error regardless:

{     
    "source":"jeff bezo",     
    "message":"hello" 
}

Solution

  • I fixed this by setting setTypeIdPropertyName in my MessageConverter Bean:

    @Bean
    public MessageConverter jacksonJmsMessageConverter(){
            MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
            converter.setTargetType(MessageType.TEXT);
            converter.setTypeIdPropertyName("_type");
            converter.setObjectMapper(new ObjectMapper());
            return converter;
        }