Camel PollEnrich not working properly - apache-camel

After transforming a file with xslt I have to append a file downloaded from ftp. So I did the following:
from("direct:adobe_productList_incremental")
.id("routeADOBESPtransformPI_productList")
.log(LoggingLevel.INFO, "---------Starting file: ${body}")
.convertBodyTo(InputStream.class)
.to("xslt:classpath:" + xsltTransformationProductList)
.log(LoggingLevel.INFO, "---------Transformed file: ${body}")
.pollEnrich(ftpType+"://"+ftpUsername+"#"+ ftpUrl +":" + ftpPort + ftpPath_incrementalComplete +"?password="+ftpPassword+"&fileName="+ftpFilename_incrementalComplete+"&passiveMode=true&binary=true&delete=false",10000)
.log(LoggingLevel.INFO, "---------After poll enrich: ${body}")
.to("file:{{file.root}}{{file.outbox.products_list_incremental}}?fileName={{file.outbox.products_list_incremental.name}}.final");
untill the poll everythin works (the transformation is done correctly), but after the pollEnrich the current body is overrided by the ftp content (and not appended as it should be).
Any help?

No it works as designed.
By default the content will be overridden. If you need to append/merge or whatever, you need to use a custom aggregation strategy, and implement code logic that does this.
See the Camel docs at: http://camel.apache.org/content-enricher.html about the ExampleAggregationStrategy.
The Camel docs says
The aggregation strategy is optional. If you do not provide it
Camel will by default just use the body obtained from the resource.

Related

get JMSMessageID when use producerTemplate

In Camel,
ProducerTemplate producerTemplate = exchange.getContext().createProducerTemplate();
producerTemplate.sendBody("endpointqueue?includeSentJMSMessageID=true", ExchangePattern.InOnly, body);
I would need to get JMSMessageID that is returned from IBM MQ/ActiveMQ. I am looking at exchange values on debug mode but cannot find it. I can only find sessionID. Where is it stored and how to get it?
The Camel documentation says:
includeSentJMSMessageID - only applicable when sending to jms destination using InOnly. enabling this option will enrich the Camel Exchange with the actual JMSMessageID that was used by the JMS client when the message was sent to the JMS destination.
Is includeSentJMSMessageID different than my needs or am I missing something?
The doc says the JMS MessageID is available as header.
So you should be able to return it like this:
from("direct:queueMessage")
.to("jms://myqueue?includeSentJMSMessageID=true")
.setBody().simple("${header.JMSMessageID}");
And then send your message using:
String msgId = producerTemplate.requestBody("direct:queueMessage", body, String.class);

PrettyPrint feature does not work in Apache Camel

I have been trying to leverage the PrettyPrint feature to display the result of my API that is using Apache Camel. Here is the context. I have this route in my code
// Route Definition for processing Health check request
from("direct:processHealthCheckRequest")
.routeId("health")
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
.setBody(constant(healthCheckResponse));
When I'm using Postman to test my API, the display is in pretty mode even though it is not set to true, like so
{
"status": "UP"
}
Now when I'm using the following code to set the PrettyPrint to false, I'm still getting the same result. It looks like the PrettyPrint feature is not working as it is supposed to
// Route Definition for processing Health check request
from("direct:processHealthCheckRequest")
.routeId("health")
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
.setBody(constant(healthCheckResponse))
.unmarshal()
.json(JsonLibrary.Jackson, HealthCheckResponse.class, false);
I'm expecting the result to be displayed on one line like here without changing the type from JSON to string.
{"status": "UP"}
Could someone please advice on this?
I've bumped into the same issue always when manually setting the HTTP_RESPONSE_CODE header. I don't know why it technically happens - without it the HTTP response always returns proper JSON for me.
Setting CONTENT_TYPE header to application/json has solved it:
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
The solution that finally worked was to set the following in my application.properties file.
camel.rest.data-format-property.prettyPrint=false
or not to provide that property at all.
Try this:
<removeHeaders id="removeHeaders_http*" pattern="CamelHttp*"/>
<setHeader headerName="Content-type" id="content_setHeader">
<constant>application/x-www-form-urlencoded</constant>
</setHeader>
Same with Java DSL:
.removeHeaders("CamelHttp*")
.setHeader("Content-type", constant("application/x-www-form-urlencoded"))

Execute route only when direct component is called

