How to get the actual file object from Camel FTP route exchange - apache-camel

In my Camel router:
from(<SourceURI>)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
// I want to extract the file object from the exchange
}
.to(<targetURI>).
How can I achieve this?
I tried e.g. exchange.getIn().getHeader(Exchange.FILE_NAME, String.class) which gives me the file name.
I am searching for something Exchange.FILE which gives me the actual file object. My Ultimate goal is to extract the file in the processor as the routed exchange is an archive file.

Get the file from the body. Camel uses a 'org.apache.camel.component.file.GenericFile' to store as the file body. But you can use Camel's type converters to get the file in a type you want.
For example you can get the content in different types, such as:
String text = exchange.getIn().getBody(String.class);
byte[] bytes = exchange.getIn().getBody(byte[].class);
InputStream is = exchange.getIn().getBody(InputStream.class);

For those who have a from("file:...") the following works:
File in = exchange.getIn().getBody(File.class);

Related

Dynamic Apache Camel Output Route

Hi i want to compute a dynamic output route using apache Camel. I receive a bunch of files in a folder location, based on its contents i want to move the file to dynamic output folder. The name of the ouput folder will be constructed based on the input content of the file. How do i acheive it.
The Following piece of code read the files, processes them, but i am not sure how to set the value of ${foldername} based on the contents of the file
from("file:D:\\camel\\input\\one?recursive=true&delete=true")
.process(new LogProcessor())
.to("file:D:\\camel\\output\\${foldername}")
Please assist
You could create a custom processor to construct the foldername and insert into a header.
public class DirectoryNameProcessor implements Processor {
#Override
public void process(Exchange exchange) {
Message in = exchange.getIn();
// Get the contents of the processed file
String body = in.getBody(String.class);
//Get the original file name
String fileName = in.getHeader("CamelFileName", String.class);
// Perform your logic
in.setHeader("foldername");
}
}
Then in your route you could access the newly created foldername-header:
.to("file:D:\\camel\\output\\${header.foldername}");
The short answer is, you can use the dynamic to endpoint toD.
http://camel.apache.org/message-endpoint.html#MessageEndpoint-DynamicTo
It would look like:
from("file:D:\\camel\\input\\one?recursive=true&delete=true")
.process(new LogProcessor())
.toD("file:D:\\camel\\output\\${foldername}")

Camel-Azure BlobServiceProducer IllegalArgumentException: Unsupported blob type:org.apache.camel.component.file.GenericFile

I have written a camel route which polls a folder and sends it to Azure Blob Container
I followed the example mentioned in the Azure document page
https://github.com/apache/camel/blob/master/components/camel-azure/src/main/docs/azure-blob-component.adoc
I am reversing the route. Instead of a consumer, I am using the Azure Blob Producer.
This is my route. I have used Java DSL.
from("file://C:/camel/source1").to("azure-blob://datastorage/container1/BLOB1?credentials=#credentials&operation=updateBlockBlob")
When I placed a file, I got the following error.
**java.lang.IllegalArgumentException: Unsupported blob type:org.apache.camel.component.file.GenericFile
at org.apache.camel.component.azure.blob.BlobServiceProducer.getInputStreamFromExchange(BlobServiceProducer.java:474) ~[camel-azure-2.19.2.jar:2.19.2]
at org.apache.camel.component.azure.blob.BlobServiceProducer.updateBlockBlob(BlobServiceProducer.java:143) ~[camel-azure-2.19.2.jar:2.19.2]
at org.apache.camel.component.azure.blob.BlobServiceProducer.process(BlobServiceProducer.java:79) ~[camel-azure-2.19.2.jar:2.19.2]**
I was able to fix this. I rewrote my route as.
from("file://C:/camel/source1")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Object file = exchange.getIn().getMandatoryBody();
exchange.getOut().setBody(
GenericFileConverter.genericFileToInputStream(
(GenericFile<?>) file, exchange));
}
})
.to("azure-blob://datastorage/container1/BLOB1?credentials=#credentials&operation=updateBlockBlob")
.to("mock:Result");
My Question is, do I need to really write the processor? Shouldn't the camel component be receiving a stream or a File Object?
Yeah this is a little bug. I have logged a ticket: https://issues.apache.org/jira/browse/CAMEL-11844
You can do the workaround you did, or you can add a .convertBodyTo and convert to a FileInputStream, String etc.
from("file://C:/camel/source1")
.convertBodyTo(String.class)
...

Using apache camel csv processor with pollEnrich pattern?

