javaspringgoogle-cloud-pubsubspring-messaging

How to fix 'java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class' error?


I am working on a Spring project in Java that recieves messages from a Google Pub/Sub subscription. However, my code fails to parse these incoming messages. The messages are given in a JSON format. A sample message is as follows:

{
    "crudType": "Create",
    "payload": {
        "id": 14833,
        "product": { "id": 14829, "name": "Product18" },
        "color": { "id": 4, "name": "Green" },
        "name": "Option 4"
    }
}

My message consumer method tries to parse the message to a compatible class named CrudEvent, which contains a ProductOption entity. The properties of these classes match the format of an inbound JSON message. I also tried to use DTOs, but without success.

@ServiceActivator(inputChannel = Channels.PRODUCT_OPTION_INPUT)
public void handleProductOptionMessage(Message<CrudEvent<ProductOption>> message) throws Exception {
    try {
        // LoggerFactory.getLogger(Consumer.class).info(message.toString());
        CrudEvent<ProductOption> event = message.getPayload();
        LoggerFactory.getLogger(Consumer.class).info(event.toString());
        ProductOption productOption = event.getPayload();
        this.logger.info(productOption.toString());
        // this.handleEvent(this.productOptionService, event, productOption);
        this.ack(message);
    } catch (Exception e) {
        this.logger.error("Failed to handle product option message!");

        throw e;
    }
}

My ProductOption entity is coded like this:

package nl.omoda.stocktestservice.entity;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import nl.omoda.stocktestservice.messaging.dto.ProductOptionDto;

@Entity
public class ProductOption {
    @Id
    private Long id;

    @ManyToOne()
    @JoinColumn(name = "productId")
    private Product product;

    private String name;

    protected ProductOption() {}

    public ProductOption(Long id, Product product, String name) {
        this.id = id;
        this.product = product;
        this.name = name;
    }

    public ProductOption(ProductOptionDto dto) {
        this(dto.getId(), new Product(dto.getProduct()), dto.getName());
    }

    @Override
    public String toString() {
        return "{" +
            " id='" + getId() + "'" +
            ", product='" + getProduct() + "'" +
            ", name='" + getName() + "'" +
            "}";
    }

    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Product getProduct() {
        return this.product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

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

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

}

The Product entity is pretty much the same, except it only has the 'id' and 'name' properties. The color field in the JSON is not used in the ProductOption entity.

Complete error log:

org.springframework.messaging.MessageHandlingException:
    error occurred during processing message in 'MethodInvokingMessageProcessor' [org.springframework.integration.handler.MethodInvokingMessageProcessor@6927b9b0];
    nested exception is java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class nl.omoda.stocktestservice.entity.Product
        (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; nl.omoda.stocktestservice.entity.Product is in unnamed module of loader 'app')

Solution

  • Spring does not seem to support a Message with a type that contains a generic. Copying the CrudEvent type as a version where the ProductOptions was pre-inserted fixed the error (I named it ProductOptionCrudEvent).

    Kind of strange that generics are not supported for message receival.