简体   繁体   中英

Cannot convert object of type to JMS message. Supported message payloads are: String, byte array, Map<String,?>, Serializable object

I am developing Spring + ActiveMQ + JMS example. In this example, I am facing the below error: I tried with Many options but not working at all.

I am looking to implement the following:

1) Queue should be keep reading messages (either using the Converter or listener)

2) Based on the InstructionMessage type I have to take decision on where to send it or not.

The code uploaded at : https://github.com/test512/spring-mvc-jms-tutorials

Sending person InstructionMessage [instructionType=10, productCode=10, quantity=10, uOM=10, timeStamp=10]
Exception in thread "main" org.springframework.jms.support.converter.MessageConversionException: Cannot convert object of type [com.jms.testing.spring.InstructionMessage] to JMS message. Supported message payloads are: String, byte array, Map<String,?>, Serializable object.
    at org.springframework.jms.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:78)
    at org.springframework.jms.core.JmsTemplate$5.createMessage(JmsTemplate.java:651)
    at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:593)
    at org.springframework.jms.core.JmsTemplate$3.doInJms(JmsTemplate.java:562)
    at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:484)
    at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:559)
    at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:648)
    at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:639)
    at com.jms.testing.spring.SpringJmsPersonProducer.sendMessage(SpringJmsPersonProducer.java:18)
    at com.jms.testing.spring.SpringJmsMessageConverterExample.main(SpringJmsMessageConverterExample.java:16)

appContextWithMessageConverter.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jms="http://www.springframework.org/schema/jms"
    xsi:schemaLocation="http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:jms.properties" />

    <bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="${jms.brokerURL}" />
    </bean>

    <bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
        <property name="connectionFactory" ref="jmsConnectionFactory" />
        <property name="maxConnections" value="50" />
    </bean>

    <jms:listener-container container-type="default" connection-factory="pooledJmsConnectionFactory" acknowledge="auto" >
        <!-- <jms:listener destination="messageDestination" ref="messageDestination" /> -->
        <jms:listener destination="messageDestination" ref="myListener" />
    </jms:listener-container>

    <bean id="instructionMessageConverter" class="com.jms.testing.spring.InstructionMessageConverter" />

    <bean id="myListener" class="com.jms.testing.spring.MyListener" />

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <constructor-arg ref="pooledJmsConnectionFactory" />
        <property name="defaultDestination" ref="messageDestination" />
        <property name="receiveTimeout" value="${jms.receiveTimeout}" />
        <!-- <property name="messageConverter" ref="instructionMessageConverter" /> -->

    </bean>

    <bean id="springJmsPersonProducer" class="com.jms.testing.spring.SpringJmsPersonProducer">
        <property name="jmsTemplate" ref="jmsTemplate" />
    </bean>

    <bean id="springJmsPersonConsumer" class="com.jms.testing.spring.SpringJmsPersonConsumer">
        <property name="jmsTemplate" ref="jmsTemplate" />
    </bean> 

    <bean id="messageDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg value="messageQueue1" />
    </bean>
</beans>

MyListener.java

public class MyListener implements MessageListener {

