Multipart form-data file upload with Camel - java

I'm trying to setup an Apache Camel route in a Java application where the consumer endpoint is a restlet component that handles an HTTP file upload as a POST of multipart form data and then the producer endpoint forwards the request to a rest service that also accepts multipart form data. I'm new to Camel and can't quite figure out how to wire it up properly. Below is how my route looks so far. Do I need to do any conversion on the body or will the multipart form data be forwarded as is? Can someone provide me some guidance on the proper way to do this or point me to the correct documentation?
<route id="createentityattachment">
<from uri="restlet:/EntityAttachments?restletMethod=POST&restletBinding=#queryStringToHeadersRestletBinding"/>
<camel:recipientList>
<camel:simple>
${header.apigateway}/entityattachments/1.0.0.0/api/v1/EntityAttachments
</camel:simple>
</camel:recipientList>
</route>

I was able to get this working with the below route definition. Note the streamCache="true" attribute on the route. This setting is required for the InputStream to be properly handled in the Exchange. See the Camel docs for more information.
<route id="createentityattachment" streamCache="true">
<from uri="restlet:/EntityAttachments?restletMethod=POST&restletBinding=#queryStringToHeadersRestletBinding"/>
<removeHeaders excludePattern="X-eviCore-EntityAttachments*" pattern="^(Camel|Backbase|User-|Accept|Cache|Cookie|breadcrumbId|Host|Connection|DNT|Upgrade-Insecure-Requests|org.restlet.startTime).*$"/>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<to uri="http4://api.innovate.lan:8280/entityattachments/1.0.0.0/api/v1/EntityAttachments"/>
</route>

Related

Camel - Body becomes empty after logging

Looks like a weird issue or the docs are missing
Case 1
from("direct:ROUTE1").to("someAPI").to("direct:ROUTE2");
from("direct:ROUTE2").log("${body}"); // BODY is printing
Case 2
from("direct:ROUTE1").to("someAPI").to("direct:ROUTE2").log("${body}");
from("direct:ROUTE2").log("${body}"); // BODY is empty
Does adding log clear the exchange body??
As #Spara and #Claus suggested and to save the hassle on how to enable Stream caching.
Below is the sample code:
Using Java DSL for Single route
from("direct:ROUTER1")
.streamCaching()
.to("direct:ROUTER2");
Using Spring DSL for Single route
<route streamCache="true">
<from uri="direct:ROUTER1"/>
<to uri="direct:ROUTER2"/>
</route>
For global and per route scope using JAVA DSL
context.setStreamCache(true);
from("direct:ROUTER1")
.to("direct:ROUTER2");
For global and per route scope using Spring DSL
<route streamCache="true">
<from uri="direct:ROUTER1"/>
<to uri="direct:ROUTER2"/>
</route>
Note link: Camel Stream Caching
why stream caching

What is the Camel endpoint for marshalling using bindy dataformat?

I have a requirement to dynamically create camel-bindy endpoint for marshalling the input. Below is the sample code:
<dataFormats>
<bindy id="bookModel" type="Csv" classType="org.camelcookbook.transformation.csv.model.BookModel"/>
</dataFormats>
<route>
<from uri="direct:unmarshal"/>
<!-- <unmarshal ref="bookModel"/> -->
<to uri="dataformat:bindy:unmarshal?ref=bookModel"/>
</route>
Instead of unmarshal tag , I need to pass exchange to the equivalent end-point but getting error "Cannot find dataformat with name bindy"
There are multiple bindy data formats: csv, fixed length, key-value pair.
Their respective data format names to use with the "dataformat:" endpoint uri are:
bindy-csv,
bindy-fixed,
bindy-kvp
So in your case you should specify unmarshalling like so:
<to uri="dataformat:bindy-csv:unmarshal?ref=bookModel"/>

Using Time to live with Camel framework in Java

I am using doing integration work with Camel and using the HTTP endpoint as a proxy to route certain messages to an HTTP endpoint. I have my route configured to use my custom error handler which places failed messages in a queue that I specified (Dead Letter Channel pattern).
<route>
...
<to uri="direct:MessageTypeGuaranteed"/>
</route>
<route errorHandlerRef="MyCustomErrorHandler">
<from uri="direct:MessageTypeGuaranteed">
<to uri="http://dummyUri?throwExceptionOnFailure=true"/>
</route>
When anything fails to get delivered to my http endpoint, it's being added to my custom queue ("CustomFailedMessageQueue"), and I have a separate route that attempts to retry these messages:
<route>
<from uri="jms:queue:CustomFailedMessageQueue">
<to uri="direct:MessageTypeGuaranteed"/>
</route>
What I'd like to do is to be able to specify that I only want a message to live say for 10 seconds. So I am trying to set time to live on my http destination itself.
For example, I have a processor that does something like this:
exchange.getIn().setHeader(Exchange.HTTP_URI, "http://localhost/nodeserver?timeToLive=10000");
However, I think I have misunderstood the documentation. The timeToLive option is only valid when passing it to the jms component, correct? In other words, if I want to make use of time to live with this end point, I will need to do that handling myself in a processor, correct?
Yes TimeToLive is an option from the JMS spec that the Camel JMS components support. That options has no meaning for other components such as HTTP.
It seems like you may want to use a filter EIP and discard the message if its to old, and you can use a java method etc to implement some code that figures out if its to old or not, and return a boolean
public boolean isNotToOld(Exchange exchange) {
...
return true // to accept and process the message
}
See more about the filter eip here
http://camel.apache.org/message-filter
And you can use it in a route something a like
<from uri="direct:MessageTypeGuaranteed">
<filter>
<method ref="myBean" method="isNotToOld"/>
<to uri="http://dummyUri?throwExceptionOnFailure=true"/>
</filter>

