Javax.jms.jmsexception: Unable to generate body from content. Serializable class cannot be used for agent: Java. Lang. ClassNotFoundException:

Ban class com. Javaliao. Portal. Model. TbLogVisit distrust such serialized as objectMessage load.

Detailed information on how to configure trusted class, see http://activemq.apache.org/objectmessage.html.

Javax.jm.jmsexception: Failed to build body from content. Serializable class not available to broker. Reason: java.lang.ClassNotFoundException: Forbidden class com.javaliao.portal.model.TbLogVisit! This class is not trusted to be serialized as ObjectMessage payload. Please take a look at http://activemq.apache.org/objectmessage.html for more information on how to configure trusted classes. at org.apache.activemq.util.JMSExceptionSupport.create(JMSExceptionSupport.java:36) at org.apache.activemq.command.ActiveMQObjectMessage.getObject(ActiveMQObjectMessage.java:213) at com.javaliao.portal.listener.LogVisitListener.consumePaymentResult(LogVisitListener.java:50) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171)Copy the code

This problem arises because:

Although the use of ObjectMessages is generally discouraged because it introduces classpath coupling between producers and consumers, ActiveMQ supports them as part of the JMS specification. Securing objectMessage objects relies on the Java serialization of the Marshal/Unmarshal object payload. This process is generally considered insecure because malicious loads can exploit the host system. This is why, starting with versions 5.12.2 and 5.13.0, ActiveMQ forces users to explicitly whitelist packages that can be exchanged using ObjectMessages. If you need to exchange object messages, you need to add the packages your application is using. By using org, apache activemq. Serializable_packages system properties (by the agent and activemq client library) can do this. You can add this system property to the activemq_opts variable in the ${activemq_HOME}/bin/env script. For example – dorg. Apache activemq. Serializable_packages = Java. Lang, javax.mail. Security, Java. Util,. Org. Apache activemq, Org. Fusesource. Hawtbuf, com. Thoughtworks. Xstream. Mapper, com. Mycompany. Myapp will com. Mycompany. Myapp package add to the list of trusted package note, The other packages listed here are enabled by default because they are required for regular agent work. If you want to simplify the mechanism, can use the * wildcard to trust all packages, such as – dorg. Apache activemq. Serializable_ package = * client on the client side, You need to break the application’s environment by using the same mechanism used to deserialize the malicious code on the objectMessage.getobject () call. You can use the same configuration on agent mechanism, properties and use the system configuration of trusted class, however, this is usually not convenient in the client application, therefore in 5.12.2 and 5.13.1, we introduced the use of activemqconnectionfactory additional configuration mechanism. Two additional methods are defined: The setTrustedPackages () method allows you to set a list of trusted packages to unserialize, Such as activemqconnectionfactory = new activemqconnectionfactory (” TCP: / / localhost: 61616 “); Factory. SetTrustedPackages (new ArrayList (arrays.aslist (” org. Apache. Activemq. Test, org. Apache. Camel. Test. The split (“, “))); SetTrustAllPackages () allows you to turn off security checks and trust all classes. It’s useful for testing. Activemqconnectionfactory = new activemqconnectionfactory (” TCP: / / localhost: 61616 “); Factory. SetTrustAllPackages (true); You can set the same properties in the Camel context, such as: org.apache.activemq.test org.apache.camel. Test Test or this configuration will override system properties if they are set.

 

My code:

Message code:

@Autowired ActiveMQUtil activeMQUtil; @override public void insertLogVisit(TbLogVisit TbLogVisit) {// Use the message queue to send messages asynchronously and save them to the database. Try {// Connect to the message server connection = activeMQUtil.getConnection(); connection.start(); Session Session = Connection.createsession (true, session.session_transacted); Queue testQueue = session.createQueue("LOG_VISIT_QUEUE"); MessageProducer producer = session.createProducer(testqueue); ObjectMessage objectMessage = session.createObjectMessage(); /*AMQ_SCHEDULED_DELAY long Time to delay delivery AMQ_SCHEDULED_PERIOD long Time to repeat delivery AMQ_SCHEDULED_REPEAT int Number of times to repeat delivery AMQ_SCHEDULED_CRON String Cron expression */ / Set the time to 30s objectMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY,1000*30); objectMessage.setObject(tbLogVisit); // Set producer.setDeliveryMode(deliverymode.persistent); producer.send(objectMessage); session.commit(); Connection.close (); // A transactional message that must be committed to take effect. } catch (JMSException e) { e.printStackTrace(); }}Copy the code

Connect utility class:

package com.javaliao.portal.util; import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.pool.PooledConnectionFactory; import javax.jms.Connection; import javax.jms.JMSException; import java.util.ArrayList; import java.util.Arrays; public class ActiveMQUtil { PooledConnectionFactory pooledConnectionFactory=null; public void init(String brokerUrl){ ActiveMQConnectionFactory activeMQConnectionFactory=new ActiveMQConnectionFactory(brokerUrl); / / set the whitelist package activeMQConnectionFactory. SetTrustedPackages (new ArrayList(Arrays.asList("org.apache.activemq.test,org.apache.camel.test".split(",")))); activeMQConnectionFactory.setTrustAllPackages(true); pooledConnectionFactory = new PooledConnectionFactory(activeMQConnectionFactory); pooledConnectionFactory.setExpiryTimeout(2000); pooledConnectionFactory.setMaximumActiveSessionPerConnection(10); pooledConnectionFactory.setMaxConnections(30); pooledConnectionFactory.setReconnectOnException(true); } public Connection getConnection(){ Connection connection = null; try { connection = pooledConnectionFactory.createConnection(); } catch (JMSException e) { e.printStackTrace(); } return connection; }}Copy the code

The configuration class:

package com.javaliao.portal.config; import com.javaliao.portal.util.ActiveMQUtil; import org.apache.activemq.ActiveMQConnectionFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import javax.jms.JMSException; import javax.jms.Session; @Configuration public class ActiveMQConfig { @Value("${spring.activemq.broker-url:disabled}") String brokerURL ; @Value("${activemq.listener.enable:disabled}") String listenerEnable; @Bean public ActiveMQUtil getActiveMQUtil() throws JMSException { if(brokerURL.equals("disabled")){ return null; } ActiveMQUtil activeMQUtil=new ActiveMQUtil(); activeMQUtil.init(brokerURL); return activeMQUtil; } @Bean public ActiveMQConnectionFactory activeMQConnectionFactory ( ){ /* if((url==null||url.equals(""))&&! brokerURL.equals("disabled")){ url=brokerURL; }*/ ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory( brokerURL); return activeMQConnectionFactory; } // Define a message listener connection factory, Defined here is the point to point mode of listener connection factory @ Bean (name = "jmsQueueListener") public DefaultJmsListenerContainerFactory jmsQueueListenerContainerFactory(ActiveMQConnectionFactory activeMQConnectionFactory ) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); if(! listenerEnable.equals("true")){ return null; } factory.setConnectionFactory(activeMQConnectionFactory); // Set the concurrency number factory.setconcurrency ("5"); / / reconnection interval factory. SetRecoveryInterval (5000 l); factory.setSessionTransacted(false); factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); return factory; }}Copy the code

Entity class:

import java.io.Serializable;
import java.util.Date;

public class TbLogVisit implements Serializable {

    private static final long serialVersionUID = 2659644940173539668L;

    private Long id;

    private String visitIpAddress;

    private String visitHostName;

    private String visitChannel;

    private String visitDescription;

    private String visitApi;

    private String visitParams;

    private String visitUrl;

    private String visitTimeConsuming;

    private String visitResult;

    private Long visitNum;

    private String visitThrowingErro;

    private Date visitStartTime;

    private Date visitEndTime;

    private Date createTime;

    private Date updateTime;
Copy the code

Receiving messages:

package com.javaliao.portal.listener; import com.javaliao.portal.log4j.BaseLogger; import com.javaliao.portal.model.TbLogVisit; import com.javaliao.portal.service.ActionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Component; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.ObjectMessage; @Component public class LogVisitListener { @Autowired ActionService actionService; /** * To reflect the gap, we made a large class, using JSON conversion is about 35MB * using JSON transfer, the unit is ms: * Total time :10000 * Receiver converts Message to textMessage time :0 * Receiver converts JSON to Object time: 7146. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * using ObjectMessage transmission: Transmission time: * total time: 6742 * 173 * send total time: 4836 * the receiver time to convert the message to ObjectMessage: 1733 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Conclusion * I didn't stress test it, I only tested it once, and the test environment was just my laptop, but I think I can make a conclusion. * Object is superior to JSON for asynchronous communication between servers. * The main advantages are that Java serialization and converting between objects is much more efficient than converting between JSON box objects, and the smaller size of the serialized objects is also a good reason for the transfer. */ @JmsListener(containerFactory = "jmsQueueListener" ,destination = "LOG_VISIT_QUEUE") public void consumePaymentResult(Message mapMessage){ try { ObjectMessage tbLogVisitObject = (ObjectMessage) mapMessage; TbLogVisit object = (TbLogVisit) tbLogVisitObject.getObject(); int count = actionService.insertLog(object); If (count < 1){baselogger.info (" log update failed "); } } catch (JMSException e) { e.printStackTrace(); }}}Copy the code

 application.preperties:

# activemq message queue spring. Activemq. Broker - url = TCP: / / 192.168.134.100:61616 activemq. Listener. Enable = trueCopy the code

 

Configuration side:

 

Mine is configured on Linux

 

It doesn’t work, it doesn’t work, and then forces me to use String.

@override public void insertLogVisit(TbLogVisit TbLogVisit) {try {// Connection Connection = activeMQUtil.getConnection(); connection.start(); Session Session = Connection.createsession (true, session.session_transacted); Queue testQueue = session.createQueue("LOG_VISIT_QUEUE"); MessageProducer producer = session.createProducer(testqueue); MapMessage mapMessage=new ActiveMQMapMessage(); String toString = JSONObject.fromObject(tbLogVisit).toString(); mapMessage.setString("tbLogVisit",toString); producer.setDeliveryMode(DeliveryMode.PERSISTENT); producer.send(mapMessage); session.commit(); Connection.close (); // A transactional message that must be committed to take effect. } catch (JMSException e) { e.printStackTrace(); }}Copy the code

Receiving messages:

@JmsListener(containerFactory = "jmsQueueListener" ,destination = "LOG_VISIT_QUEUE") public void consumeLogResult(MapMessage mapMessage){ try { String object = mapMessage.getString("tbLogVisit"); JSONObject jsonObject = new JSONObject().fromObject(object); TbLogVisit logVisit = (TbLogVisit) JSONObject.toBean(jsonObject, TbLogVisit.class); int count = actionService.insertLog(logVisit); If (count < 1){baselogger.info (" log update failed "); } } catch (JMSException e) { e.printStackTrace(); }}Copy the code

Then the console: