Apache Camel Azure queue: Message body is empty while sending message - apache-camel

I am trying to send a message to Azure Queue using Apache Camel, route code is:
from("direct:testMessage")
.process(exchange -> {
exchange.getIn().setBody("test message");
})
.to("azure-storage-queue://azureaccount/test-queue?operation=sendMessage");
The code to initiate Azure service client bean is:
#Bean
public QueueServiceClient queueServiceClient() {
StorageSharedKeyCredential credential = new StorageSharedKeyCredential(ACCOUNT, ACCESS_KEY);
return new QueueServiceClientBuilder().endpoint(QUEUE_URL).credential(credential).buildClient();
}
I am getting the below error:
com.azure.storage.queue.models.QueueStorageException: Status code 400, "<?xml version="1.0" encoding="utf-8"?><Error><Code>InvalidXmlDocument</Code><Message>XML specified is not syntactically valid.
RequestId:e3496af3-f003-001a-0d2e-a38636000000
Time:2020-10-15T20:08:26.5431469Z</Message><LineNumber>0</LineNumber><LinePosition>0</LinePosition><Reason /></Error>"
Tried enabling HTTP logs and looks like the Message body is empty:
2020-10-16 01:38:25 INFO c.a.s.q.i.M.enqueue - --> POST https://azureaccount.queue.core.windows.net/blob-backup-queue/messages
53-byte body:
<?xml version='1.0' encoding='UTF-8'?><QueueMessage/>
--> END POST
2020-10-16 01:38:25 INFO c.a.s.q.i.M.enqueue - <-- 400 https://azureaccount.queue.core.windows.net/blob-backup-queue/messages (163 ms, 294-byte body)
Response body:
<?xml version="1.0" encoding="utf-8"?><Error><Code>InvalidXmlDocument</Code><Message>XML specified is not syntactically valid.
RequestId:e3496af3-f003-001a-0d2e-a38636000000
Time:2020-10-15T20:08:26.5431469Z</Message><LineNumber>0</LineNumber><LinePosition>0</LinePosition><Reason /></Error>
<-- END HTTP

Going through the source code of camel-azure component, figured out the right way to send message is as follows:
exchange.getIn().getHeaders().put(QueueConstants.MESSAGE_TEXT, "test message");

Related

Application REST Client on Karaf

