2

I am using spring boot amqp in which I will be consuming a list of Employee objects from a queue. My listener method looks like this:

@RabbitListener(queues = "emp_queue")
public void processAndPortEmployeeData(List<Employee> empList) {
    empList.forEach(emp -> { some logic })
}

However, when I try to consume the message, I get a class cast exception: For some reason, I'm getting a LinkedHashMap.

Caused by: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.integration.domain.Employee

If I change my listener method to consume a single employee object, it works fine and I'm using the following jackson configurations for it:

@Configuration
@EnableRabbit
public class RabbitConfiguration implements RabbitListenerConfigurer {

@Bean
public MappingJackson2MessageConverter jackson2Converter() {
    return new MappingJackson2MessageConverter();
}

@Bean
public DefaultMessageHandlerMethodFactory handlerMethodFactory() {
    DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
    factory.setMessageConverter(jackson2Converter());
    return factory;
}

@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
    registrar.setMessageHandlerMethodFactory(handlerMethodFactory());
}

}

Is there some other jackson configuration that I need to do to consume the list of employee objects?

Thanks a lot!

Sample Input Json message which I will be consuming:

[
  {
    "name" : "Jasmine",
    "age" : "24",
    "emp_id" : 1344
  },
  {
    "name" : "Mark",
    "age" : "32",
    "emp_id" : 1314
  }
]
vin4yak
  • 35
  • 8

1 Answers1

3

What version of Spring AMQP are you using?

If 1.6 or greater, the framework passes the argument type to the message converter.

Before 1.6 you either need type information in the message headers, or you need to configure the converter with type information.

That said, since the converter created a map, it implies that was what received (not a list).

Please show a sample of the JSON in a message.

EDIT

Note that boot auto-configures the message converter if there's a single bean of that type...

@SpringBootApplication
public class So40491628Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So40491628Application.class, args);
        Resource resource = new ClassPathResource("data.json");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        FileCopyUtils.copy(resource.getInputStream(), baos);
        context.getBean(RabbitTemplate.class).send("foo", MessageBuilder.withBody(baos.toByteArray())
            .andProperties(MessagePropertiesBuilder.newInstance().setContentType("application/json").build()).build());
        Thread.sleep(10000);
        context.close();
    }

    @Bean
    public Jackson2JsonMessageConverter converter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    public Queue foo() {
        return new Queue("foo");
    }

    @RabbitListener(queues = "foo")
    public void listen(List<Employee> emps) {
        System.out.println(emps);
    }

    public static class Employee {

        private String name;

        private String age;

        private int empId;

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAge() {
            return this.age;
        }

        public void setAge(String age) {
            this.age = age;
        }

        public int getEmpId() {
            return this.empId;
        }

        public void setEmpId(int empId) {
            this.empId = empId;
        }

        @Override
        public String toString() {
            return "Employee [name=" + this.name + ", age=" + this.age + ", empId=" + this.empId + "]";
        }

    }
}

Result:

[Employee [name=Jasmine, age=24, empId=0], Employee [name=Mark, age=32, empId=0]]

enter image description here

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Thanks for your response. I'm using spring-boot-starter-amqp 1.4.1 which uses version 1.6.2 of spring-rabbit. (both are latest) – vin4yak Nov 08 '16 at 16:33
  • added sample input json as well. – vin4yak Nov 08 '16 at 16:36
  • Oh; your configuration is wrong; you should be using a `Jackson2JsonMessageConverter` - I'll post an update in a few minutes. – Gary Russell Nov 08 '16 at 16:43
  • Thanks! The above configurations work fine if I receive a single object. However, the requirement changed and we will be receiving a list and after the changes, it broke. – vin4yak Nov 08 '16 at 16:48
  • I tried the configuration as per your edit but if I remove DefaultMessageHandlerMethodFactory and don't implement RabbitListenerConfigurer in my code and just use Jackson2JsonMessageConverter config, I'm consuming the message as bytes. – vin4yak Nov 08 '16 at 17:08
  • Is the sender not setting the contentType header? See my sender. There are [two converters involved](http://docs.spring.io/spring-amqp//reference/html/_reference.html#async-annotation-conversion). The type inference works with the first converter. – Gary Russell Nov 08 '16 at 17:12
  • The upstream application isn't built yet so I was manually sending the message using rabbitmq UI. But it looks like there's no way I can send a message using the UI as a string.. I guess I'll need to write a sample application to send it to the queue using the properties you used! – vin4yak Nov 08 '16 at 17:24
  • You can use the Admin UI - I do it all the time; just set the `content_type` property to `application/json`. – Gary Russell Nov 08 '16 at 17:26
  • You're right.. It works! Thanks a ton! Come to think of it, in my previous project which was based on spring-integration, I used your tutorials a lot! You are awesome. – vin4yak Nov 08 '16 at 18:24