[JDK 8 / 11, Camel 2.23.1, Spring Boot 2.1.3]
Hi,
I'm currently learning Camel and I can't get past a strange problem.
I want to download a URL to a file.
The URL works, e.g. http://localhost/date shows "11:59:00".
The target file is created, but it's empty. Why?
#Component
public class DownloadRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
String timer1 = "timer://foo?fixedRate=true&period=10000&delay=5000";
String url = "http://localhost:8888/date";
String file = "file:target/date";
from(timer1).to(url).log("${id}->${body}").to(file); // ID-Laptop-1551464093962-0-2->11:59:00, that's ok
}
}
The logger outputs the correct content, e.g.
2019-03-01 19:15:00.421 INFO 2984 --- [3 - timer://foo] route1
: ID-Laptop-1551464093962-0-2->11:59:00
but the generated file (e.g. target/date/ID-Laptop-1551464093962-0-2) is empty.
Any ideas?
Solution (thanks to Tache):
from(timer1).to(url).to(file); // without logging
OR
from(timer1).to(url).convertBodyTo(String.class).log("${id}->${body}").to(file); // with logging
The output of your http endpoint (http://localhost:8888/date) is probably a stream that is read using a InputStream. Such stream can be read only once.
Possible solutions:
remove your log statement
enable stream caching in your Camel context or route
convert message body to a string (.convertBodyTo(String.class) ) before logging it and sending it to a file
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 want to run a processor upon file appearance in a directory. My file url is like this:
file:{{file.root}}in?include=.*\.csv&charset=windows-1251&move=../out/done
A procedure that associates an url with a processor is like this:
MessageProcessor getOrCreateConsumer(CamelContext context, String uri) {
Endpoint endpoint = context.getEndpoint(uri);
endpoint.setCamelContext(context); // added this out of desperation, doesn't help
processor = new MessageProcessor();
try {
Consumer consumer = endpoint.createConsumer(processor);
endpoint.start(); // do we need this at all? works the same without it
consumer.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
return processor;
}
}
MessageProcessor is a processor that does some things to an exchange.
Everything seems to work except the file doesn't get moved to the ../out/done directory. While debugging I can't get when the endpoint is configured to provide the file message exchange with this post operation.
I think I am missing some magic call that is normally invoked by a RouteBuilder and that will fully configure the file endpoint. Can you please help me out?
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}")
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)
...
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);
}
}