How to fix several input per route in Camel 3.X.X? - apache-camel

I have a route that looks like this:
from(URL_A)
.from(URL_B)
.to(URL_C)
.process(...)
// logging
.to(URL_D)
This route works perfectly in Camel 2.X.X but not in 3.7.X
The error message I get:
Only one input is allowed per route. Cannot accept input: From[direct:ABCD]
I checked the migration guide, but I cannot get how to migrate this sort of route.
Do you have any idea how to tackle it further?

I think you can use direct component: https://camel.apache.org/components/3.4.x/direct-component.html
For example:
from(URL_A)
.to(direct:collector)
from(URL_B)
.to(direct:collector)
from(direct:collector)
.to(URL_C)
.process(...)
// logging
.to(URL_D)

#Stepan Shcherbakov suggested a solution, below will be an enhancement of it:
String [] sources = {URL_A, URL_B}
for (String source : sources) {
from(source)
.to(direct:collector)
}
from(direct:collector)
.to(URL_C)
.process(...)
// logging
.to(URL_D)

Related

Is it the designed and desired behavior that makes Camel routes to not execute onCompletion tasks in a pipeline with several SEDA queues?

I stumbled upon a problem with not working onCompletion between the routes that pass a message over SEDA queues.
The route configuration is similar to this simplified version:
from("direct:a")
.onCompletion().log("a - done").end()
.to("seda:b");
from("seda:b")
.onCompletion().log("b - done").end()
.to("seda:c");
from("seda:c")
.onCompletion().log("c - done").end()
.to("seda:d");
from("seda:d")
.onCompletion().log("d - done").end()
.to("mock:end");
With this configuration, I get only "d - done" logging.
I debugged the execution and noticed that the onCompletion handler from "a", "b", and "c" don't get executed because they are route-scoped and get attempted to be executed in the scope of the next route.
This happens because they get handed over from the initial exchange to an exchange prepared for the next route. It happens in the SedaProducer::addToQueue method with copy parameter defined as true, which makes the ::prepareCopy method being called, which in its turn calls ExchangeHelper.createCorrelatedCopy with handover defined as true.
It seems to me like a bug, because looking at the routes configuration I'd expect different behavior: all the onCompletion tasks get executed reporting on routes finalization. Though maybe I'm missing something here, and if this is the case then I would appreciate you guys helping me to find out the missing details.
The workaround I implemented is ugly but does the trick. Before sending to the next route (queue) we hand over completions to a holder exchange and after sending we hand over them back from the holder.
Here is a code example:
route
.process(exchange -> {
var holder = new DefaultExchange(exchange.getContext());
exchange.adapt(ExtendedExchange.class).handoverCompletions(holder);
exchange.setProperty(SYNC_HOLDER_PROPERTY, holder);
})
.to("seda://next")
.process(exchange -> exchange
.getProperty(SYNC_HOLDER_PROPERTY, ExtendedExchange.class)
.handoverCompletions(exchange));

Gatling Required ChainBuilder Found B When ExitOnBlockOrFail. Method Exec Not Recognised

I am wondering why this code shows an error saying that it expects a ChainBuilder but found B, and what B actually is. I'm a few months now wondering about this issue since my code still compiles with it but I would love to remove this error if possible.
Even when I simplify it the most but keeping the exitBlockOnFail piece it will still give me the red warning, so I'd like to know what is the connection between the two of them please.
// Error
object StudentDashboardBranches {
def studentDashboard: ChainBuilder = exitBlockOnFail(
exec {
session =>
println(session("session").as[String])
session
}
)
}
// No error
object StudentDashboardBranches {
def studentDashboard: ChainBuilder = (
exec {
session =>
println(session("session").as[String])
session
}
)
}
And perhaps a different question or maybe related to the same issue, not sure, but the exec method appended after the pause is not recognised, however, this error doesn't leave if I delete the exitBlockOnFail piece as the first one does.
PS: These are my imports
import io.gatling.core.Predef.{exec, _}
import io.gatling.core.structure.ChainBuilder
Any help or clarification is much appreciated.
Many thanks.
UPDATE
As per Stephane's answer, we were missing one of the imports.
import io.gatling.core.Predef._ // required for Gatling core structure DSL
It's working now.
Have you broken the DSL imports? If so, please check the doc.

CakePhp 4.x basic Authentication