I'am writing a simple . application deploying on Karaf 4.1.0. It's role is sending a rest request to REST API. When I start my bundle I have an error:
javax.ws.rs.ProcessingException: org.apache.cxf.interceptor.Fault: No message body writer has been found for class package.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.WebClient.doResponse(WebClient.java:1149)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1094)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)
at org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:428)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1631)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.method(WebClient.java:1626)
at org.apache.cxf.jaxrs.client.WebClient$SyncInvokerImpl.post(WebClient.java:1566)
at org.apache.cxf.jaxrs.client.spec.InvocationBuilderImpl.post(InvocationBuilderImpl.java:145)
at package.worker.service.implementation.ConnectionServiceImpl.postCheckRequest(ConnectionServiceImpl.java:114)
at package.worker.service.implementation.ConnectionServiceImpl.sendCheck(ConnectionServiceImpl.java:103)
at package.worker.module.QueueSharedListener.run(QueueSharedListener.java:37)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.cxf.interceptor.Fault: No message body writer has been found for class package.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1222)
at org.apache.cxf.jaxrs.client.AbstractClient$AbstractBodyWriter.handleMessage(AbstractClient.java:1091)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
at org.apache.cxf.jaxrs.client.AbstractClient.doRunInterceptorChain(AbstractClient.java:649)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1093)
... 11 more
Caused by: javax.ws.rs.ProcessingException: No message body writer has been found for class com.emot.dto.QueueSharedDTO, ContentType: application/json
at org.apache.cxf.jaxrs.client.AbstractClient.reportMessageHandlerProblem(AbstractClient.java:780)
at org.apache.cxf.jaxrs.client.AbstractClient.writeBody(AbstractClient.java:494)
at org.apache.cxf.jaxrs.client.WebClient$BodyWriter.doWriteBody(WebClient.java:1217)
... 15 more
Initialization WebTarget:
private ConnectionServiceImpl() {
client = ClientBuilder.newClient();
client.property(
ClientProperties.CONNECT_TIMEOUT,
snifferProperties.getProperty(SnifferProperties.PARAM_REST_API_CONNECTION_TIMEOUT));
client.property(
ClientProperties.READ_TIMEOUT,
snifferProperties.getProperty(SnifferProperties.PARAM_REST_API_READ_TIMEOUT));
System.out.println(2);
webTarget = client.target(buildUrl());
}
Send requests :
private synchronized boolean postCheckRequest(String path, Object content) {
boolean result = true;
try {
Response response = webTarget
.path("check")
.path("add/one")
.request(MediaType.APPLICATION_JSON)
.post(Entity.json(content));
result = (response.getStatus() == 200);
} catch (Exception e) {
System.out.println("Error but working");
e.printStackTrace();
result = false;
}
return result;
}
I have always the problems with Karaf... i dont understand why it . couldn't working correctly...
The issue you are facing is mostly not a Karaf issue, but a typical issue you may face while working with some JAX-RS implementation in non-JavaEE environment.
Exception literally says that your implementation misses message body writer. Message body writer is the class which implements class javax.ws.rs.ext.MessageBodyWriter and is responsible for serializing your data objects to some format (like JSON). There is another class named javax.ws.rs.ext.MessageBodyReader, which does the opposite thing. All these classes are registered to JAX-RS framework as providers, extending its capabilities. Details are here: https://jersey.java.net/documentation/latest/message-body-workers.html
So, generally you must decide what you use for serializing/deserializing between your data objects and HTTP MediaType and register a proper JAX-RS provider.
With Jackson, for example, your problem can be easily solved by using one of its standard implementation: either com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider, if you use JAXB annotations, or com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider, if you prefer Jackson annotations. Add this class in providers section of your Blueprint descriptor:
<jaxrs:server id="restServer" address="/rest">
<jaxrs:serviceBeans>
....
</jaxrs:serviceBeans>
<jaxrs:providers>
....
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
....
</jaxrs:providers>
</jaxrs:server>

Apache Camel: Unable to get the Exception Body

Whenever there is normal flow in my Camel Routes I am able to get the body in the next component. But whenever there is an exception(Http 401 or 500) I am unable to get the exception body. I just get a java exception in my server logs.
I have also tried onException().. Using that the flow goes into it on error, but still I do not get the error response body that was sent by the web service(which I get when using POSTMAN directly), I only get the request in the body that I had sent to the web service.
Also adding the route:
from("direct:contractUpdateAds")
.to("log:inside_direct:contractUpdateAds_route_CompleteLog?level=INFO&showAll=true&multiline=true")
.streamCaching()
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.log("before calling ADS for ContractUpdate:\nBody:${body}")
.to("{{AdsContractUpdateEndpoint}}")
.log("after calling ADS for ContractUpdate:\nBody:${body}")
.convertBodyTo(String.class)
.end();
Option 1: handle failure status codes yourself
The throwExceptionOnFailure=false endpoint option (available at least for camel-http and camel-http4 endpoints) is probably what you want. With this option, camel-http will no longer consider an HTTP Status >= 300 as an error, and will let you decide what to do - including processing the response body however you see fit.
Something along those lines should work :
from("...")
.to("http://{{hostName}}?throwExceptionOnFailure=false")
.choice()
.when(header(Exchange.HTTP_RESPONSE_CODE).isLessThan(300))
// HTTP status < 300
.to("...")
.otherwise()
// HTTP status >= 300 : would throw an exception if we had "throwExceptionOnFailure=true"
.log("Error response: ${body}")
.to("...");
This is an interesting approach if you want to have special handling for certains status codes for example. Note that the logic can be reused in several routes by using direct endpoints, just like any other piece of Camel route logic.
Option 2 : Access the HttpOperationFailedException in the onException
If you want to keep the default error handling, but you want to access the response body in the exception handling code for some reason, you just need to access the responseBody property on the HttpOperationFailedException.
Here's an example:
onException(HttpOperationFailedException.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
// e won't be null because we only catch HttpOperationFailedException;
// otherwise, we'd need to check for null.
final HttpOperationFailedException e =
exchange.getProperty(Exchange.EXCEPTION_CAUGHT, HttpOperationFailedException.class);
// Do something with the responseBody
final String responseBody = e.getResponseBody();
}
});

