How to get AMQP Message properties in Apache Camel AMQP Component - apache-camel

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?

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.
https://camel.apache.org/components/3.20.x/amqp-component.html
https://www.amqp.org/sites/amqp.org/files/amqp.pdf
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!

It may have a different name in Camel. Try either printing all the headers or stop it in the debugger and examine the incoming message.

Related

HTTP response message generated by server is not being recognized as a response

I'm programming a simple web server in C. The HTTP message the server generates is stored in a buffer and sent (via send()) as follows:
Scenario 1:
"200 OK\nContent-Type: text/html\nContent-Length: " + resource size in bytes + "\r\n\n"
where the resource size in bytes is converted to a char array using snprintf and then concatenated into the string.
Scenario 2:
"HTTP1.1 404 Not Found\r\nContent-Length: 0\r\n\n"
Scenario 3:
"HTTP1.1 405 Method Not Allowed\r\nAllow: GET, HEAD\r\n"
These are the headers, they are sent beforehand. The message body is sent afterwards as follows:
char resource[length];
int numRead;
while ( (numRead= read(filefd, resource, length)) > 0 )
send(client, resource, length, 0);
When I use wireshark, it doesn't recognize it as an HTTP response. When I use firefox, the web page continues loading until I shut down the server, at which point it displays the HTTP response instead of a webpage (index.html):
image
Do I have to encode the message before sending? Or is there something wrong with my message format?
There are many errors here which suggest that you just made up your own version of HTTP instead of reading the standard. Please refer to the standard for all the details if you want to implement HTTP.
In short:
"200 OK\nContent-Type: text/html\nContent-Length: " + resource size in bytes + "\r\n\n"
It should be HTTP/1.1 200 OK... and not just 200 OK.... All line ends must be \r\n and not \n and there must be a single \r\n at the end of the HTTP header.
"HTTP1.1 404 Not Found\r\nContent-Length: 0\r\n\n"
"HTTP1.1 405 Method Not Allowed\r\nAllow: GET, HEAD\r\n"
It should be HTTP/1.1 not HTTP1.1. And then all the other problems.
Again, if you really want to implement your own HTTP stack then study the standard. It is far more complex than you might think. Just because HTTP is a text based protocol does not mean that it is simple nor that any text is a correct HTTP message. Additionally implementations are tolerant in different ways, so just because it works with one client (i.e. browser) does not mean it is correct and that it will work with a different client too.

How to read acknowledgement after send action in Citrus with Camel MLLP Component

I have an application that receives a HL7 message and sends back an acknowledgement response. I'm using Citrus with the Camel MLLP component to send this HL7 message. What I'm trying to achieve is to be able to read the acknowledgement to compare it.
I currently have a test with hl7Payload, a String variable with HL7 content. In my Citrus context I have:
<citrus-camel:sync-endpoint id="mllpOutEndpoint"
camel-context="mllpContext"
endpoint-uri="mllp:localhost:6662"/>
I tried extracting the header I found on the Camel documentation:
send("mllpOutEndpoint")
.fork(true)
.messageType(MessageType.PLAINTEXT)
.payload(hl7Payload)
.extractFromHeader("CamelMllpAcknowledgementString", "receivedAck");
echo("${receivedAck}");
But I get this error:
com.consol.citrus.exceptions.UnknownElementException: Could not find header element CamelMllpAcknowledgementString in received header
Everything works fine without extractFromHeader(). The application receives my HL7 message, sends back an ACK and the test passes, but I'm struggling to get this ACK content back to make further tests. What am I missing here?
I got it:
async().actions(
send("mllpOutEndpoint")
.messageType(MessageType.PLAINTEXT)
.payload(hl7Payload),
receive("mllpOutEndpoint")
.header("CamelMllpAcknowledgementType", "AA")
);

How to send MQ message without RFH header in C?