I am following the CakePHP 4.x tutorial to the letter (as far as I can see) until chapter "CMS Tutorial - Authentication".
Half way through "Now, on every request, the AuthenticationMiddleware will inspect the request session to look for an authenticated user. If we are loading the /users/login page, it will also inspect the posted form data (if any) to extract the credentials."
When I try to access articles or users I get an error:
( ! ) Fatal error: Interface
'Authentication\AuthenticationServiceProviderInterface' not found in
C:\wamp64\www\cake\src\Application.php on line 41
I have tried to figure out why this would be, but I cannot find it. I have tried looking up the same problem on the internet, no dice. Not even a mention that this could be security related (I found a mention about strict brower settings earlier but it was related to another problem).
I have uploaded my code on Github here: https://github.com/plafeber/cakephp-tutorial
I would greatly appreciate any feedback. I was under the assumption that if I create the full code set from the tutorial, given of course I run CakePHP 4.1.5 and follow the related Cake 4.x manual, that it would work. However, I already found out that I have to change the line about the use of DefaultPasswordHasher compared to what was in the code. So I can imagine the Tutorial page is not exactly as it should be.
This would be hte correct line about the use of the DefaultPasswordHasher in User.php;
//the use line
use Cake\Auth\DefaultPasswordHasher as AuthDefaultPasswordHasher;
//and the function
protected function _setPassword(string $password) : ?string
{
if (strlen($password) > 0) {
$hasher = new AuthDefaultPasswordHasher();
return $hasher->hash($password);
}
}
The solution to this was to navigate to the Cake install dir (containing the src and config folder and so on), then running the Composer call again. This apparently placed the filed in the right directories and then the error no longer appeared.

How to dynamically return a from endpoint in apache camel DSL

Here is my code
from("google-pubsub:123:subscription1?maxMessagesPerPoll=3 & concurrentConsumers=5" ).routeId("myroute")
.process(new ProducerProcessor())
to("google-pubsub:123:topic1")
;
In my code above ,the from channel I want to make it generic.Basically it should be able to consume data from good-pubsub or may be from a file or from a JMS queue.Hence depending upon a parameter I want to return
a different from channel.Something like below
private RouteDefinition fromChannel(String parameter) {
if (parameter is "google" then
return from("google-pubsub:123:subscription1?maxMessagesPerPoll=3 & concurrentConsumers=5" )
if (parameter is "file" then
return from(/my/fileFolder/)).split(body().tokenize("\n")).streaming().parallelProcessing();
}
I tried this but I am getting null pointer exception in the fromChannel method.Please let me know if you have better ideas.
Rewrite based on comment
You can for example create a (static) template route for every input type and generate the routes based on a configured endpoint list.
I described such an endpoint configuration and route generation scenario in this answer.
Like this you can generate the split part for every file route and any other specialty for other route types.
All these input routes are routing at their end to a common processing route
.from(pubsubEndpoint)
.to("direct:genericProcessingRoute")
.from(fileEndpoint)
.split(body()
.tokenize("\n"))
.streaming()
.parallelProcessing()
.to("direct:genericProcessingRoute")
.from("direct:genericProcessingRoute")
... [generic processing]
.to("google-pubsub:123:topic1")
The multiple input (and output) routes around a common core route is called hexagonal architecture and Camel fits very well into this.

Catch-all routing using Tipfy

Using tipfy, how does one express a catch-all route in urls.py if more specific routes do not match?
Tipfy uses Werkzeug-like routing, so there's this (in urls.py):
def get_rules(app):
rules = [
Rule('/<any>', endpoint='any', handler='apps.main.handlers.MainHandler'),
Rule('/', endpoint='main', handler='apps.main.handlers.MainHandler'),
]
This will match most random entry points into the application (app.example.com/foo, app.example.com/%20 etc) but does not cover the app.example.com/foo/bar case which results in a 404.
Alternatively, is there a graceful way to handle 404 in Tipfy that I'm missing?
I think you want:
Rule('/<path:any>', endpoint='any', handler='apps.main.handlers.MainHandler')
The path matcher also matches slashes.
Maybe you could write custom middle ware:
class CustomErrorPageMiddleware(object):
def handle_exception(self, e):
return Response("custom error page")
To enable it add somewhere to tipfy config:
config['tipfy'] = {
'middleware': [
'apps.utils.CustomErrorPageMiddleware',
]
}
It gives you quite a flexibility - you could for example send mail somewhere to inform that there was a problem. This will intercept all exceptions in your application

Resources