Apache Camel: Query Params vs Header Params - java

I'm currently trying out Apache camel (as routing engine). I understand that Camel supports multiple DSLs and that it could be configured using Java (Java DSL) or Spring (Spring DSL).
Question:
I have the following Spring DSL configuration. The idea is that if the incoming request has header-param called "name", it would hit when clause or else to would route the request to google:
<camel:route>
<camel:from uri="servlet:///test" />
<camel:choice>
<camel:when>
<camel:header>name</camel:header>
<camel:transform>
<camel:simple>Hello ${header.name} how are you?</camel:simple>
</camel:transform>
</camel:when>
<camel:otherwise>
<camel:to uri="http://www.google.com?bridgeEndpoint=true" />
</camel:otherwise>
</camel:choice>
</camel:route>
I expected the above config to work only for Header Param. However, I noticed that this configuration is working even for Query params as shown in the following request:
http://localhost:8080/<war-context>/test?name=test
Is there a way to make sure that it is made to work only for header params ?

It's well documented here that query params are copied to exchange headers in the servlet component.

Related

Spring-integration: how correctly to call SOAP web-service?

SOAP WS with trivial characteristics:
- WSDL address:
http://<ip>:<port>/service/name?wsdl
- target method name:
getData(...)
Part of Spring-integration config ('spring-context.xml') for calling WS:
...
<int:chain input-channel="data" output-channel="stdout">
<!-- transform message to request -->
<int:transformer
ref="soapRequestTransformer"
method="createReq">
</int:transformer>
<!-- define the WS method to be called (getData) -->
<int:header-enricher>
<int:header
name="SoapAction"
value="http://<ip>:<port>/service/name/getData"/>
</int:header-enricher>
<!-- call WS -->
<int-ws:outbound-gateway uri="http://<ip>:<port>/service/name"/>
</int:chain>
...
After transformer (successful) i have request object:
request.getHeaders(): {sequenceNumber=1, correlationId=2bfb560c-
96ba-9c35-96dc-ba16104604b9, id=32f43a24-dee2-461c-a9b9-92c8ee37aaec,
sequenceSize=12, timestamp=1532509142590}
request.getPayload(): org.types.GetDataRequest#77409e4b
How to properly setup the spring-integration to correctly call the WS?
Problem is that WS is not called.
How does this problem look? We can't say anything without at least some stack trace.
Is the name of the WS method used correctly?
The SoapAction value is vendor-specific. You have to consult the WS provided to determine what and how should be presented in that header.
How are the parameters passed?
Not sure about nay parameter. Yes, you can send some query params as well, but doesn't look like that is your questions. Please, be more specific.
By the way, if you send some POJO (e.g. your org.types.GetDataRequest), you need to use a marshaling gateway instead of simple one:
<int-ws:outbound-gateway marshaller="someMarshaller" uri="http://<ip>:<port>/service/name"/>
Not sure, of course, if you have a good XSD-genarated model, based on the provided info in that WSDL...

Http header Content-Length in camel cxf client request

We have a simple camel route "from->to":
<from uri="cxf:bean:testServiceProvider?loggingFeatureEnabled=true" />
<to uri="cxf:bean:testServiceClient?loggingFeatureEnabled=true" />
This route acts like a router or proxy for a third party's web service:
Clients use it as endpoint.
Adds WSS headers to Soap message.
Route requests to real endpoint.
Service and client in this proxy are created with cxf beans.
The endpoint's web service seems to require Content-Length HTTP header, but cxf requests to endpoint does not contain this header by default. All the requests done by this proxy receive the same response:
HTTP response '411: Length required' when communicating with https://host:port/testService
We tried to add this header with an OutInterceptor, adding it to PROTOCOL_HEADERS:
Map<String, List> headers = (Map<String, List>) message.get(Message.PROTOCOL_HEADERS);
headers.put("Content-Length", Collections.singletonList(String.valueOf(messageLength)));
Two questions:
How to know the value of messageLength?
Is there an easier way to do this?
Thanks!
You can try with http:conduit, disabling AllowChunking. This will force cxf to include Content-Length header in the request. By default cxf behaviour is to allow chunking, so it can be generating the problem you're facing, even specifying the Content-length header.
<http:conduit name="{http://namespace}WebService.http-conduit">
<http:client AllowChunking="false" CacheControl="No-Cache"
ContentType="text/xml;charset=UTF-8" ConnectionTimeout="900000"
ReceiveTimeout="900000" Connection="Keep-Alive" />
</http:conduit>
Looking at the CXF documentation you may be able to use the relayHeaders functionality to propogate headers from the "from" endpoint to the "to" endpoint.
CXF Bean Docs
Alternatively you could copy the value of the content-length from the inbound message as suggested here...
"If you want to keep those headers in the old version of camel, you need
to put the headers into a map and put this map into message header with
the key "org.apache.cxf.message.PROTOCOL_HEADERS"."
Copy headers