I want to unzip files whenever a "direct" route is called. The fileName I am getting from some other direct route.
from("direct:unZipFile")
.from("file:C:\\MYFILES\\File\\Unzipped\\?fileName=${header.fileName}&idempotent=true")
.split(new ZipSplitter())
.streaming()
.to("file:C:\\MYFILES\\File\\Unzipped\\")
.split(body().convertToString().tokenize("\n"))
.transform()
.simple("${in.body}")
.end();
Now it is working when I call the direct component but it also keeps on scanning the directory and processes the same file. I understand that the above code allows the trigger from direct as well as file component but I just want it to execute from "direct" component and I can't remove the "file" component as from that only I am reading the file.
You can also try to use pollenrich
from("direct:unZipFile")
.pollEnrich.simple("file:C:\\MYFILES\\File\\Unzipped\\?fileName=${header.fileName}")
.split(new ZipSplitter())
.streaming()
...
You can use consumerTemplate
from("direct:unZipFile")
.process(exchange -> {
Exchange recvFiles = exchange.getContext().createConsumerTemplate().receive("file:C:\\MYFILES\\File\\Unzipped\\?fileName=${header.fileName}&idempotent=true");
exchange.getIn().setBody(recvFiles.getIn().getBody());
exchange.getIn().getHeaders().putAll(recvFiles.getIn().getHeaders());
})
.split(new ZipSplitter())
.streaming()
.to("file:C:\\MYFILES\\File\\Unzipped\\")
.split(body().convertToString().tokenize("\n"))
.transform()
.simple("${in.body}")
.end();
You can also just set the message body with a java.io.File where you compute the file name in a few lines of Java code, but yeah otherwise the pollEnrich is the EIP pattern solution for this.

Apache Camel route with no "to" endpoint

I am using Apache Camel to assist with capturing message data emitted by a third party software package. In this particular instance, I only need to capture what is produced by the software, there is no receiver on the other end (really no "end" to go to).
So, I tried to set up a route with just the "from" endpoint and no "to" endpoint. Apparently this is incorrect usage as I received the following exception:
[2018-08-15 11:08:03.205] ERROR: string.Launcher:191 - Exception
org.apache.camel.FailedToCreateRouteException: Failed to create route route1 at: >>> From[mina:udp://localhost:9877?sync=false] <<< in route: Route(route1)[[From[mina:udp://localhost:9877?sync=false]] -... because of Route route1 has no output processors. You need to add outputs to the route such as to("log:foo").
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1063)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:196)
at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:974)
at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:3301)
at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:3024)
at org.apache.camel.impl.DefaultCamelContext.access$000(DefaultCamelContext.java:175)
at org.apache.camel.impl.DefaultCamelContext$2.call(DefaultCamelContext.java:2854)
at org.apache.camel.impl.DefaultCamelContext$2.call(DefaultCamelContext.java:2850)
at org.apache.camel.impl.DefaultCamelContext.doWithDefinedClassLoader(DefaultCamelContext.java:2873)
at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:2850)
at org.apache.camel.support.ServiceSupport.start(ServiceSupport.java:61)
at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:2819)
at {removed}.Launcher.startCamel(Launcher.java:189)
at {removed}.Launcher.main(Launcher.java:125)
Caused by: java.lang.IllegalArgumentException: Route route1 has no output processors. You need to add outputs to the route such as to("log:foo").
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1061)
... 13 more
How do I set up a camel route that allows me to intercept (capture) the message traffic coming from the source, and not send it "to" anything? There is no need for a receiver. What would be an appropriate "to" endpoint that just drops everything it receives?
The exception suggestion of to("log:foo"). What does this do?
You can see if the Stub component can help
http://camel.apache.org/stub.html
Example:
from("...")
.to("stub:nowhere");
The exception suggestion of to("log:foo"). What does this do?
It sends your route messages to an endpoint with a component of type log:
(http://camel.apache.org/log.html) - component which basically dumps message contents (body and/or headers and/or properties) to your log file using appropriate log category.
If you just want to drop everything received, it's a good choice:
to("log:com.company.camel.sample?level=TRACE&showAll=true&multiline=true")
Apparently if you're under Linux or Unix, you can also redirect to /dev/null like in this example:
to( "file:/dev?fileName=null")
I am not sure it can be used on Windows but I don't think so.
Note that the syntax: to( "file:/dev/null") does not work as it point to a directory called null but with the fileName option it will work.

ApacheCamel: readLock = idempotent vs readLock = rename

In my application, we have multiple nodes for sharing File system. So in this case,idempotentRepository required for Locking mechanism.
Question:
In Apache Camel why "readLock = idempotent" is working only for File component but not for FTP?
readLock: idempotent not compatible for FTP, its compatible only for File component.
<from uri="ftp://XXX:xxxxxx#localhost/var/opt/irs/message?delete=true&readLock=idempotent&idempotentRepository=#idempotentRepo&readLockLoggingLevel=WARN&shuffle=true" />
readLock: rename is compatible for FTP
and I achieved with this
<from uri="ftp://XXX:xxxxxx#localhost/var/opt/irs/message?delete=true&readLock=rename&idempotentRepository=#idempotentRepo&readLockLoggingLevel=WARN&shuffle=true" />
I would like to know the reson behind it.
Can anybody explain the reson behind it?
As you can clearly see in the document here. It states
idempotent -- Camel 2.16 (only file component) is for using a idempotentRepository as the read-lock.
You can achieve this for ftp by doing something like this
from("ftps://{{ftp.username}}#{{ftp.host}}/{{ftp.importDirectory}}?password="
+ "{{ftp.password}}&readLock=changed&move={{ftp.processed}}"
+ "&moveFailed={{ftp.failed}}"
+ "&securityProtocol=SSL&execProt=P&execPbsz=0&passiveMode=true")
.idempotentConsumer(header("camelFileAbsolutePath"),
MemoryIdempotentRepository.memoryIdempotentRepository(200))
.to("bean:ftpConsumer?method=consumeMethod")

Resources