How can I disable Websphere's 8.5 default 4xx message?

I have a Jersey REST application in Websphere 8.5. Whenever my resource responds with a 4xx response, Webpshere (or IBM Http Server) overwrites the response body with its default error message, for example:
Error 404: Not Found
Not only don't I want the response body to be overwritten, I want the response body as produced by my resource, but also Websphere does not update the Content-Length: response header, thereby creating an inconsistency between content-length and the actual response body length.
Is there a way to force Websphere (or IBM HTTP server) to not overwrite the response body when my resource produces a 4xx response?
For example a call to the following resource:
#Path("timeout")
public class TimeoutService {
#GET
#Path("withbody")
#Produces(MediaType.APPLICATION_JSON)
public Response getWithBody() {
Response.ResponseBuilder builder = Response.status(Response.Status.NOT_FOUND);
builder.entity("{ \"status\" : \"notok\" }");
return builder.build();
}
}
will result in:
Error 404: No
Request Method:GET
Status Code:404 Not Found
Response Headers
$WSEP:
Connection:Keep-Alive
Content-Language:en-US
Content-Length:13
Content-Type:text/html;charset=ISO-8859-1
Date:Tue,21 Apr 2015 11:50:38 GMT>
Keep-Alive:timeout=15, max=100
X-Powered-By:Servlet/3.0
Note how the default message gets truncated because of the inconsistent content length.
But what I want is this call to respond with a 404 and { "status" : "notok" } and Content-Type set to application/json
Yes, you can. Here is the page that outlines the Jersey property that needs to be changed to disable WAS hi-jacking your errors:
https://java.net/jira/browse/JERSEY-2521
In short, you have to set the property org.glassfish.jersey.server.ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR to true when you configure Jersey.

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

CXF - catching/handling HTTP exceptions

This is rather a basic cxf usage question. How/where can we catch the actual HTTP exception/error. I kind of followed the Interceptor/MessageObserver concept but could not capture the HTTP error using them.
I see this error in the log4j log file.
Caused by: org.apache.cxf.transport.http.HTTPException: HTTP response
'401: Unauthorized' when communicating with http://10.107.172.79/test/_vti_bin/lists.asmx
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1502)
at org.apache.cxf.transpot.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1448)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1356)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:614)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
... 9 more
Only javax.xml.ws.WebServiceException with "Could not send Message."
message is thrown while calling the service
try{
GetListCollectionResult result = port.getListCollection();
}catch (javax.xml.ws.WebServiceException excep){
}
This is how we call the service.
To provide NTLM credentials:
Authenticator.setDefault( extended class of Authenticator);
Create the service.
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(ListsSoap.class);
factory.setAddress(list_url);
ListsSoap port = (ListsSoap) factory.create();
Update the conduit.
..
Client client = ClientProxy.getClient(port);
HTTPConduit http = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
http.setClient(httpClientPolicy);
Call service and get the result.
GetListCollectionResult result = port.getListCollection();
Nevermind, I found the answer in the CXF mailing list.
excep.getCause()
gives access to the underlying exception, in my case it is the HTTP Transport exception.

Resources