I created a simple route to get contact from a remote host. But, there seems to be a lot of confusion regarding the bridgeEndpoint option.
Initially, I added the route using the Java DSL as follows:
from("direct:getContact")
.marshal().json(JsonLibrary.Jackson)
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.recipientList(simple("http://<remoteHost>:8080/api/contact" +
"/${header.contactId}"))
.unmarshal().json(JsonLibrary.Jackson);
This route is just a proxy for the get contact API of the remote host.
I got the following error:
Invalid uri: /ib/contact/51702/contact/51702. If you are forwarding/bridging http endpoints, then enable the bridgeEndpoint option on the endpoint: Endpoint[http://<remoteHost>:8080/api/contact/51702]
/ib/* you see is the base url for the tomcat servlet. As suggested in the error, I added the bridgeEndpoint=true to the endpoint as shown below:
from("direct:getContact")
.marshal().json(JsonLibrary.Jackson)
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.recipientList(simple("http://<remoteHost>:8080/api/contact" +
"/${header.contactId}?bridgeEndpoint=true"))
.unmarshal().json(JsonLibrary.Jackson);
Then, I get a different error:
org.apache.camel.component.http.HttpOperationFailedException:
HTTP operation failed invoking
http://<remoteHost>:8080/api/contact/51702/contact/51702 with statusCode: 404
at org.apache.camel.component.http.HttpProducer.populateHttpOperationFailedException(HttpProducer.java:233)
at org.apache.camel.component.http.HttpProducer.process(HttpProducer.java:158)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:448)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:190)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:652)
at org.apache.camel.processor.MulticastProcessor.doProcessSequential(MulticastProcessor.java:580)
at org.apache.camel.processor.MulticastProcessor.process(MulticastProcessor.java:227)
at org.apache.camel.processor.RecipientList.sendToRecipientList(RecipientList.java:167)
at org.apache.camel.processor.RecipientList.process(RecipientList.java:120)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:118)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
It is still appending "contact/51702" to the url of the remote host, which is giving 404.
What am I missing here?
From the FAQ
In camel there are a number of components that use the http protocol headers to do their business.
I believe your producer does it as well. So the following could solve your problem.
from("direct:getContact")
.marshal().json(JsonLibrary.Jackson)
.setHeader("Content-Type", constant("application/json"))
.setHeader("Accept", constant("application/json"))
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.removeHeader(Exchange.HTTP_PATH)
.recipientList(simple("http://<remoteHost>:8080/api/contact" +
"/${header.contactId}?bridgeEndpoint=true"))
.unmarshal().json(JsonLibrary.Jackson);
You could also remove contact/${header.contactId} from the endpoint. As it looks redundant. But this depends on what you want to achieve.
Answer by #SubOptimal is almost correct (we can say his answer is suboptimal, lol), except it should be HTTP_URI header. From the doc:
If the bridgeEndpoint option is true, HttpProducer will ignore the Exchange.HTTP_URI header, and use the endpoint’s URI for request.
Therefore there are 2 solutions:
add .removeHeader(Exchange.HTTP_URI) to the route definition
add ?bridgeEndpoint=true query parameter
However this might not solve the problem if you have other headers that get in the way. Probably that was your case, that's why removing all Camel http headers helped.
Please be aware though that removing all headers might break your logic: for example HTTP_METHOD header is used to define the http method of the outgoing request. And it's up to you if you want to proxy the method too or not. You can find more in the same doc via the link above.
Related
My goal is to modify the http response status code and the content that comes with it (not using spring by the way). The current response I'm getting is an HTTP 501 and I would like to modify that response to return an HTTP 200 instead with a custom message.
I've tried using javax.servlet.Filter and found out that that won't work (as far as I know) since I can't modify the response after the filterChain.doFilter() call. I tried redirecting a response using httpResponse.sendError() but didn't work since I'm getting an IllegalStateException telling me about the reponse was already committed or something like that.
I've also tried adding an <error-page>...</error-page> block in my web.xml using a web servlet as the location but apparently that does not work for unsupported methods and invalid headers (tried using a random string as request method; also tried Transfer-Encoding: random as an invalid header); works for invalid uri paths though.
Is there a way to create a general "filter" mechanism where I can modify the response code and message when there's a HTTP error code response?
I'm trying to develop a client application for a WebService using Apache's CXF library. In this specific server implementation, when there's some data missing in the request (e.g. Some person's ID number), it returns HTTP code 403 (Forbidden) but with a response body containing the application specific error details as a Soap Fault.
As an example, here's a response I gathered using SoapUI:
As you can see in the highlighted text, there's a response body in this request.
Now I need to retrieve the response body from within my application. I tried using interceptors in different phases such as SEND_ENDING and POST_PROTOCOL, but can't seem to find it within the Message parameter given to the handleMessage() method.
What am I missing?
Here's the exception and stack trace I'm getting:
org.apache.cxf.interceptor.Fault: Could not send Message.
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:67)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:531)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:440)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:355)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:313)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:140)
at com.sun.proxy.$Proxy36.arquivo(Unknown Source)
at br.com.dgsistemas.TesteWS.main(TesteWS.java:133)
Caused by: org.apache.cxf.transport.http.HTTPException: HTTP response '403: Forbidden' when communicating with https://www.wsrestrito.caixa.gov.br/siies/WsSolicitacao
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.doProcessResponseCode(HTTPConduit.java:1620)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1627)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1572)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1373)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:673)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63)
... 9 more
Thank you!
You have two independent problem.
First you have to remove the chunked message.
http://cxf.apache.org/docs/client-http-transport-including-ssl-support.html#ClientHTTPTransport(includingSSLsupport)-ANoteAboutChunking
Before you call the endpoint, you have to disable the chunked communication:
HTTPConduit conduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy policy = new HTTPClientPolicy();
// Chunking is by default enabled in CXF webservices so we have to disable it.
policy.setAllowChunking(false);
conduit.setClient(policy); // update HTTP client's conduit
Second I think you have to remove a BOM. You can see what is it in the following wikipedia note:
https://en.wikipedia.org/wiki/Byte_order_mark
If you want to remove BOM check this:
Byte order mark screws up file reading in Java
NOTE1: The chunked messages depends on server settings, the server may
ignore your request setting.
NOTE2: You can handle BOM and chunked message both if you write a
stream interceptor. The chunked messages don't have a Content-Length header, and while actual length lesser than the expected, you have to wait more message from server.
You should be able to extend AbstractSoapInterceptor, register this in the Phase.MARSHAL phase, and extract the message in your handleMessage override.
Use SoapMessage.getExchange().getInMessage() or .getInFaultMessage() to extract the message from the SOAP response.
Did you try using the LoggingInInterceptor for that (See description), or extend it and overwrite the handleMessage method. It can be used to monitor all SOAP IN messages
Example for usage can be found here
I have two apps. First of them executes some business logics and than calls other app with POST or DELETE
from("direct:firstapp").routeId("rst_firstapp").streamCaching()
//data preparation was here
.setHeader(Exchange.CONTENT_TYPE, simple(MediaType.APPLICATION_JSON_VALUE))
.convertBodyTo(MyRequest.class)
.choice().id("rst_req_lockCardChoice")
.when().simple("${mycondition} == '1'")
.setHeader(Exchange.HTTP_METHOD, simple("POST"))
.log(LoggingLevel.INFO, "Call post")
.when().simple("${mycondition} == '0'")
.setHeader(Exchange.HTTP_METHOD, simple("DELETE"))
.log(LoggingLevel.INFO, "Call delete")
.end()
.marshal(new JsonDataFormat(JsonLibrary.Jackson))
.log(LoggingLevel.INFO, " Call to service ${body}")
.to('myadress/mypath').id("rt_call_service")
.log(LoggingLevel.INFO, " Response from service ${body}");
Logs for line: .log(LoggingLevel.INFO, " Call to service ${body}")
print that body exists in both cases.
The second app receives this requests and does some other business logics.
rest().post("/mypath")
.consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.type(MyRequest.class)
.responseMessage().code("200").message("Success").endResponseMessage()
.route().routeId("rst_postrecieve")
.log(LoggingLevel.INFO, "Recieved request ${body}")
.id("rst_rst_post_recieved")
.to("direct:drt_rst_postbranch")
.endRest();
rest().delete("/mypath")
.consumes(MediaType.APPLICATION_JSON_VALUE)
.produces(MediaType.APPLICATION_JSON_VALUE)
.type(MyRequest.class)
.responseMessage().code("200").message("Success").endResponseMessage()
.route().routeId("rst_deleterecive")
.log(LoggingLevel.INFO, "Received request ${body}")
.id("rst_rst_delete_recieved")
.to("direct:drt_rst_deletebrach")
.endRest();
POST request works fine.
First app sends body. Second recieves it.
DELETE doesn't.
First app cuts the body out. Line .log(LoggingLevel.INFO, " Call to service ${body}") prints the body. But the reciever gets a DELETE request with empty body. I have sniffed the Http-request with Wireshark: the body is empty.
However, I'm perfectly able to send a DELETE request with body using any other tool like Swagger-ui, Postman, Soap-UI etc. The second app will recieve body and proccess it correctly.
Why does Camel forbid to send a DELETE body, while it allows to recieve it? I expected the behaviour to be the same.
What is the workaround? I can't change request type, it's a customer requirement.
Camel version: 2.21.0.000033-fuse-000001-redhat-1
P.S. I know that Http standards state that no body is expected for DELETE, however all the tools & frameworks I faced allowed to send and receive it. Including Spring, for example.
Most probably Delete method will ignore body while processing. This is no problem with your code, it was because of HTTP implementation.
If a DELETE request includes an entity body, the body is ignored [...]
If you still need to send some data from one app to another to Delete method. I suggest you can try with path param. So than you can get as parameter in second app and do you process.
I'm trying to read a JSON response from a RESTful webserver running on an IoT module (Advantech WISE-4012). According to the documentation, any GET request should be made in this form
GET /ai_value/slot_0/ch_0
Any Java implementation of GET requests (Java libraries, Apache etc.), anyway, append to the end of the request the protocol signature HTTP/1.1. E.g:
GET http://192.168.0.14/ai_value/slot_0/ch_0 HTTP/1.1
Because of this (probably) i'm getting Error 400 (Bad request) on every client i tried so far. The only working method i've discovered was sending a simple request through the address bar on Google Chrome browser (sometimes i get a response, sometimes a get a bad request error either). How can i write a java implementation of a GET request plain and simple as described by the documentation? How can i test a custom GET request without HTTP/1.1 at the end? Every chrome extension i tried (Advanced REST Client, Postman) add the protocol version at the end, so i haven't had the chance to verify if that's why i'm getting a bad request error.
EDIT:
This is the response header from Advanced REST client
Connection: close
Content-Type: application/json
Server: WISE-4000/8.1.0020
While the source message is the following one:
GET /ai_value/slot_0/ch_0 HTTP/1.1
HOST: 192.168.0.14
The only mismatch between the documentation is the HTTP/1.1 signature as mentioned before. Adding the "accept: application/json" makes no difference either
After a bit of digging into the documentation, it looks like the default timeout (i.e. 720 seconds) is the one causing an issue. There doesn't seem to be any way to work it around (ideally, the system should reset the time after a successful request and we should only get 400 - or 403 ideally after 720 seconds of inactivity).
A couple of points I would like to recommend to the API developers for WISE-4012 (if they are in touch with you):
Add brief documentation for authentication and timeout (probably, more response codes and error messages with each error response)
Enable OAuth for API Access
As far as current implentation is conerned, I guess you need to do a basic auth and pass username/password with every request, Or add Authentication header with every API request to get successful response without any 400s.
Check if this helps.
HttpClient httpClient = HttpClients.createDefault();
URI reqUri = new URI(<uri>);
RequestBuilder requestBuilder = RequestBuilder.create("GET");
requestBuilder.setUri(reqUri);
requestBuilder.setHeader(<headerKey>, <headerValue>);
requestBuilder.setEntity(<entity_data>);
HttpUriRequest httpRequest = requestBuilder.build();
httpResponse = httpClient.execute(httpRequest);
I have a route builder that looks as follows:
.post("/myEndpoint")
.type(MyObject.class)
.to("bean:myListener?method=create")
I would like this to return a 201 Created HTTP Response Code, at present its returns a 200 OK.
Is there a way to do this in the RouteBuilder without having to forward any results onto a separate service class and then manually set the code on the Exchange?
We managed to get it to work by doing the following -
.post("/myEndpoint")
.type(MyObject.class)
.route()
.setHeader(Exchange.HTTP_RESPONSE_CODE,simple(HTTP_CREATED))
.to("bean:myListener?method=create")
.endRest()
See the header section here http://camel.apache.org/constant.html for setting headers.. You should be able to set the http response code and body directly.