My use case is based on the rest controller input I need to fetch or move files from different source system to destination system.
Route :-
#Component
public class MoveFile extends RouteBuilder {
#override
public void configure() throws Exception {
from("file:tmp/${header.inPath}")
.to("file:/tmp${header.outPath}?fileName=${header.fileName}")
.setBody().constant("File - ${header.inPath}/${header.fileName} Moved Succesfully")
}
}
My rest controller will pass the jobName along the getMapping to invoke this specific route inPath , outPath and File Names
#Resource(name=RouteProperties)
private Prosperties props;
#GetMapping("/runJob/{jobToInvoke}")
public String runJob (#PathVariable final String jobToInvoke){
String inPath=props.getProperty("inPath"+jobToInvoke)
String outPath=props.getProperty("outPath"+jobToInvoke)
String fileName=props.getProperty("fileName"+jobToInvoke)
String jobStatus = ProducerTemplate.withHeader("inPath",inPath)
.
.
.to(??)
.request(String.class)
}
I need help to use Producer Template to pass the properties using to ?
I tried some search on the google, but there is an example available in youtube (link) , But in that Video it is calling uri , (Direct:sendMessage) and from in the route also has that.
How to handle in this scenario ?
Thanks in Advance
A route beginning with a direct: endpoint can be invoked programmatically from Java code. In the route, the pollEnrich component invokes a consumer endpoint to read a file and replace the exchange message body with the file contents.
from("direct:start")
.pollEnrich().simple("file:/tmp?fileName=${header.inPath}")
.toD("file:/tmp?fileName=${header.outPath}")
.setBody().simple("File - ${header.inPath} Moved Successfully");
To invoke the route from Java code:
String jobStatus = producerTemplate.withHeader("inPath", inPath)
.withHeader("outPath", outPath)
.to("direct:start")
.request(String.class);
I don't know if these dynamic file URIs in from work, but at least the Camel File documentation states
Also, the starting directory must not contain dynamic expressions with
${ } placeholders. Again use the fileName option to specify the
dynamic part of the filename.
So the docs are suggesting to change
from("file:tmp/${header.inPath}")
into
from("file:tmp?fileName=${header.inPath}")
The fileName option can be a relative path (not just a filename).
If that change works for you, your question becomes obsolete because the route URI is no more dynamic.
.withHeader("inPath",inPath)
.to("file:tmp")
Related
I'm trying to process a file using Apache Camel, and after processing move it to a specific folder, while keeping the filename and directory structure.
What I have in a application.yml file:
camel-from: "file:/C:/in/received?move=../in/processed/${file:name}&recursive=true&readLock=changed&readLockMarkerFile=false&delay=1000&maxDepth=2&minDepth=2"
Using Java the Route is as follows:
#Component
#RequiredArgsConstructor
public class TestRoute extends RouteBuilder {
#Value("${camel-from}")
private String fromUri;
#Override
public final void configure() {
from(fromUri)
// rest of code
}
}
If I use the string directly in the route from, it works just fine. However, reading it from the application.yml file, no matter which characters I try to escape, I can't get it to read the uri properly. (I always end up with either an error, or creating folders such as processed/name instead of ${file:name} getting interpreted).
Any ideas?
Thanks
Property replacement needs to be escaped in SPEL language, so Apache Camel gets the value in raw form. You can escape it with #{'$'}. There is open issue spring-framework#9628 about making this escape sequence shorter / more intuitive.
camel-from: "file:/C:/in/received?move=../in/processed/#{'$'}{file:name}"
I am new to apache camel and writing the camel test to validate the route. My use case is,
From camel test invoke route which copies files from folderA to folderB.
Camel route is invoked using direct component from test route.
This doesn't work for me,
from("direct:waitingForVideoFiles").routeId("waitingForVideoFiles")
.to("file:E://folderA?noop=true")
.to("file:D://folderB?FileName=ready-${file:name}")
My camel test direct component looks like this,
template.sendBody("direct:waitingForVideoFiles",null);
Here i can see CamelFileName is set to null.
This code works for me.
from("file:E://folderA?noop=true&include=.*.mov|.*.ts").routeId("VideoFilesAvailable")
.setHeader("statusVideoFile", simple("failed"))
.log(LoggingLevel.INFO, "header is ${headers}")
.to("file:D://folderB")
files are getting copied without direct component.
you are passing body as null, try with some content
If you haven't mentioned the fileName, camel will create its own file with the content you are passing.
I tried your example and that works for me
#Test
public void testSendMatchingMessage() throws Exception {
template.sendBody("direct:waitingForVideoFiles", "some content");
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:waitingForVideoFiles")
.routeId("waitingForVideoFiles")
.to("file:///Users/sucheth.shivakumar/Desktop/folderA");
}
};
}
Just in case if you want to send your own file name, You can use something like this
template.sendBodyAndHeader("direct:waitingForVideoFiles", "some body content", Exchange.FILE_NAME, "such.txt");
You need a PollEnrich:
from("direct:waitingForVideoFiles").routeId("waitingForVideoFiles")
.loopDoWhile(body().isNotNull())
.pollEnrich("file:E://folderA?noop=true")
.toD("file:D://folderB?FileName=ready-${file:name}")
.end();
Note that I've changed "to" to "toD" because of "${file:name}".
Hope it helps.
R.
Requisite disclaimer about being new to Camel--and, frankly, new to developing generally. I'd like to have a string generated as the output of some function be the source of my camel route which then gets written to some file. It's the first part that seems challenging: I have a string, how do I turn it into a message? I can't write it into a file nor can I use JMS. I feel like it should be easy and obvious, but I'm having a hard time finding a simple guide to help.
Some pseudo-code using the Java DSL:
def DesiredString() {return "MyString";}
// A camel route to be implemented elsewhere; I want something like:
class MyRoute() extends RouteBuilder {
source(DesiredString())
.to("file://C:/out/?fileName=MyFileFromString.txt");
}
I vaguely understand using the bean component, but I'm not sure that solves the problem: I can execute my method that generates the string, but how do I turn that into a message? The "vague" is doing a lot of work there: I could be missing something there.
Thanks!
Not sure if I understand your problem. There is a bit of confusion about what the String should be become: the route source or the message body.
However, I guess that you want to write the String returned by your method into a File through a Camel route.
If this is correct, I have to clarify first the route source. A Camel Route normally starts with
from(component:address)
So if you want to receive requests from remote via HTTP it could be
from("http4:localhost:8080")
This creates an HTTP server that listens on port 8080 for messages.
In your case I don't know if the method that returns the String is in the same application as the Camel route. If it is, you can use the Direct component for "method-like" calls in the same process.
from(direct:input)
.to("file:...");
input is a name you can freely choose. You can then route messages to this route from another Camel route or with a ProducerTemplate
ProducerTemplate template = camelContext.createProducerTemplate();
template.sendBody("direct:input", "This is my string");
The sendBody method takes the endpoint where to send the message and the message body. But there are much more variants of sendBody with different signatures depending on what you want to send it (headers etc).
If you want to dive into Camel get a copy of Camel in Action 2nd edition. It contains everything you need to know about Camel.
Example:Sending String(as a body content)to store in file using camel Java DSL:
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
public void configure() {
from("timer:StringSentToFile?period=2000")
.setBody(simple(DesiredString()))
.to("file:file://C:/out/?fileName=MyFileFromString.txt&noop=true")
.log("completed route");
}
});
ProducerTemplate template = context.createProducerTemplate();
context.start();
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}")
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.