Apache Camel avoid duplicating options across similar XML routes

I have some Apache Camel routes with many options, like this one:
<from uri="sftp://user#host:22/path?
password=vvvvv;delay=3000&streamDownload=true&
delete=true&idempotent=true&idempotentRepository=#jpaStore&
inProgressRepository=#jpaStore"/>
This isn't so bad, but I have six other routes with the same options but different paths. I'd like to put all the options in a constant to avoid duplication:
<from uri="sftp://user#host:22/path?OPTIONS"/>
I might be able to use Camel EL to accomplish this, but none of the examples show it, and my attempts to guess the syntax aren't working.
I create a Spring bean like this:
<bean id="myoptions" class="java.lang.String">
<constructor-arg value="allmyoptions"/>
</bean>
And try to refer to it like this:
<from uri="sftp://user#host:22/path?${myoptions}"/>
But I get an error:
There are 1 parameters that couldn't be set on the endpoint. Check the uri if the parameters are spelt correctly and that they are properties of the endpoint. Unknown parameters=[{${myoptions}=}]
This question, Simple Expression in apache-camel uri, is attempting something similar, but they use Java DSL and my routes are configured in XML.
Does anyone know a good way to avoid duplicating all this options across routes?
From this page, How do I use Spring Property Placeholder with Camel XML, I read that "We do NOT yet support the ${something} notation inside arbitrary Camel XML." This said, they suggest various workarounds on this page, Properties.
What worked for me was to configure a BridgePropertyPlaceholderConfigurer as follows:
<bean id="bridgePropertyPlaceholder" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="location" value="classpath:myproperties.properties"/>
</bean>
In the properties file I have:
OPTIONS=password=vvvvv;delay=3000&streamDownload=true&delete=true&idepotent=true&idempotentRepository=#jpaStore&inProgressRepository=#jpaStore
This allows me to use both the Spring property placeholder notation ${} and the Camel placeholder notation with {{ }}:
<from uri="sftp://user#host:22/path?{{OPTIONS}}"/>
One gotcha is that I needed to get rid of my encoded ampersands in the properties file, replacing & with just &.
See also:
Answer: How to use a dynamic URI in From()
Documentation: How do I use Spring Property Placeholder with Camel XML
Documentation: Properties

Spring Integration HTTP Inbound Gateway Request Overlap

