2

I have a use case where I need to convert a message from one type to another (i.e. TextMessage -> ObjectMessage).

I found that when diverting between queues there is an option to transform the message. I have implemented the Transformer interface as instructed in the documentation.

import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.core.server.transformer.Transformer;
import javax.jms.ObjectMessage;
    
public class TypeTransformer implements Transformer {
    
    @Override
    public Message transform(Message message) {
        return message;
    }
}

But I am now beginning to realize that it might be impossible to convert from a org.apache.activemq.artemis.api.core.Message to an javax.jms.ObjectMessage?

Is this right? That it cannot be done or is there some other way?

Justin Bertram
  • 29,372
  • 4
  • 21
  • 43

1 Answers1

1

It should technically be possible to convert a javax.jms.TextMessage to a javax.jms.ObjectMessage, but it may be cumbersome. Here are some important things to note:

  • javax.jms.TextMessage, javax.jms.ObjectMessage, and org.apache.activemq.artemis.api.core.Message are all just interfaces. The javax version is what you use on the client and Message is what is used on the broker. The data for each type of message is stored differently in the underlying message implementation.
  • The class for the Java object that you wish to put into the ObjectMessage will need to be on the broker's classpath. This isn't required under normal circumstances as the broker itself will never serialize or deserialize the object.
  • You should really try to avoid ObjectMessage whenever possible. ObjectMessage objects depend on Java serialization to marshal and unmarshal their object payload. This process is generally considered unsafe (and slow!), because a malicious payload can exploit the host system. Lots of CVEs have been created for this. For this reason, most JMS providers force users to explicitly whitelist packages that can be exchanged using ObjectMessage messages. For example, here's the related documentation for ActiveMQ Artemis. There are a number of other issues with using JMS ObjectMessage not related to security that you should read about.

Granted you understand all that you should be able to convert the message using code something like this:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.apache.activemq.artemis.api.core.ICoreMessage;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.server.transformer.Transformer;

public class TypeTransformer implements Transformer {

   @Override
   public Message transform(Message message) {
      ICoreMessage coreMessage = message.toCore();
      try {
         // get the data from the TextMessage
         SimpleString mySimpleString = coreMessage.getBodyBuffer().readNullableSimpleString();
         if (mySimpleString == null) {
            // no text in the message so no transformation can be done
            return message;
         }
         String myString = mySimpleString.toString();

         // parse the data from the TextMessage and set it on the serializable object
         Serializable object = new MySerializable();         

         // turn serializable object into byte array and write it to the message
         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
         ObjectOutputStream oos = new ObjectOutputStream(baos);
         oos.writeObject(object);
         oos.flush();
         byte[] data = baos.toByteArray();
         coreMessage.getBodyBuffer().clear();
         coreMessage.getBodyBuffer().writeInt(data.length);
         coreMessage.getBodyBuffer().writeBytes(data);
         coreMessage.setType(Message.OBJECT_TYPE);

         return coreMessage;
      } catch (Exception e) {
         e.printStackTrace();
         return message;
      }
   }
}
Justin Bertram
  • 29,372
  • 4
  • 21
  • 43