Apache Camel 2.12.1
Is it possible to use the Camel CSV component with a pollEnrich? Every example I see is like:
from("file:somefile.csv").marshal...
Whereas I'm using the pollEnrich, like:
pollEnrich("file:somefile.csv", new CSVAggregator())
So within CSVAggregator I have no csv...I just have a file, which I have to do csv processing myself. So is there a way of hooking up the marshalling to the enrich bit somehow...?
EDIT
To make this more general... eg:
from("direct:start")
.to("http:www.blah")
.enrich("file:someFile.csv", new CSVAggregationStrategy) <--how can I call marshal() on this?
...
public class CSVAggregator implements AggregationStrategy {
#Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
/* Here I have:
oldExchange = results of http blah endpoint
newExchange = the someFile.csv GenericFile object */
}
Is there any way I can avoid this and use marshal().csv sort of call on the route itself?
Thanks,
Mr Tea
You can use any endpoint in enrich. That includes direct endpoints pointing to other routes. Your example...
Replace this:
from("direct:start")
.to("http:www.blah")
.enrich("file:someFile.csv", new CSVAggregationStrategy)
With this:
from("direct:start")
.to("http:www.blah")
.enrich("direct:readSomeFile", new CSVAggregationStrategy);
from("direct:readSomeFile")
.to("file:someFile.csv")
.unmarshal(myDataFormat);
I ran into the same issue and managed to solve it with the following code (note, I'm using the scala dsl). My use case was slightly different, I wanted to load a CSV file and enrich it with data from an additional static CSV file.
from("direct:start") pollEnrich("file:c:/data/inbox?fileName=vipleaderboard.inclusions.csv&noop=true") unmarshal(csv)
from("file:c:/data/inbox?fileName=vipleaderboard.${date:now:yyyyMMdd}.csv") unmarshal(csv) enrich("direct:start", (current:Exchange, myStatic:Exchange) => {
// both exchange in bodies will contain lists instead of the file handles
})
Here the second route is the one which looks for a file in a specific directory. It unmarshals the CSV data from any matching file it finds and enriches it with the direct route defined in the preceding line. That route is pollEnriching with my static file and as I don't define an aggregation strategy it just replaces the contents of the body with the static file data. I can then unmarshal that from CSV and return the data.
The aggregation function in the second route then has access to both files' CSV data as List<List<String>> instead of just a file.

Put modified xml back into the message?

I'm using CXF to send messages with SOAP over JMS.
I'm trying to write a CXF Interceptor in the POST_MARSHALL phase.
I want to change some attributes when the xml is generated.
I know i can get the content from the message via
message.getContent(java.io.Writer.class).
This happens to be in the form of JMSConduit$1. Which - I think - is a StringWriter (if I debug my code I can see a buf field).
I can get the xml in String format and make my changes, but the problems is putting it back in the message.
I can not change the JMSConduit$1 to something else, otherwise CXF won't send it to the JMS Endpoint. (it must be a JMSConduit).
I can't find a way to put the modified xml back in a JMSConduit, which i can get through
message.getExchange().getConduit();
So, how can I put my modified xml back into the message/JMSConduit?
Finally found an answer. I used a FilterWriter.
public void handleMessage(Message message) throws Fault {
final Writer writer = message.getContent(Writer.class);
message.setContent(Writer.class, new OutWriter(message, writer));
}
class OutWriter extends FilterWriter {
#Override
public void close() throws IOException {
// Modify String (in xml form).
message.setContent(Writer.class, out);
}
}

Apache Camel: How do I do a simple modification on files in one directory, then store the output in another directory?

Seems simple, but I can't get it working. What I want to do is take any files that are placed in the "from" path, modify their contents, and place the modified file in the "to" path (with a .txt extension). Here's what I have:
this.context.addRoutes(new RouteBuilder() {
public void configure() {
from( "file:" + getFromPath() + getOptions() )
.to( "file:" + getToPath() + "?fileName=${file:name.noext}.txt")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String name = (String) exchange.getIn().getHeader("CamelFileName");
File body = exchange.getIn().getBody(File.class);
String parsedText = modifyFile(body);
exchange.getOut().setBody(parsedText);
}
})
;}
});
The output file is getting created, but the contents are exactly the same as the input file. I.e, the file is not getting modified. I confirmed that the "modifyFile" method is returning what I want it to, but can't get it to write those contents to the output ("to") path.
Thanks for the help!
If you want to modify the file content, you need to put the processor between the "from" and "to" endpoints.

Resources