    @Override
    public void onMessage(Message message) {
        MapMessage mapMessage = (MapMessage) message;

        try {
            int instructionType = Integer.parseInt(mapMessage.getString("instructionType"));
            int productCode = Integer.parseInt(mapMessage.getString("productCode"));
            int quantity = Integer.parseInt(mapMessage.getString("quantity"));
            int timeStamp = Integer.parseInt(mapMessage.getString("timeStamp"));
            int uOM = Integer.parseInt(mapMessage.getString("uOM"));
            InstructionMessage instructionMessage = new InstructionMessage(instructionType, productCode, quantity, uOM,
                    timeStamp);
            System.out.println(instructionMessage.toString());

        } catch (NumberFormatException | JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

InstructionMessage.java

public class InstructionMessage implements Serializable{

private static final long serialVersionUID = 1L;
    private int instructionType;
    private int productCode;
    private int quantity;
    private int uOM;
    private int timeStamp;


    public InstructionMessage(int instructionType, int productCode, int quantity, int uOM, int timeStamp) {
        super();
        this.instructionType = instructionType;
        this.productCode = productCode;
        this.quantity = quantity;
        this.uOM = uOM;
        this.timeStamp = timeStamp;
    }

    public int getInstructionType() {
        return instructionType;
    }

    public void setInstructionType(int instructionType) {
        this.instructionType = instructionType;
    }

    public int getProductCode() {
        return productCode;
    }

    public void setProductCode(int productCode) {
        this.productCode = productCode;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public int getuOM() {
        return uOM;
    }

    public void setuOM(int uOM) {
        this.uOM = uOM;
    }

    public int getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(int timeStamp) {
        this.timeStamp = timeStamp;
    }

    @Override
    public String toString() {
        return "InstructionMessage [instructionType=" + instructionType + ", productCode=" + productCode + ", quantity="
                + quantity + ", uOM=" + uOM + ", timeStamp=" + timeStamp + "]";
    }
}

SpringJmsMessageConverterExample.java

public class SpringJmsMessageConverterExample {
    public static void main(String[] args) throws URISyntaxException, Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "appContextWithMessageConverter.xml");

        try {
            SpringJmsPersonProducer springJmsProducer = (SpringJmsPersonProducer) context.getBean("springJmsPersonProducer");
            InstructionMessage m1 = new InstructionMessage(10,10,10,10,10);
            System.out.println("Sending person " + m1);
            springJmsProducer.sendMessage(m1);

            InstructionMessage m2 = new InstructionMessage(5,5,5,5,5);
            System.out.println("Sending person " + m2);
            springJmsProducer.sendMessage(m2);

            InstructionMessage m3 = new InstructionMessage(0,0,0,0,0);
            System.out.println("Sending person " + m3);
            springJmsProducer.sendMessage(m3);

            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

            SpringJmsPersonConsumer springJmsConsumer = (SpringJmsPersonConsumer) context.getBean("springJmsPersonConsumer");
            System.out.println("Consumer receives " + springJmsConsumer.receiveMessage());
        } finally {
            context.close();
        }
    }
}

SpringJmsPersonProducer.java

public class SpringJmsPersonProducer {

    private JmsTemplate jmsTemplate;

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage(final InstructionMessage instructionMessage) {
        getJmsTemplate().convertAndSend(instructionMessage);
    }
}

SpringJmsPersonConsumer.java

public class SpringJmsPersonConsumer {

    private JmsTemplate jmsTemplate;

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public InstructionMessage receiveMessage() throws JMSException {
        InstructionMessage instructionMessage = (InstructionMessage) getJmsTemplate().receiveAndConvert();
        return instructionMessage;  
    }
}

simply add a generated serial Version ID and not default 1L!

public class InstructionMessage implements Serializable{

private static final long serialVersionUID = -295422703255886286L;

UPDATE :

1) to make your listener working fine, you need to update destination="messageQueue1 because with messageDestination container create this queue and your messages are sent to messageQueue1 :

<jms:listener-container container-type="default"
    connection-factory="pooledJmsConnectionFactory" acknowledge="auto">
    <jms:listener destination="messageQueue1" ref="myListener" />
</jms:listener-container>

and update MyListener

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;

public class MyListener implements MessageListener {

    @Override
    public void onMessage(Message message) {
        try {
            ObjectMessage mapMessage = (ObjectMessage) message;
            InstructionMessage instructionMessage = (InstructionMessage) mapMessage.getObject();
            System.out.println(instructionMessage.toString());
        } catch (NumberFormatException | JMSException e) {
            e.printStackTrace();
        }
    }
}

2) conditional send

import org.springframework.jms.core.JmsTemplate;

public class SpringJmsPersonProducer {

    private JmsTemplate jmsTemplate;

    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    public void sendMessage(final InstructionMessage instructionMessage) {
        if (canSend(instructionMessage)) {
            getJmsTemplate().convertAndSend(instructionMessage);
        } else {
            throw new IllegalArgumentException("message");
        }
    }

    private boolean canSend(InstructionMessage instructionMessage) {
        return instructionMessage.getQuantity() > 0;
    }
}

JMS moves data to different JVM. Cannot be done with ordinary objects. Must be serialised, so require to implement interface Serializable Suppose such can be done, ask main architect

import java.io.Serializable;
...
public class InstructionMessage implements Serializable {

 private static final long serialVersionUID = 7526472295622776147L;

....
}

Background: network don't understand Java objects, can only transfer bytes. Is is basic for Java development, any book can be helpful (Thinking in Java by Eckel)

EDIT: partial answer (try to answer) to You changed (with Serializable ) code. Now ClassCastException exception says: sending and receiving part are mutual not compatible. Framework or server made conversion InstructionMessage -> HashMap, I guess on early stages.

I guess too, but here I'm almost sure, HashMap content is functionally the same.

Your simplest, but not elegant way is to leave InstructionMessage and work with HashMap on both side (change sender), or debug a problem.

If I remember, You remove Converter class java, but remain in XML. I'm NOT a big Spring fan (personally hate "xml programming"), I guess some correction in XML can help

Use Jackson conversion instead, handy in an event driven system where multiple event's objects need to trigger

@Bean
public JmsTemplate jmsTemplate() {
    JmsTemplate template = new JmsTemplate();
    template.setConnectionFactory(connectionFactory());
    template.setMessageConverter(jacksonJmsMessageConverter());
    template.setPubSubDomain(true);
    return template;
}

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

and then set

containerFactory.setMessageConverter(jacksonJmsMessageConverter());

that's it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM