Apache Camel AS2 component cannot not process application/xml message - apache-camel

I tested to send a message with content-type set to application/xml to a Camel/AS2 server connection. However, I got 500 response. Does Camel/AS2 support application/xml content-typed messages?
Here comes the stack trace:
2022-01-16 11:44:23,725 [AS2Hdlr-6405] INFO .AS2ServerConnection [] - Processing new AS2 request
2022-01-16 11:44:23,725 [AS2Hdlr-6405] WARN nent.as2.AS2Consumer [] - Failed to process AS2 message
org.apache.http.HttpException: Failed to extract EDI message: invalid content type 'application/xml' for AS2 request message
at org.apache.camel.component.as2.api.util.HttpMessageUtils.extractEdiPayload(HttpMessageUtils.java:169) ~[camel-as2-api-3.13.0.jar!/:3.13.0]
at org.apache.camel.component.as2.AS2Consumer.handle(AS2Consumer.java:124) [camel-as2-3.13.0.jar!/:3.13.0]
at org.apache.http.protocol.HttpService.doService(HttpService.java:437) [wildfly-elytron-1.18.1.Final.jar!/:1.18.1.Final]
at org.apache.http.protocol.HttpService.handleRequest(HttpService.java:342) [wildfly-elytron-1.18.1.Final.jar!/:1.18.1.Final]
at org.apache.camel.component.as2.api.AS2ServerConnection$RequestHandlerThread.run(AS2ServerConnection.java:147) [camel-as2-api-3.13.0.jar!/:3.13.0]
2022-01-16 11:44:23,725 [AS2Hdlr-6405] INFO .AS2ServerConnection [] - Client closed connection

Related

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?
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.

Issue with Apache Camel https rest api with username and password

I have the following piece of code, I've built for connecting to a "https" REST end point using Apache Camel. The problem is that I get 401 error if this is run.
from("timer:learnTimer?period=100s")
.to("log:?level=INFO&showBody=true")
.setHeader("currentTime", simple(currentTime))
.setHeader(Exchange.CONTENT_TYPE,constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.setHeader(Exchange.HTTP_URI, simple("https://xxxxxx/api/siem/offenses?filter=status%20%3D%20%22OPEN%22%20and%20start_time%20%3E%201543647979000?&authMethod=Basic&authUsername=xxxxx&authPassword=xxxxx"))
.to("https://xxxxxxx/api/siem/offenses?filter=status%20%3D%20%22OPEN%22%20and%20start_time%20%3E%201543647979000?&authMethod=Basic&authUsername=xxxx&authPassword=xxxx").convertBodyTo(String.class)
.to("log:?level=INFO&showBody=true");
The error I am receiving is:
Stacktrace
org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking https://xx.xx.xx.xx/api/siem/offenses?filter=status+%3D+%22OPEN%22+and+start_time+%3E+1543647979000%3F with statusCode: 401
at org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException(HttpProducer.java:243)
at org.apache.camel.component.http.HttpProducer.process(HttpProducer.java:165)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:148)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:138)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:101)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:197)
at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:79)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
15:16| WARN | CamelLogger.java 213 | Error processing exchange. Exchange[ID-zabbixproxy-node2-1544019394005-0-1]. Caused by: [org.apache.camel.http.common.HttpOperationFailedException - HTTP operation failed invoking https://xx.xx.xx.xx/api/siem/offenses?filter=status+%3D+%22OPEN%22+and+start_time+%3E+1543647979000%3F with statusCode: 401]
org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking https://10.96.40.66/api/siem/offenses?filter=status+%3D+%22OPEN%22+and+start_time+%3E+1543647979000%3F with statusCode: 401
at org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException(HttpProducer.java:243)
at org.apache.camel.component.http.HttpProducer.process(HttpProducer.java:165)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:148)
Are you sure you should set these header before making an rest call?
un necessary request headers in IN Message may cause some issue.
Exchange exchange = ExchangeBuilder.anExchange(camelContext)
.withHeader("").withProperty("")
.withPattern(ExchangePattern...)
.withHeader(Exchange.HTTP_METHOD, HttpMethod.GET)
.build();
producer.send("the end point to rest",exchange);
// producer is ProducerTemaplte
In above code you can set The ExchangePattern and required Headers and property (if only needed).
Hope this helps.

Camel HL7 - ClosedChannelException while sending ACK back to the client

I'm building a HL7 listener using netty4 and processing HL7 messages. Once succesfully processed an ACK is sent back.
from("hl7NettyListener")
.routeId("route_hl7listener")
.startupOrder(997)
.unmarshal()
.hl7(false)
.to("direct:a");
from("direct:a")
.doTry()
.to("bean:processHL7?method=process")
.doCatch(HL7Exception.class)
.to("direct:ErrorACK")
//.transform(ack())
.stop()
.end()
.transform(ack())
.wireTap("direct:b");
This is working fine in my local eclipse. I fire a HL7 message and I get a ACk back.
But i package this application into a jar and put it on my server and then try doing a
cat example.hl7 | netcat localhost 4444 (to fire a HL7 message to port 4444 on linux env)
I dont get an ACK back. I get a closedconnection exception.
DEBUG NettyConsumer - Channel: [id: 0xdf13b06b, L:0.0.0.0/0.0.0.0:4444] writing body: MSH|^~\&|Karisma||Kestral|Kestral|20180309144109.827+1300||ACK^R01|701||2.3.1
2018-03-09 14:41:09,838 [ad #3 - WireTap] DEBUG WireTapProcessor - >>>> (wiretap) direct:b Exchange[]
2018-03-09 14:41:09,839 [ServerTCPWorker] DEBUG NettyConsumer - Caused by: [org.apache.camel.CamelExchangeException - Cannot write response to null. Exchange[ID-annan06-56620-1520559639101-0-2]. Caused by: [java.nio.channels.ClosedChannelException - null]]
org.apache.camel.CamelExchangeException: Cannot write response to null. Exchange[ID-annan06-56620-1520559639101-0-2]. Caused by: [java.nio.channels.ClosedChannelException - null]
at org.apache.camel.component.netty4.handlers.ServerResponseFutureListener.operationComplete(ServerResponseFutureListener.java:54)
at org.apache.camel.component.netty4.handlers.ServerResponseFutureListener.operationComplete(ServerResponseFutureListener.java:36)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:514)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:488)
at io.netty.util.concurrent.DefaultPromise.access$000(DefaultPromise.java:34)
at io.netty.util.concurrent.DefaultPromise$1.run(DefaultPromise.java:438)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:418)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:440)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:873)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.nio.channels.ClosedChannelException
at io.netty.channel.AbstractChannel$AbstractUnsafe.write(...)(Unknown Source)
That worked. It was failing because netcat was immediately closing the connection. Had put a ”netcat -i 5 localhost” for netcat to wait for 5 secs and successfully received the ACK back.

Unable to register new destination using RegisterDestination in Amazon MWS API

When I try to register any new subscription destination using RegisterDestination (Amazon MWS Subscriptions API) I received error:
"An exception was thrown while attempting to Create a Destination. This can happen if the Destination has already been registered."
I try many randoms (100% unique) urls.
ListRegisteredDestination return empty list.
Thank for help
My Request:
POST /Subscriptions/2013-07-01?AWSAccessKeyId=myAccess-Key
&Action=RegisterDestination
&SellerId=My-Seller-ID
&SignatureVersion=2
&Timestamp=2017-01-27T18%3A48%3A28Z
&Version=2013-07-01
&Signature=cybKM%2BtLq4F%2Fv%2BZnGhPtyLHg%2FCiOk31WbG0tHmpGQFg%3D
&SignatureMethod=HmacSHA256
&MarketplaceId=MyMarketPlaceId
&Destination.DeliveryChannel=SQS
&Destination.AttributeList.member.1.Key=sqsQueueUrl
&Destination.AttributeList.member.1.Value=http%3A%2F%2Fsomeurl.pl%2Famazon%2Fnotlistner HTTP/1.1
Host: mws.amazonservices.de
x-amazon-user-agent: AmazonJavascriptScratchpad/1.0 (Language=Javascript)
Content-Type: text/xml

