Component vert.x does not propagate headers - apache-camel

I'm using the Camel component Vertx: http://camel.apache.org/vertx.html
The headers set in Camel are not sent into the eventBus address:
from("direct:inbound")
.setHeader("myHeader").constant("myHeaderValue")
.to("vertx:inboundAddress");
inboundAddress does not receive the header "myHeader".
I've checked the code of the VertxProducer in Vertx Component and it actually only send the body.Is there any reason why the headers are not sent to the eventBus?
Btw, the documentation on GitHub is much more up to date: https://github.com/apache/camel/blob/master/components/camel-vertx/src/main/docs/vertx-component.adoc

I agree that the vertx headers should be accessible via camel. If someone sees an official way it is supported I would love to know. Until then there is an easy workaround though. Instead of passing just the body in the message pass both the headers and the body as the message body and both will be accessible.
JsonObject message = new JsonObject();
message.put("body", routingContext.getBodyAsJson());
DeliveryOptions d = new DeliveryOptions()
.addHeader("myheader", "myheadervalue")
.addHeader("h2", "h2value");
message.put("deliveryOptions", d.toJson());
vertx.eventBus().send("myVertxEndpoint",
message,...
Later in Camel, you could call a method similar to the following as part of your route to have the Vertx headers put inside the camel headers.
public void setVertxHeaders(#Body JsonObject jsonObject, Exchange exchange ) {
exchange.getIn().getHeaders()
.putAll(jsonObject.getJsonObject("deliveryOptions").getJsonObject("headers")
.getMap());
}

You're setting the headers incorrectly.
Instead of
.setHeader("myHeader").constant("myHeaderValue")
Do
.setHeader("myHeader", constant("myHeaderValue"))
See their test suite for more references:
https://github.com/apache/camel/blob/3505e718db48cc0d8be5b47f6c4030c958d93a5f/camel-core/src/test/java/org/apache/camel/component/bean/BeanWithAnnotationInheritedTest.java

Related

Optional response body for rest client using RESTEasy

I'm writing a POC for Quarkus. I'm using this quick start guide to build a REST client. The REST service I'll be integrating with is third party. Here is a simple example of my current implementation:
#Path("/v1")
#RegisterRestClient
public class EmployeeApi {
#POST
#Path("/employees")
ApiResponse createEmployee(#RequestBody Employee employee)
}
This works fine. The issue I'm having is that the third party API will, depending on success / failure, return a response body. In the scenario it does fail, it provides details in the response body (ApiResponse) on why it was unsuccessful. When it succeeds, it returns nothing. This causes Quarkus to throw the following exception:
javax.ws.rs.ProcessingException: RESTEASY003145: Unable to find a MessageBodyReader of content-type application/octet-stream and type com.test.app.ApiResponse
I've tried to wrap ApiResponse in an Optional type but does not solve the problem. I see absolutely nothing in Quarkus / RESTEasy documentation that would indicate a work-around.
I'm wondering if I should be using javax.ws.rs.core.Response instead.
The problem is JaxRS tries to fit ApiResponse to a default return type being application/octet-stream
You should make sure to specify explicitly that you're returning application/json
This is possible using #Produces(APPLICATION_JSON) on top of your service.
Here is the correct code snippet
#Path("/v1")
#RegisterRestClient
public class EmployeeApi {
#POST
#Path("/employees")
#Produces(APPLICATION_JSON)
ApiResponse createEmployee(#RequestBody Employee employee)
}

Camel-jetty component : use of available options

I am using the recipientList tag in my route as shown below. The bean findCallbackUrl will return a String which is my destination address. i.e i will be making a POST request to this endpoint . For example i may have a server listening at http://localhost:8080/acceptcallbacks .
<recipientList>
<method ref="findCallbackUrl"/>
</recipientList>
So when the String(destination) returned from the findCallbackUrl bean is jetty:http://localhost:8080/acceptcallback , the POST works fine.
However when some of the option mentioned in the Camel-jetty component are used so issues are seen. when the destination returned is jetty:http://localhost:8080/acceptcallbacks?enableJmx=false or jetty:http://localhost:8080/acceptcallbacks?disableStreamCache=false the POST works fine. However if the returned string is jetty:http://localhost:8080/acceptcallbacks?chunked=false , the call becomes a GET request .
Not sure what is happening here. If the Camel-jetty options are not to be used in the way they have been used above , for options enableJmx or disableStreamCache or some other options , the resulting destination URL should have been http://localhost:8080/acceptcallbacks?enableJmx=false which is a GET request.
Can chunked=false be used for producer as well as consumer endpoints or is it only for consumer endpoint?
Selection of http method in jetty depends on In Body of camel exchange, or on CamelHttpMethod header.
Look at source code of class org.apache.camel.component.http.helper.HttpHelper, method createMethod:
public static HttpMethods createMethod(Exchange exchange, HttpEndpoint endpoint, boolean hasPayload) throws URISyntaxException {
// compute what method to use either GET or POST
HttpMethods answer;
HttpMethods m = exchange.getIn().getHeader(Exchange.HTTP_METHOD, HttpMethods.class);
if (m != null) {
// always use what end-user provides in a header
answer = m;
} else if (hasPayload) {
// use POST if we have payload
answer = HttpMethods.POST;
} else {
// fallback to GET
answer = HttpMethods.GET;
}
return answer;
}
Now look at the call of this method in class org.apache.camel.component.jetty.JettyHttpProducer:
HttpMethods methodToUse = HttpHelper.createMethod(exchange, getEndpoint(), exchange.getIn().getBody() != null);
IMHO options don't matter. Check exchange.getIn().getBody() before request to HTTP, or you can reset header CamelHttpMethod.

CXF wsdl2java, GZip compression, and stub reutilization

I´m using CXF to consume a WebService and, as the responses are quite large, I´m requesting with a gzip "Accept-Encoding" and using GZIPInInterceptor to handle the gziped response. Also my WSDL is very large (360kb) and it takes a long time(+10 seconds) to create the stub, because it has to read and parse the WSDL, so I´m creating the stub once and reusing it.
The problem is, whenever I try to use two different methods the second request gives me an error saying it is expecting the previous request.
To illustrate my problem I created a simple example with this public WebService:
http://www.webservicex.net/BibleWebservice.asmx?WSDL
Without the GZip compression it works fine:
BibleWebserviceSoap bibleService = new BibleWebservice().getBibleWebserviceSoap();
String title = bibleService.getBookTitles();
response.getWriter().write(title);
String johnResponse = bibleService.getBibleWordsbyKeyWord("John");
response.getWriter().write(johnResponse);
I´m able to receive both responses.
Enabling Gzip compression:
BibleWebserviceSoap bibleService = new BibleWebservice().getBibleWebserviceSoap();
//GZIP compression on bibleService
Client client = ClientProxy.getClient(bibleService);
client.getInInterceptors().add(new GZIPInInterceptor());
client.getInFaultInterceptors().add(new GZIPInInterceptor());
// Creating HTTP headers
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Accept-Encoding", Arrays.asList("gzip"));
// Add HTTP headers to the web service request
client.getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
String title = bibleService.getBookTitles();
response.getWriter().write(title);
String johnResponse = bibleService.getBibleWordsbyKeyWord("John");
response.getWriter().write(johnResponse);
When I try to receive the second response I´m getting this exception:
org.apache.cxf.interceptor.Fault: Unexpected wrapper element {http://www.webserviceX.NET}GetBookTitlesResponse found. Expected {http://www.webserviceX.NET}GetBibleWordsbyKeyWordResponse.
On my real application I´m getting an exception with the request:
org.apache.cxf.binding.soap.SoapFault: OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name 'GetAvailabilityRequest' and namespace 'http://schemas.navitaire.com/WebServices/ServiceContracts/BookingService'. Found node type 'Element' with name 'ns4:PriceItineraryRequest' and namespace 'http://schemas.navitaire.com/WebServices/ServiceContracts/BookingService'
My sample project can be downloaded here:
http://www.sendspace.com/file/plt0m4
Thank you
Instead of setting the protocol headers directly like that, use CXF's GZIPOutInterceptor to handle that.
Either that or reset the PROTOCOL headers for each request. When set like that, the headers map gets updated as the request goes through the chain. In this case, the soapaction gets set. This then gets resent on the second request.

How to know if a Kohana request is an internal one?

I'm writing an API using Kohana. Each external request must be signed by the client to be accepted.
However, I also sometime need to do internal requests by building a Request object and calling execute(). In these cases, the signature is unnecessary since I know the request is safe. So I need to know that the request was internal so that I can skip the signature check.
So is there any way to find out if the request was manually created using a Request object?
Can you use the is_initial() method of the request object? Using this method, you can determine if a request is a sub request.
Kohana 3.2 API, Request - is_initial()
It sounds like you could easily solve this issue by setting some sort of static variable your app can check. If it's not FALSE, then you know it's internal.
This is how I ended up doing it: I've overridden the Request object and added a is_server_side property to it. Now, when I create the request, I just set this to true so that I know it's been created server-side:
$request = Request::factory($url);
$request->is_server_side(true);
$response = $request->execute();
Then later in the controller receiving the request:
if ($this->request->is_server_side()) {
// Skip signature check
} else {
// Do signature check
}
And here is the overridden request class in application/classes/request.php:
<?php defined('SYSPATH') or die('No direct script access.');
class Request extends Kohana_Request {
protected $is_server_side_ = false;
public function is_server_side($v = null) {
if ($v === null) return $this->is_server_side_;
$this->is_server_side_ = $v;
}
}
Looking through Request it looks like your new request would be considered an internal request but does not have any special flags it sets to tell you this. Look at 782 to 832 in Kohana_Request...nothing to help you.
With that, I'd suggest extending the Kohana_Request_Internal to add a flag that shows it as internal and pulling that in your app when you need to check if it is internal/all others.
Maybe you are looking for is_external method:
http://kohanaframework.org/3.2/guide/api/Request#is_external
Kohana 3.3 in the controller :
$this->request->is_initial()
http://kohanaframework.org/3.3/guide-api/Request#is_initial

How to get custom SOAP header from WCF service response in Silverlight?

I'm trying to get custom response message header in Silverlight application.
on server-side new MessageHeader added to response headers:
OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("headerName", "headerNS", "The header value"));
and I can see this header in Fiddler:
s:Envelope [
xmlns:s=http://schemas.xmlsoap.org/soap/envelope/
]
s:Header
headerName [ xmlns=headerNS ] The
header value
But, I can't find a way to read header value in Silverlight application service callback:
using (new OperationContextScope(proxy.InnerChannel))
{
var headers = OperationContext.Current.IncomingMessageHeaders;
// headers is null :(
}
Does anyone encountered with similar issue?
Getting SOAP headers in responses on Silverlight isn't as easy as it should be. If you use the event-based callbacks, you're out of luck - it just doesn't work. You need to use the Begin/End-style operation call, like in the example below.
void Button_Click(...)
{
MyClient client = new MyClient();
IClient proxy = (IClient)client; // need to cast to the [ServiceContract] interface
proxy.BeginOperation("hello", delegate(IAsyncResult asyncResult)
{
using (new OperationContextScope(client.InnerChannel))
{
proxy.EndOperation(asyncResult);
var headers = OperationContext.Current.IncomingMessageHeaders;
// now you can access it.
}
});
}
Notice that you cannot use the generated client (from slsvcutil / add service reference) directly, you need to cast it to the interface, since the Begin/End methods are not exposed (explicitly implemented) on the client class.
To get headers from http request try to use Client HTTP stack.
The easies way to do it is to register the prefix, for example:
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);

Resources