How to send MQ message without RFH header in C or in other words how do i send NonJMS MQ message using 'C' library interface?
Basically, is there any 'C' equivalent of
((com.ibm.mq.jms.MQQueue) queue).setTargetClient(JMSC.MQJMS_CLIENT_NONJMS_MQ);
Following 'C' MQ calls I am making
MQCONNX(qmgrName, &mqcno, &hConn_, &compCode, &cReason);
MQOPEN(hConn_, &od, openOptions, &hObj_, &openCode, &reason)
MQCRTMH(hConn_, &cmho, &hMsg, &createCode, &reason)
MQSETMP(hConn_, hMsg, &smpo, &prop, &pd, MQTYPE_STRING, propVal.length(), propVal, &compCode, &reason);
pmo.Version = MQPMO_VERSION_3;
pmo.OriginalMsgHandle = hMsg;
MQPUT(hConn_, hObj_, NULL, &pmo, msg._theMessage.length(), buffer, &compCode, &reason);
MQDLTMH(hConn_, &hMsg, &dmho, &compCode, &reason);
pmo.OriginalMsgHandle = hMsg //This line is causing RFH header
MQ Receiver is giving following output. I am using C++ MQ interface to receive the message because that's what existing code is doing and need to make sure that C generated msgs can be read by C++ receiver
2024489 - 2019-09-26 09:00:05.691154 Receiver: Received Message from MQ of size 490
2024489 - 2019-09-26 09:00:05.691163 Receiver: Received Message from MQ --> RFH ^B
std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string (this=0x6ce7938,
__str="RFH \002\000\000\000P\000\000\000\"\002\000\000\063\003\000\000MQSTR \000\000\000\000\270\004\000\000(\000\000\000<usr><GROUP_ID>1</GROUP_ID></usr> corrId: \"CORR_ID\"\nchannel: \"HIFI\"\nemp
Ids {\n empId {\n type: \"CALLER_NO\"\n value: \"123456"...)
The IBM MQ classes for JMS API and the XMS APIs (C++ and .NET) are the only APIs that default to sending a RFH2 header.
The setting below that you mention is specific to the JMS API (there would be something similar or the same for XMS) and tells the API that the app receiving the message is not a JMS app so do not send the RFH2 header:
((com.ibm.mq.jms.MQQueue) queue).setTargetClient(JMSC.MQJMS_CLIENT_NONJMS_MQ);
If you are using the C API to send messages it will NOT have a RFH2 header so there is no setting to turn off what is not sent.
There are 2 ways for a C program to handle JMS (aka MQRFH2) messages.
As you saw, the default behavior is for GMO Options field to have MQGMO_PROPERTIES_AS_Q_DEF and the queue's Property Control attribute to be set to Compatibility. Hence, when your application gets a message, it will have the MQRFH2 structure.
If you changed the GMO Options field to have MQGMO_PROPERTIES_IN_HANDLE then when your application gets a message, it will receives just the message payload and all of the message properties will be available via the message handle.
In the sample MQ programs included with IBM MQ, there is one called amqsbcg0.c. There are 2 builds of it: amqsbcg (bindings mode) and amqsbcgc (client mode).
It takes up to 3 parameters: QueueName, QMgrName and PropertyOptions
(1) If you run it without any property options or property options set to 0 then it will set GMO Options field to have MQGMO_PROPERTIES_AS_Q_DEF. Hence, if the message on the queue is a JMS message then the program will output an MQRFH2 structure.
(2) If you run it with property options set to 1 then it will set GMO Options field to have MQGMO_PROPERTIES_IN_HANDLE. Hence, if the message on the queue is a JMS message then then program will output the message properties followed by the message payload.

about qpid exchange,queue

every one:
i am new to qpid and encounter some problem. the exchange created by me can’t route message to the queue, as follows:
first i create a durbale queue “test-queue-1” in the qpid use the quid-config command:
qpid-config add queue test-queue-1 --durable
next i create a durable direct exchange “test-exchange-1" in the qpid also use the qpid-config command:
qpid-config add exchange direct test-exchange-1 --durable
the last, in bind them as follow command:
qpid-config bind test-exchange-1 test-queue-1 test-queue-1
everything seems ok in the qpid-tool:
Object Summary:
ID Created Destroyed Index
========================================================================================
128 12:28:28 - org.apache.qpid.broker:queue:qmfc-v2-hb-iZ23c6sri0pZ.12680.1
129 12:28:28 - org.apache.qpid.broker:queue:qmfc-v2-iZ23c6sri0pZ.12680.1
130 12:28:28 - org.apache.qpid.broker:queue:qmfc-v2-ui-iZ23c6sri0pZ.12680.1
131 12:28:28 - org.apache.qpid.broker:queue:reply-iZ23c6sri0pZ.12680.1
132 12:24:17 - org.apache.qpid.broker:queue:test-queue-1
133 12:28:28 - org.apache.qpid.broker:queue:topic-iZ23c6sri0pZ.12680.1
116 12:27:20 -
and
org.apache.qpid.broker:binding:org.apache.qpid.broker:exchange:test-exchange-1,org.apache.qpid.broker:queue:test-queue-1,test-queue-1
now i am ready to test them, start the recv/send demo program:
[devel#iZ23c6sri0pZ build]$ ./recv amqp://127.0.0.1/test-queue-1
send the message:
[devel#iZ23c6sri0pZ build]$ ./send -a amqp://127.0.0.1/test-exchange-1 hi,everyone
but the "recv program” can’t recv any message.
if i send message like this :
[devel#iZ23c6sri0pZ build]$ ./send -a amqp://127.0.0.1/test-queue-1 hi,everyone
the “recv program” can recv the message:
Address: amqp://127.0.0.1/test-queue-1
Subject: Hello Subject
Content: "hi,everyone"
who can tell me why?i read the amqp protocol, maybe the routing-key in the message don’t match the binding-key, but if this, how could i set the routing-key?
my recv/send writed by proton-c , version 0.8. qpidd is 0.32 version.
When you send a message to a qpid direct exchange, it gets routed to a bound queue based on the routing-key of the message. In proton-c you can set the routing key by setting the message-subject using the function
PN_EXTERN int pn_message_set_subject (pn_message_t* msg,const char* subject)
Unfortunately this is not implemented in the example send.c that is shipped with proton-c v0.8 You can insert the following line somewhere around here and rebuild your send executable
pn_message_set_subject(message, "my-routing-key");
You can also with some effort, add a new command-line option to accept and use a routing-key from ./send
The java example implements a -s option to set the message subject.
I too think it's a binding issue.
Try binding with following,
qpid-config bind test-exchange-1 test-queue-1 test-exchange-1
#Feng Fang: "test-exchange-1" is a routing key which is you are using while sending a message. If that does not give a try with "test-exchange-1/test-exchange-1"
Keep rest as-is and give a try.
I hope this helps!

How to get instance of current message bein processed in cxf?

I want to get the ID of the Inbound message in my implemented service end point which has following parameters available:
Custom JAXB Request
#Context HttpServletRequest
e.g. From below inbound message i want to retrieve ID: 1 in my service endpoint.
INFO: Inbound Message
ID: 1
Address:
Encoding: ISO-8859-1
Http-Method: POST
Content-Type: application/xml
Headers:
Payload:
Can anyone please tell me if there is a way to get that ID ?
You can get the current CXF Message using PhaseInterceptorChain.getCurrentMessage(). The logging ID used by the logging interceptors is stored in the Message Map, and can be retrieved with its key, e.g.
String loggingId = (String) PhaseInterceptorChain.getCurrentMessage().get(LoggingMessage.ID_KEY);

Resources