Camel Route from Jetty to Absolute URL

I have an OSGi bundle deployed on Apache Karaf. I have a simple camel route:
<camelContext trace="true" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="jetty:http://0.0.0.0:8282/services?handlers=securityHandler&matchOnUriPrefix=true"/>
<setHeader headerName="CamelHttpQuery">
<constant>wt=xml&rows=1000000&fl=nid,title&fq=sm_vid_Third_parties_with_which_this_organisation_s_content_can_be_shared:%22Indeed%22</constant>
</setHeader>
<to uri="http://172.28.128.158:8983/solr/targetjobs.co.uk.gtimedia.test/select/?"/>
<!-- <split>
<xpath>//int[#name='nid']</xpath>
</split>-->
<convertBodyTo type="java.lang.String" />
</route>
</camelContext>
I can not get it working. When I invoke http://localhost:8282/services it should route to the uri specified in below the setHeader. Instead I am getting this exception:
java.lang.IllegalArgumentException: Invalid uri: /services.
If you are forwarding/bridging http endpoints, then enable the bridgeEndpoint
option on the endpoint:
Endpoint[http://172.28.128.158:8983/solr/targetjobs.co.uk.gtimedia.test/select/]
It says that I need to enable bridge endpoint, but this is not an endpoint, it is an absolute URL to which I am trying to point my route.
I have tried to set up Spring as shown here but this did not work either.I have also tried to change this:
<to uri="http://172.28.128.158:8983/solr/targetjobs.co.uk.gtimedia.test/select/?"/>
to this:
<to uri="jetty//http://172.28.128.158:8983/solr/targetjobs.co.uk.gtimedia.test/select/?"/>
No success as well. Maybe someone knows how to route from jetty uri to absolute url?
Have you tried bridgeEndpoint? As described below:
http://camel.apache.org/how-to-use-camel-as-a-http-proxy-between-a-client-and-server.html
Your target url will look like:
<to uri="jetty//http://172.28.128.158:8983/solr/targetjobs.co.uk.gtimedia.test/select?bridgeEndpoint=true&throwExceptionOnFailure=false"/>
Worked for me:
#Override
public void configure() throws Exception {
restConfiguration()
.host("localhost")
.component("jetty")
.port("8085");
rest("/api")
//NEW ROUTE
.get("/getResidences")
.produces("application/json")
//OLD ROUTE
.to("http://localhost:3000/api/residences?bridgeEndpoint=true&throwExceptionOnFailure=false");
}
Note the .componet("jetty") in rest configuration

How can I merge the response from an external service call with the original message in camel

From a logical perspective this is the kind of routing behaviour I wish to implement:
I want to be able to merge the response of an external service with the original request.
I have been able to implement this using multicasting, an aggregator, and a mock endpoint but I was wondering if there is a cleaner way. My current implementation looks like this:
<multicast strategyRef="serviceAggregator" stopOnException="false">
<to uri="mock:foo" />
<to uri="http://0.0.0.0:9999/service/?throwExceptionOnFailure=false" />
</multicast>
<camel:to uri="log:uk.co.company.aggregated?showAll=true" />
<to uri="http://0.0.0.0:9999/anotherService/
The part I particularly don't like is using a mock endpoint but I also don't think that this is a very readable way to express the above diagram. So I was wondering if there was a more elegant way of doing this?
I suggest to read about the EIP patterns, for example the content enricher
http://camel.apache.org/content-enricher.html
Where you can merge the reply message with the request message.
Mind the Content Enricher has 2 modes
- enrich
- pollEnrich
Make sure to notice the difference, from the docs in the link above.
<route>
<from uri="...">
<enrich uri="http://0.0.0.0:9999/service/?throwExceptionOnFailure=false" strategyRef="serviceAggregator"/>
<to uri="log:uk.co.company.aggregated?showAll=true" />
<to uri="http://0.0.0.0:9999/anotherService/>
...
</route>
And yes you diagram is showing splitter, but the sample code is using multicast EIP.
You could simply store the original message in a header or property and later do some merge in a bean. Using the header and the current body.
.setHeader("orig", body())
.to("externalService")
.bean(new MyMergeBean())

Categories