I'm pretty new in camel, sorry if this question is stupid but lets say I have a camel splitter which iterates over some objects from the database. I'd like to ask if there is something like continue in camel splitter. Let's say I have an array of numbers like {1,2,3,4} in a body and I want to print numbers, but I don't want to print number 3. I know I can use choose but in some cases continue would be a better option. Thank you very much.
In this case, I would use Message Filter EIP, either with filter(Predicate) or stop()
Using filter:
from("direct:filter3")
.split(body())
.filter(body().isNotEqualTo(3))
.to("log:splitted");
Using stop:
from("direct:stop3")
.split(body())
.choice().when(body().isEqualTo(3)).stop().end()
.to("log:splitted");
Related
I have two queues which having same type of objects in them. I want to aggregate them into a single queue through java DSL. Could anyone tell me if this is possible? If so, any code references?
If I understand your question correctly, it is possible to do such a thing.
If you need just to drive them into a single route (without any aggregations, enrichments, etc.) you can just proceed with this piece of code:
from('direct:queue1')
.to('direct:start');
from('direct:queue2')
.to('direct:start');
from('direct:start')
//there goes your processing
If you need to aggregate them later on, use Aggregator. Or you can use example from java-addict301's answer if it solves your case.
I believe this may be doable in Camel using the Content Enricher pattern.
Specifically, the following paradigm can be used to retrieve a message from one queue (where direct:start is) and enrich it with a message from the second queue (where direct:resource is). The combined message can then be built in your AggregationStrategy implementation class.
AggregationStrategy aggregationStrategy = ...
from("direct:start")
.enrich("direct:resource", aggregationStrategy)
.to("direct:result");
from("direct:resource")
My question is very similar to this one but the solution there does not work for me here - I am trying to use the filter EIP to discard selected exchanges. My routes look like (edited down for clarity):
from("{{fromSource}}")
.convertBodyTo(RequestInterface.class)
.enrich(INVOKE_BACKEND_URI, combiner)
.to("{{toDestination}}");
from(INVOKE_BACKEND_URI)
.to(backendUri)
.filter().method(DiscardResponse.class).log(LoggingLevel.INFO, "Discarding undesired response").stop().end()
.convertBodyTo(BodyInterface.class);
When the filter does NOT select the message, all is well - the log() is not displayed and the message goes to the convertBodyTo() and then back to the main route.
However, when the filter DOES select the message, the log() text is displayed but the exchange still continues on to the convertBodyTo() where it throws an exception because it's a message that shouldn't be there. The stop() appears to either not be executed or has no affect.
Can anyone suggest a solution to this?
It is possible from within a Processor to do this in order to stop the exchange:
exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);
Since I'm not used to writing my routes using Java DSL I don't know if that option is available directly on the exchange within the route, but it probably is.
I guess one way could be:
from(INVOKE_BACKEND_URI)
.to(backendUri)
.filter().method(DiscardResponse.class).log(LoggingLevel.INFO, "Discarding undesired response")
.choice()
.when(simple("${property.Exchange.FILTER_MATCHED}=true")
.stop()
.end()
.convertBodyTo(BodyInterface.class);
Take a look at the bottom of the doc here:
http://camel.apache.org/message-filter.html
After using recipientList in a camel route, within a choice.when, I would like to route this further to another destination with to("xy").
Syntax-highlighting in a java IDE is showing me that this is not possible.
If I put an end after recipientList, all appears to fit again.
Is that required? I could not find any examples in the docs/net showing s/t similar...
.choice()
.when(aPredicate)
.setHeader(Exchange.FILE_NAME).simple("st")
.recipientList(getAValueBuilder())
.end()
.to("ftp:me#ftpserv//usr/dest")
.when(anotherPredicate)
.to(nirv)
.setHeader(Exchange.FILE_NAME).simple("nt")
.to("ftp:me#ftpserv//usr/anotherdest")
.end()
Generarilly I am not sure, while using choice, when to use end. And to make it more difficult, there is an endChoice...
I tryed to use the formatting, to show the way I think it should be use above.
Thanks for feedback.
I'm looking for a way to conditionally handle messages based on the aggregation of messages. I've looked into a lot of ways to do this, but it seems that Apache Camel doesn't support it. I'll explain the scenario and then the solutions I tried.
Scenario:
I'm trying to conditionally clean a directory. I poll from the directory every x days and fetch all the files (file://...). I route this into an aggregation, that aggregates the files into a single size (directorySize). I then check if this size passes a certain threshold.
Here is where the problem lies. I now want to remove certain files if this condition passes, but I don't have access to the original messages anymore because they were aggregated in a new exchange.
Solutions:
I tried to fetch the files again to process them. Problem is that you can't make a consumer fetch on demand as far as I know. I tried using pollEnrich, but that will only fetch a single file and not all files in the directory.
I tried to filter/stop the parent route. The problem here is that filter()/choice...stop()/end() will only stop the aggregated route with the directory size and not the parent route with the file messages. I can't conditionally process these.
I tried to move the aggregated condition to another route that I would call, but this causes the same problem as the first solution.
Things I consider doing:
Rewrite the aggregation strategy to not only aggregate the size, but also the files itself into a groupedExchange. This way I can split the aggregation again after the check. I don't really like this solution because it causes a lot boilerplate, both in code as during runtime.
Move the file size calculator to a processor instead of the aggregator. This would defeat the purpose of using camel in the first place.. I would manually be fetching the files and adding the sizes.. And that for every single file..
Use a ControlBus to dynamically start the delete route on that directory. Once again a lot of workaround to achieve something that I feel should be able to be done in a simple route.
I would like to set the calculated size on every parent message, but I have no clue how this could be achieved?
Another way to stop the parent route that I haven't thought of?
I'm a bit stunned that you can't elegantly filter messages based on the aggregation of these messages. Is there something that I missed in Camel that would provide an elegant solution? Or is this a case of the least bad solution?
Simple Schema
Message(File)
Message(File) --> AggregatedMessage(directorySize) --> delete certain Files?
Message(File)
Camel is really awesome, but sometimes it's sure difficult to see exactly which design pattern to use ;)
Firstly, you need to keep a copy of the file objects, because you don't know whether to delete them or not until you reach your threshold - there are basically (at least) two ways to do this.
Alternative 1
The first way is to use a List in an exchange property. This property will hang around no matter what you do with the exchange body. If you have a look at the source code for GroupedExchangeAggregationStrategy, it does precisely this:
list = new ArrayList<Exchange>();
answer.setProperty(Exchange.GROUPED_EXCHANGE, list);
// ...
list.add(newExchange);
Or you could do the same thing manually on your own exchange property. In any case, it's completely fine to use the Grouped aggregation strategy as you have done.
Alternative 2
The second way to "keep" old messages is to send a copy to a stopped SEDA queue. So you would do to("seda:xyz"). You define this queue as .noAutoStartup(). Then you can send messages to it and they will queue up on an internal queue, managed by camel. When you want to process the messages, you simply start it up via controlbus and stop it again afterwards.
Generally, messing around with starting and stopping queues should be avoided unless absolutely necessary, but that's certainly another way to do it
Suggested solution
I suggest you do as you have done (i.e. alternative 1):
aggregate via GroupedExchangeAggregationStrategy to keep the individual files in a list
Compute the total file size (use a processor, or do it along the way with a custom aggregation strategy)
Use a filter(simple("${body} < 123"))
"Unwind" your aggregation via a splitter(simple("${property.CamelGroupedExchange}"))
Delete your files one by one
Please let me know if this doesn'y makes sense, or if I have misunderstood your problem in any way.
If X is false I want to route to A, if X is true I want to route to A and B
I tried to write something like
from(?)
.choice()
.when( X )
.multicast().to(A,B).end()
.otherwise() // I get a (compile) error underlined here saying 'otherwise() is not on type ProcessorDefinition
.to( A )
it doesn't like it
I suspect this isn't the best way of phrasing this
basically I always want to route to (A) and if that condition is there I also want to route to (B)
what is the best way of expressing this in Camel?
use endChoice() at the end of your when() clause and it'll work...
see http://camel.apache.org/why-can-i-not-use-when-or-otherwise-in-a-java-camel-route.html
See this FAQ about the choice: https://camel.apache.org/why-can-i-not-use-when-or-otherwise-in-a-java-camel-route.html
You can also use dynamic recipient list and compute the endpoints to route to. Then you can return 1 or 2 depending on the conditions: http://camel.apache.org/recipient-list.html
If you always want your message to go to route A, then do not include it in the choice clause
from(?)
.to( A )
.choice()
.when( X )
to(B).end()
Something like above should suffice your case. Also read the articles that Claus has given in his answer.
Regarding your compilation error, remove the end() after the when clause. end() causes the choice() clause to be finished but you then use otherwise() clause while choice has already been closed.
I have found that expressing your routes using the XML notation is a lot more concise in meaning.
For instance with the Java DSL people often make the mistake of not calling, or even adding 'endChoice()' and 'end()' like you have in your example; Sometimes you will also face an issue with Camel's Route Builder which is currently a limitation due to Java's Generics.
Unfortunately using XML comes with the cost of using XML :)