I try to send an object via AMQP messaging (RabbitMQ) but when I receive the message the converter raise a JsonMappingException because it was unable to deserialize that message.
Order.java:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.joda.ser.LocalDateSerializer;
import org.joda.time.LocalDate;
import javax.persistence.*;
import java.util.List;
/**
* Created by Flaviu Cicio on 07.07.2016.
*/
@Entity
@Table(name = "t_order", schema = "orders")
public class Order {
@Id
private Long id;
private Long userId;
@JsonSerialize(using = LocalDateSerializer.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
private LocalDate date;
@OneToMany(mappedBy="byOrder", cascade = CascadeType.ALL)
@GeneratedValue( strategy = GenerationType.IDENTITY )
private List<OrderProduct> products;
public Order() {
}
public Order(Long id, Long userId, List<OrderProduct> products, LocalDate date) {
this.id = id;
this.userId = userId;
this.products = products;
this.date = date;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public List<OrderProduct> getProducts() {
return products;
}
public void setProducts(List<OrderProduct> products) {
this.products = products;
}
public LocalDate getDate() {
return date;
}
public void setDate(LocalDate date) {
this.date = date;
}
@Override
public String toString() {
return "Order{" +
"id=" + id +
", userId=" + userId +
", date=" + date +
'}';
}
}
RabbitConfiguration.java:
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfiguration {
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("localhost");
return connectionFactory;
}
@Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
@Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey("order-queue");
template.setMessageConverter(jsonMessageConverter());
return template;
}
@Bean
public Queue userQueue() {
return new Queue("order-queue", false);
}
}
OrderController.java:
import com.orderservice.service.OrderService;
import com.shopcommon.model.Order;
import com.shopcommon.model.OrderProduct;
import com.shopcommon.model.Product;
import com.shopcommon.model.User;
import org.apache.log4j.Logger;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping(value = "/orders")
public class OrderController {
Logger logger = Logger.getLogger(OrderController.class);
@Autowired
OrderService orderService;
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
AmqpAdmin rabbitAdmin;
@RequestMapping(method = RequestMethod.GET)
public List<Order> getOrders(){
return orderService.get();
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Order getOrder(@PathVariable("id") Long id){
logger.info("Returned order: " + orderService.getById(id));
return orderService.getById(id);
}
/**
* Persist a new order received from amqp messaging
*
* @return
*/
@RequestMapping(method = RequestMethod.POST)
public Order createOrder(){
Order order = (Order) rabbitTemplate.receiveAndConvert("order-queue");
logger.info("Created a new order: " + order);
return orderService.create(order);
}
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public void deleteOrder(@PathVariable("id") Long id){
orderService.delete(id);
}
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public Order updateOrder(@PathVariable("id") Long id, @RequestBody Order order){
List<OrderProduct> products = order.getProducts();
products.stream().forEach(orderProduct -> orderProduct.setOrder(order));
order.setProducts(products);
return orderService.update(order);
}
/**
* Get user from a specific order
*
* @param id
* @return
*/
@RequestMapping(value = "/{id}/user", method = RequestMethod.GET)
public User getOrderUser(@PathVariable("id") Long id){
Order order = orderService.getById(id);
rabbitAdmin.purgeQueue("user-queue", false);
try {
logger.info("Requested from user-service user from order with id " + id);
logger.info("USER ID " + order.getUserId());
URL obj = new URL("http://localhost:9999/user-service/users/" + order.getUserId());
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.getResponseCode();
User user = (User)rabbitTemplate.receiveAndConvert("user-queue");
logger.info("Received from user-service: " + user);
return user;
} catch (IOException e) {
logger.error(e.getMessage());
}
logger.info("No user found!");
return null;
}
/**
* Get products from a specific order
*
* @param id
* @return
*/
@SuppressWarnings("Duplicates")
@RequestMapping(value = "/{id}/products", method = RequestMethod.GET)
public List<Product> getOrderProducts(@PathVariable("id") Long id){
rabbitAdmin.purgeQueue("product-queue", false);
List<Product> products = new ArrayList<>();
logger.info("Requested from product-service products from order with id " + id);
Order order = orderService.getById(id);
order.getProducts().forEach(orderProduct -> {
try {
URL obj = new URL("http://localhost:9999/product-service/products/" + orderProduct.getProductId());
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.getResponseCode();
products.add((Product) rabbitTemplate.receiveAndConvert("product-queue"));
} catch (MalformedURLException e) {
logger.error(e.getMessage());
} catch (IOException e) {
logger.error(e.getMessage());
}
});
logger.info("Received from product-service a number of " + products.size() + " products");
return products;
}
}
Stacktrace:
2016-07-13 09:40:27.668 ERROR 5108 --- [nio-8004-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.amqp.support.converter.MessageConversionException: Failed to convert Message content] with root cause
com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT), expected START_ARRAY: expected String, Number or JSON Array
at [Source: {"id":null,"userId":1,"products":[],"date":{"dayOfMonth":13,"dayOfWeek":"WEDNESDAY","dayOfYear":195,"month":"JULY","monthValue":7,"year":2016,"hour":9,"minute":40,"nano":199000000,"second":27,"chronology":{"id":"ISO","calendarType":"iso8601"}}}; line: 1, column: 44] (through reference chain: com.shopcommon.model.Order["date"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:261) ~[jackson-databind-2.8.0.jar:2.8.0]
at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1340) ~[jackson-databind-2.8.0.jar:2.8.0]
at com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:65) ~[jackson-datatype-joda-2.6.7.jar:2.6.7]
at com.fasterxml.jackson.datatype.joda.deser.LocalDateDeserializer.deserialize(LocalDateDeserializer.java:15) ~[jackson-datatype-joda-2.6.7.jar:2.6.7]
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:490) ~[jackson-databind-2.8.0.jar:2.8.0]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95) ~[jackson-databind-2.8.0.jar:2.8.0]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276) ~[jackson-databind-2.8.0.jar:2.8.0]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140) ~[jackson-databind-2.8.0.jar:2.8.0]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789) ~[jackson-databind-2.8.0.jar:2.8.0]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2871) ~[jackson-databind-2.8.0.jar:2.8.0]
at org.springframework.amqp.support.converter.Jackson2JsonMessageConverter.convertBytesToObject(Jackson2JsonMessageConverter.java:122) ~[spring-amqp-1.5.6.RELEASE.jar:na]
at org.springframework.amqp.support.converter.Jackson2JsonMessageConverter.fromMessage(Jackson2JsonMessageConverter.java:92) ~[spring-amqp-1.5.6.RELEASE.jar:na]
at org.springframework.amqp.rabbit.core.RabbitTemplate.receiveAndConvert(RabbitTemplate.java:819) ~[spring-rabbit-1.5.6.RELEASE.jar:na]
at com.orderservice.controller.OrderController.createOrder(OrderController.java:62) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_91]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:281) ~[spring-boot-actuator-1.3.6.RELEASE.jar:1.3.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:115) ~[spring-boot-actuator-1.3.6.RELEASE.jar:1.3.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103) ~[spring-boot-actuator-1.3.6.RELEASE.jar:1.3.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) ~[tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:670) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520) [tomcat-embed-core-8.0.36.jar:8.0.36]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476) [tomcat-embed-core-8.0.36.jar:8.0.36]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_91]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_91]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.36.jar:8.0.36]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_91]
First, you should add the following maven dependency:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
Second, you should update your MessasgeConverter bean with this:
@Bean
public MessageConverter messageConverter()
{
ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
return new Jackson2JsonMessageConverter(mapper);
}
After this update you don't need to use "JsonSerialize" annotations.
For anyone who is still looking for a answer they can copy paste in their application :)
The code below asks spring to inject its default object mapper, then asks it to register all the modules in the classpath. So make sure you have included 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' in your pom or gradle file.
@Configuration
@Slf4j
public class SpringInternalObjectMapperConfig {
/*
Java 8 date/time type `java.time.LocalDateTime` not supported by default:
add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
*/
@Bean
public Jackson2JsonMessageConverter messageConverter(ObjectMapper springInternalObjectMapper) {
log.info("Updating spring's object mapper.");
springInternalObjectMapper.findAndRegisterModules();
return new Jackson2JsonMessageConverter(springInternalObjectMapper);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With