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

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")
);

Related

Apache Camel PollEnrich cacheSize(-1)

I'm using a route to perform this:
When a file is sent to sftpEndpoint, I use pollEnrich to check if I received and Ack for this file.
If the ack file containt an error code, I throw an exception and the route is performed again to redeliver the file.
And when I try to check the ack another time in the first redelivery, the ack file isn't consumed beacause it has the same name as the ack generated in the first time (the ack name must not be changed after the redelivery).
from(sendFileToSftpEndpoint)
.errorHandler(noErrorHandler()).to(sftpEndpoint)
.pollEnrich().simple(sftpAckEndpoint)
.aggregationStrategy(new RequestAcknowledgementAggregator())
.process(new AcknowledgmentReceivingChecker())
According to the documentation, I have to use cacheSize(-1).
from(sendFileToSftpEndpoint)
.errorHandler(noErrorHandler()).to(sftpEndpoint)
.pollEnrich().simple(sftpAckEndpoint).cacheSize(-1)
.aggregationStrategy(new RequestAcknowledgementAggregator())
.process(new AcknowledgmentReceivingChecker())
But using cacheSize(-1) resolve the problem only for the first redelivery as the ack file is consumed again, but in the next redelivery (2nd 3rd ...) it's not consumed again.

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.

Apache Camel route with no "to" endpoint

I am using Apache Camel to assist with capturing message data emitted by a third party software package. In this particular instance, I only need to capture what is produced by the software, there is no receiver on the other end (really no "end" to go to).
So, I tried to set up a route with just the "from" endpoint and no "to" endpoint. Apparently this is incorrect usage as I received the following exception:
[2018-08-15 11:08:03.205] ERROR: string.Launcher:191 - Exception
org.apache.camel.FailedToCreateRouteException: Failed to create route route1 at: >>> From[mina:udp://localhost:9877?sync=false] <<< in route: Route(route1)[[From[mina:udp://localhost:9877?sync=false]] -... because of Route route1 has no output processors. You need to add outputs to the route such as to("log:foo").
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1063)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:196)
at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:974)
at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:3301)
at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:3024)
at org.apache.camel.impl.DefaultCamelContext.access$000(DefaultCamelContext.java:175)
at org.apache.camel.impl.DefaultCamelContext$2.call(DefaultCamelContext.java:2854)
at org.apache.camel.impl.DefaultCamelContext$2.call(DefaultCamelContext.java:2850)
at org.apache.camel.impl.DefaultCamelContext.doWithDefinedClassLoader(DefaultCamelContext.java:2873)
at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:2850)
at org.apache.camel.support.ServiceSupport.start(ServiceSupport.java:61)
at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:2819)
at {removed}.Launcher.startCamel(Launcher.java:189)
at {removed}.Launcher.main(Launcher.java:125)
Caused by: java.lang.IllegalArgumentException: Route route1 has no output processors. You need to add outputs to the route such as to("log:foo").
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1061)
... 13 more
How do I set up a camel route that allows me to intercept (capture) the message traffic coming from the source, and not send it "to" anything? There is no need for a receiver. What would be an appropriate "to" endpoint that just drops everything it receives?
The exception suggestion of to("log:foo"). What does this do?
You can see if the Stub component can help
http://camel.apache.org/stub.html
Example:
from("...")
.to("stub:nowhere");
The exception suggestion of to("log:foo"). What does this do?
It sends your route messages to an endpoint with a component of type log:
(http://camel.apache.org/log.html) - component which basically dumps message contents (body and/or headers and/or properties) to your log file using appropriate log category.
If you just want to drop everything received, it's a good choice:
to("log:com.company.camel.sample?level=TRACE&showAll=true&multiline=true")
Apparently if you're under Linux or Unix, you can also redirect to /dev/null like in this example:
to( "file:/dev?fileName=null")
I am not sure it can be used on Windows but I don't think so.
Note that the syntax: to( "file:/dev/null") does not work as it point to a directory called null but with the fileName option it will work.

Camel errorHandler / deadLetterChannel REST response

I have a Camel rest endpoint (Jetty) which validates and processes incoming requests. Besides specific Exception handlers (onException) it uses a DLQ error handler (errorHandler(deadLetterChannel...)) which is setup to retry 3 times - if unsuccessful the message is moved to the DLQ.
My question is, how do I still return a user friendly error message back to the client if an unexpected Exception occurs rather than the full Exception body? Is there some config I'm missing on the errorHandler?
I've tried to find some examples on the camel unit tests (DeadLetterChannelHandledExampleTest) and camel in action 2 (Chapter 11) but none seemed to have specific examples for this scenario.
Code is:
.from(ROUTE_URI)
.errorHandler(deadLetterChannel("{{activemq.webhook.dlq.queue}}")
.onPrepareFailure(new FailureProcessor())
.maximumRedeliveries(3)
.redeliveryDelay(1000))
.bean(ParcelProcessor.class, "process");
Thank you for your help!
Use a 2nd route as the DLQ, eg direct:dead and then send the message first to the real DLQ, and then do the message transformation afterwards to return a friendly response.
errorHandler(deadLetterChannel("direct:dead")
from("direct:dead")
.to("{{activemq.webhook.dlq.queue}}")
.transform(constant("Sorry something was wrong"));

mg_send_response_line() in mongoose not working

I am trying to develop a server application using mongoose C library. In the initial stage of the my try, I am stuck at sending a response for an HTTP request. I am trying to send a simple response of status 200 using following line of code:
mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
printf("Response sent...\n");
But the response is not received to the client(Postman or web-browser).
There is no error, and even the printf line of Response sent is printed.
As against to this, following lines are getting executed successfully:
mg_http_send_error(nc,404, "Fatal Error!"); // I get this error at client side.
The basic simplest_web_server also works fine. Why is my single line of code sending response failing. I am not able to understand/ debug this.
Regards,
Neeraj.
The issue is that there is no content length or transfer encoding specified for the HTTP response and the server does not close the connection so the client hangs waiting for the response.
If you look through the source code, you will see that in mg_http_send_error(), the MG_F_SEND_AND_CLOSE flag is set but it is not set within mg_send_response_line() (though, like you, I assumed that this would be handled by the function).
To fix the issue in your context,
mg_send_response_line(nc, 200, "Access-Control-Allow-Origin: *");
printf("Response sent...\n");
nc->flag |= MG_F_SEND_AND_CLOSE;

Resources