Search code examples
apache-camelqueueamqpspring-amqpsolace

How to get AMQP Message properties in Apache Camel AMQP Component


I have a Springboot application using Apache Camel AMQP component to comsume messages from a Solace Queue. To send a message to the Queue I use Postman and the Solace REST API. In order to differentiate the message type I add Content-Type to the header of the Http request in Postman. I used SDKPerf to check the message header consumed from solace and the message header is found under "HTTP Content Type" along with other headers.

However, I can't seem to find a way to get this Content-Type from Camel Side. In the documentation it says

String header = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);

However this always produces null. Any Ideas how to get the message properties in Camel?


Solution

  • EDIT: I think it's actually due to the fact that Camel is using QPid JMS, and there is no JMS API way of getting the Content Type, it's not in the spec. Even though AMQP 1.0 does support content-type as a property. But yeah, my suggestion of a custom property below is still probably the way I would go.


    Edited for clarity & corrections. TL/DR: use a custom user property header.

    The SMF Content Type header in the original (REST) message is passed through to the consumed AMQP message as a property content-type, however the JMS API spec does not expose this; there is no way in standard JMS to retrieve this value. It is, however, used by the broker to set the type of message (e.g. TextMessage). Check "Content-Type Mapping to Solace Message Types" in the Solace docs.

    Using Solace's SDKPerf AMQP JMS edition to dump the received message to console (note this uses QPid libraries):

    ./sdkperf_jmsamqp.sh -cip=amqp://localhost:5672 -stl=a/b/c
     -md -q
    
    curl http://localhost:9000/TOPIC/a/b/c -d 'hello' -H 'Content-Type: text'
    
    ^^^^^^^^^^^^^^^^^^ Start Message ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    JMSDeliveryMode:                        PERSISTENT
    JMSDestination:                         a/b/c
    JMSExpiration:                          0
    JMSPriority:                            4
    JMSTimestamp:                           0
    JMSRedelivered:                         false
    JMSCorrelationID:                       null
    JMSMessageID:                           null
    JMSReplyTo:                             null
    JMSType:                                null
    JMSProperties:                          {JMSXDeliveryCount:1;}
    Object Type:                            TextMessage
    Text:                                   len=5
      hello
    

    The header does not get mapped through, but does get used to set the message type. If I remove that HTTP header, the received AMQP message is binary. But since other types of Content-Types also map to TextMessages (e.g. application/json, application/xml, etc.), the fact you're receiving a TextMessage is not enough to infer exactly what Content-Type you published your REST message with.

    For completeness, I used WireShark with an AMQP decoder, and you can see the header present on the received AMQP message:

    Frame 3: 218 bytes on wire (1744 bits), 218 bytes captured (1744 bits) on interface \Device\NPF_Loopback, id 0
    Null/Loopback
    Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
    Transmission Control Protocol, Src Port: 5672, Dst Port: 60662, Seq: 2, Ack: 1, Len: 174
    Advanced Message Queueing Protocol
        Length: 174
        Doff: 2
        Type: AMQP (0)
        Channel: 2
        Performative: transfer (20)
        Arguments (5)
        Message-Header
            Durable: True
        Message-Annotations (map of 1 element)
            x-opt-jms-dest (byte): 1
        Message-Properties
            To: a/b/c
            Content-Type: text                       <----------
        Application-Properties (map of 1 element)
            AaronEncoding (str8-utf8): CustomText
        AMQP-Value (str32-utf8): hello
    

    So my suggestion is this:

    Set an additional custom header, a User Property, which will get passed through to the AMQP message:

    curl http://localhost:9000/TOPIC/a/b/c -d 'hello' -H 'Solace-User-Property-AaronEncoding: CustomText' -H 'Content-Type: text'
    
    JMSDestination:                         a/b/c
    JMSProperties:                          {AaronEncoding:CustomText;JMSXDeliveryCount:1;}
    Object Type:                            TextMessage
    Text:                                   len=5
      hello
    

    My always-goto guide for Solace REST interactions: https://docs.solace.com/API/RESTMessagingPrtl/Solace-REST-Message-Encoding.htm

    Hope that helps!