2

New to protobuf, need your kind advice please.

I have a JMS subscriber which is receiving a message (Type -> com.tibco.tibjmsTibjmsBytesMessage) from a TIBCO queue. The messages have been published as Protobuf bytes message on the queue. As as subscriber I have converted these bytesMessage to byte array. Since I have the protobuf message's JAVA class protoFile.class, hence in order to deserialize the byte array, I am calling the protoFile.parseFrom(byte[] data) function. However in response I see that the values of all the fields are empty.

Can someone please suggest what is being missed ? All I need is to read the data from my Protobuf bytes message and assign them to java objects. I do not have the schema of the proto file, only have the compiled JAVA class of the proto file say protoFile.class

TIA!

Nikhil24
  • 51
  • 1
  • 10
  • What you are doing makes sence. Are the processor architectures between the Publisher and Subscriber applications compatible ? (I mean do you have the same processor technology on both sides?) – EmmanuelM Apr 08 '21 at 09:55

1 Answers1

0

A protobuf message does not have the class name in the data stream. This I think is done because the language of origin can be different than the receiver's language. The receive's end point would expect a given type and have proto generated code to convert it. If you restrict your world to Java on both ends, you could send the class name along with the data. You could even use another proto message to do that. Then assume that all messages are of that type. The real message and type are wrapped up in that message. Then you have the classname and the data. Here's a MessageConverter for Jms that you could grab parts of it that fits your situation.

The proto file

syntax = "proto3";
package jms;

option java_package = "some.package.here.jms";
option java_multiple_files = true;

message AnyMessage{
  string classname = 1;
  bytes bytesData = 2;
}

The code

@Slf4j
public class ProtobufJmsMessageConverter implements MessageConverter {

@Override
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
    GeneratedMessageV3 protoMsg = (GeneratedMessageV3) object;
    
    AnyMessage wrap = AnyMessage.newBuilder().setClassname(object.getClass().getName()).setBytesData(protoMsg.toByteString()).build();
            
    BytesMessage bytesMessage = session.createBytesMessage();
    bytesMessage.writeBytes(wrap.toByteArray());
    return bytesMessage;
}

@Override
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
    BytesMessage bytesMessage = (BytesMessage) message;
    int messageLength = (int) bytesMessage.getBodyLength();
    byte[] rawProtoMsg = new byte[messageLength];
    bytesMessage.readBytes(rawProtoMsg);
    try {
        AnyMessage anyMessage = AnyMessage.parseFrom(rawProtoMsg);
        Class protoClass = Class.forName(anyMessage.getClassname());
        
        Method m = protoClass.getMethod("parseFrom", byte[].class);
        Object result = m.invoke(null, anyMessage.getBytesData().toByteArray());
        return result;
    } catch (InvalidProtocolBufferException | ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        log.error("",e);
        throw new MessageConversionException("Only AnyMessage should be sent.", e);
    }
}

}
aerobiotic
  • 381
  • 2
  • 5