Accepting gzipped requests in CXF

I'm using Apache CXF to develop a JAX-RS service. My service is working, but I want to change it to accept compressed (gzipped) requests.
I've done some googling and added this to my beans.xml file:
<bean id="GZIPInInterceptor" class="org.apache.cxf.transport.common.gzip.GZIPInInterceptor"/>
<bean id="compressGZIPFeature" class="org.apache.cxf.transport.http.gzip.GZIPFeature"/>
<cxf:bus>
<cxf:inInterceptors>
<ref bean="GZIPInInterceptor"/>
</cxf:inInterceptors>
<cxf:features>
<ref bean="compressGZIPFeature"/>
<cxf:logging/>
</cxf:features>
</cxf:bus>
but when I call the service from my client application with a compressed request, I get the error Invalid UTF-8 start byte 0x8b and this in the log file:
28-May-2012 12:59:42 org.apache.cxf.interceptor.AbstractLoggingInterceptor log
INFO: Inbound Message
----------------------------
ID: 5
Address: http://localhost:8080/ ... /
Encoding: ISO-8859-1
Http-Method: POST
Content-Type: application/xml
Headers: {Accept=[application/xml], connection=[Keep-Alive], content-encoding=[g
zip], Content-Length=[246], content-type=[application/xml], expect=[100-continue
], host=[localhost:8080]}
Payload: ? ? ý¢`?I?%&/m-{¦J§JÎÓt?`?$Ï?#?ý-?-µ?ý?iG#)½*?-eVe]f?#¦Ý?+¸Ì{´¢¸Ì{´
¢¸¦;?N'¸¯ ?\fd?l÷+J++?!?¬+??~|??"? ?´?ez?ÎMQ-?¹hw+¾Q?/ºi¼X^|÷Ц=¯>°Þ¸8z|͵?ú4}|·
???¡-÷t+ÍÎgO?÷vv¸v÷w÷´¦ tg¦¸-/Õ+´Ý}¦¦w?¦+C¤/²®_·dþÚ¦²{;'??>³¶ßÚ+¦'¤?Ý|·õ¦±¦¦¦?O?
¯Ý>¥¦?·°.w
--------------------------------------
28-May-2012 12:59:43 org.apache.cxf.jaxrs.provider.AbstractJAXBProvider handleJA
XBException
WARNING: javax.xml.bind.UnmarshalException
- with linked exception:
[com.ctc.wstx.exc.WstxIOException: Invalid UTF-8 start byte 0x8b (at char #2, by
te #-1)]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStrea
mException(UnmarshallerImpl.java:426)
...
28-May-2012 12:59:43 org.apache.cxf.interceptor.AbstractLoggingInterceptor log
INFO: Outbound Message
---------------------------
ID: 5
Response-Code: 400
Content-Type: text/plain
Headers: {Content-Type=[text/plain], Date=[Mon, 28 May 2012 11:59:43 GMT]}
Payload: JAXBException occurred : Invalid UTF-8 start byte 0x8b (at char #2, byt
e #-1). Invalid UTF-8 start byte 0x8b (at char #2, byte #-1).
--------------------------------------
In CXF, you can use
#GZIP
annotations for compressing the messages
The answer was that the interceptor was not getting picked up from the XML file. I tried adding this annotation above my service method and it started working:
#org.apache.cxf.interceptor.InInterceptors (interceptors = {"org.apache.cxf.transport.common.gzip.GZIPInInterceptor" })

Resources