I have an HTTP Inbound Gateway in my Integration Application, which I will call during some save operation. It's like this. If I have one product, I will call the API once, and if I have more than once, then I will call multiple times. The problem is, for single invoke, SI works just fine. But for multiple calls, request and response get messed up. I thought Spring Integration Channels are just like MQ's, but it is not?
Let me explain this. Let's say I have 2 products. First, I invoke SI for Product A and then for B. Response of A got mapped to request B! It happens all the time. I don't want to use some dirty hacks like wait for the first response to come and invoke again. This means the system has to wait for a long time. I guess we can do it in Spring Integration using task executor, but with all the basic samples out there, I can't find the right one. So please help me find out how can I fix this issue!
My Configuration is :
<int:channel id="n2iMotorCNInvokeRequest" />
<int:channel id="n2iMotorCNInvokeResponse" />
<int:channel id="n2iInvoketransformerOut" />
<int:channel id="n2iInvokeobjTransformerOut" />
<int:channel id="n2iInvokegatewayOut" />
<int-http:inbound-gateway id="i2nInvokeFromPOS"
supported-methods="GET"
request-channel="i2nInvokeRequest"
reply-channel="i2nInvokeResponse"
path="/postProduct/{Id}"
mapped-response-headers="Return-Status, Return-Status-Msg, HTTP_RESPONSE_HEADERS"
reply-timeout="50000">
<int-http:header name="Id" expression="#pathVariables.Id"/>
</int-http:inbound-gateway>
<int:service-activator id="InvokeActivator"
input-channel="i2nInvokeRequest"
output-channel="i2nInvokeResponse"
ref="apiService"
method="getProductId"
requires-reply="true"
send-timeout="60000"/>
<int:transformer input-channel="i2nInvokeResponse"
ref="apiTransformer"
method="retrieveProductJson"
output-channel="n2iInvokeRequest"/>
<int-http:outbound-gateway request-channel="n2iInvokeRequest" reply-channel="n2iInvoketransformerOut"
url="http://10.xx.xx.xx/api/index.php" http-method="POST"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<int:service-activator
input-channel="n2iInvoketransformerOut"
output-channel="n2iInvokeobjTransformerOut"
ref="apiService"
method="productResponse"
requires-reply="true"
send-timeout="60000"/>
The i2nInvokeFromPOS gateway is what we call from Web Application which is where all the products will be created. This Integration API will fetch that data, and post it to the backend system so that it will get updated to the other POS locations too!
Steps :
I will send the productId to i2nInvokeFromPOS.
apiTransformer -> retrieveProductJson() method will fetch the product details from DB based on the ID
Send the Request JSON to Backend system using http:outbound-gateway
Get the response from Backend and update the product status as uploaded in DB. Happens in apiService -> productResponse()
Once the response for A is received, all I'm getting is HTTP 500 Error for the Request B! But the Backend API is just fine.
The framework is completely thread-safe - if you are seeing cross-talk between different requests/responses then one (or more) of your components that the framework is invoking is not thread-safe.
You can't keep state in fields in, for example, code invoked from a service activator.

REST EndPoint for Apache Camel

I'm trying to create en REST endpoint with Apache Camel. I already have a REST service that return me JSON content and I want this endpoint to get it. My problem is that I don't know what's happening when my Camel route is built.. For the moment, it doesn't do anything. Here is my code :
restConfiguration().component("servlet")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true").host("localhost")
.port(9080);
rest("/ContextServices/rest/contextServices/document")
.consumes("application/json").produces("application/json")
.get("/testContext/557064c8f7f71f29cea0e657").outTypeList(String.class)
.to("bean:processor?method=affiche")
.to(dest.getRouteTo());
I'm running my REST service on a local Tomcat on port 9080, my full URL is
/ContextServices/rest/contextServices/document/{collection}/{id}.
I've tried to read the documentation but there is two syntax and both don't work:
from("rest:get:hello:/french/{me}").transform().simple("Bonjour ${header.me}");
or
rest("/say")
.get("/hello").to("direct:hello")
.get("/bye").consumes("application/json").to("direct:bye")
.post("/bye").to("mock:update");
The first is Java DSL, the second is REST DSL, what's the difference ?
Thanks a lot !
First of all, REST component itself is not a REST implementation.
It just declares language to describe REST endpoints.
You should use actual implementation of REST, something like Restlet (see the full list here)
I can be wrong, but AFAIR, REST endpoint is only for the case when you want to listen for REST requests from another application.
What you need is to make request to REST endpoint and process it.
The question is: when do you want to trigger request?
Is it some event, or may be you want to check external REST service periodically?
For the latter case I use the following pattern:
<route>
<from uri="timer:polling-rest?period=60000"/>
<setHeader headerName="CamelHttpMethod">
<constant>GET</constant>
</setHeader>
<recipientList>
<simple>http://${properties:service.url}/api/outbound-messages/all</simple>
</recipientList>
<unmarshal ref="message-format"/>
<!-- do something with the message, transform it, log,
send to another services, etc -->
<setHeader headerName="CamelHttpMethod">
<constant>DELETE</constant>
</setHeader>
<recipientList>
<simple>http://${properties:service.url}/api/outbound-messages/by-id/${header.id}</simple>
</recipientList>
</route>
Sorry for the example with http component instead of REST.
I simply copy-pasted it from my working project, which uses pure http.
I suppose, rewriting this via something like Restlet or CXF component.

Categories

Resources