Instantiate one Routebuilder from inside a route in another RouteBuilder in Camel - apache-camel

I am relatively new to Camel. I have a use case where I need to instantiate a RouteBuilder only when it receives get an exchange to kickstart the process from an Orchestration module. I am trying to do this mainly because, the exchange carries information required to instantiate the new RouteBuilder. Is there a way where I could instantiate this new RouteBuilderB from inside a route in the existing RouteBuilderA.
public class RouteBuilderA extends RouteBuilder {
public void configure(){
//So, something like this?
from("direct:A")
.process(//new RouteBuilderB())
.to("direct:B")
}
Is there a way to accomplish this?

Yes its just Java code, so write a Processor that creates the RoutBuilder instance you want, and do any configuration with setter/getter etc. And then you can add that as routes to CamelContext using the addRoutes method.

Related

Use string as Apache Camel endpoint?

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();

How to pass parameters to a Camel route?

It is possible to pass parameters to a Camel route?, for instance, in the next code snippet:
public class MyRoute extends RouteBuilder {
public void configure() throws Exception {
from("direct:start")
.to("cxf:bean:inventoryEndpoint?dataFormat=PAYLOAD");
}
}
The value for dataFormat is in hard code, but, what if I want set it dynamically?, passing a value from the code where route is called. I know this is possible adding a constructor and passing parameters in it, like this:
public class MyRoute extends RouteBuilder {
private String type;
public MyRoute(String type){
this.type = type;
}
public void configure() throws Exception {
from("direct:start")
.to("cxf:bean:inventoryEndpoint?dataFormat=" + type);
}
}
There is another way?
Thanks so much!
As you mentioned, you can use a constructor (or setters or any other Java/Framework instruments) if the parameters are static from a Camel point of view.
The parameters are configurable in the application, but after the application is started they do no more change. So, every message processed by the Camel route uses the same value.
In contrast, when the parameters are dynamic - i.e. they can change for every processed message, you can use the dynamic endpoint toD() of Camel. These endpoint addresses can contain expressions that are computed on runtime. For example the route
from("direct:start")
.toD("${header.foo}");
sends messages to a dynamic endpoint and takes the value from the message header named foo.
Or to use your example
.toD("cxf:bean:inventoryEndpoint?dataFormat=${header.dataFormat}");
This way you can set the dataformat for every message individually through a header.
You can find more about dynamic endpoints on this Camel documentation page

Get the CamelContext for standalone Apache Camel instance

I'm using the Main class from Apache Camel to run it as standalone.
I need to use the JMS component so I have to add it to the CamelContext instance used by the Main class.
Of course I need to do that before calling main.run(args) method.
The problem is the following ...
Using main.getCamelContexts().get(0) returns an index out of bounds exception.
Using main.getOrCreateCamelContext() returns a valid CamelContext instance named "camel-1" to which I'm able to add my JMS component but .... when I execute main.run(args), another CamelContext named "camel-2" is used !
The only way I found to add my JMS component is to use :
main.bind("jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
Is this the only way or the CamelContext way should work ?
Thanks,
Paolo.
You can provide completely own camel context to be used. To achieve that you can inherit org.apache.camel.main.Main and override only one method
protected Map<String, CamelContext> getCamelContextMap()
Here is the example of inherited body:
#Override
protected Map<String, CamelContext> getCamelContextMap() {
Map<String, CamelContext> camelContextMap = new HashMap<>();
DefaultCamelContext camelContext = new DefaultCamelContext();
camelContext.setName("MyContext");
// Add your context configuration here
camelContextMap.put("connectorContext", camelContext);
return camelContextMap;
}
In general it is better to create context map not in the "getCamelContextMap()" inherited method but somewhere in the constructor.
Old Question, but was able to figure it out - tried with camel 2.17.x version
private void runMyExample() {
//Add a Main Listener
main.addMainListener(new MyMainListener);
main.run();
}
public static class MyMainListener extends MainListenerSupport {
#Override
public void afterStart(MainSupport main) {
System.out.println("MainExample with Camel is now started!");
//This is the right instance
CamelContext context = main.getCamelContexts().get(0);
}
#Override
public void beforeStop(MainSupport main) {
System.out.println("MainExample with Camel is now being stopped!");
}
}
Regards,
R.

Get Camel route information from Endpoint

I am creating a JMS route programatically by following code :
from("jms:queue:OUTBOUND_QUEUE?concurrentConsumers=5&messageListenerContainerFactoryRef=msgListenerContainerFactory").processRef("mqprocessor");
I have class :
public class MessageListenerContainerFactoryImpl implements MessageListenerContainerFactory {
#Override
public AbstractMessageListenerContainer createMessageListenerContainer(
JmsEndpoint endpoint) {
}
}
I want to exchange some information/parameter between the above route and endpoint. And based on the parameter value I want choose the connection factory to be set in this message listener container.
Please let me know if I was able to explain my problem statement.
Is there any other way to achieve this? I want build connectionfactory at runtime and so do route.
Is there any method in JmsEndpoint which I can leverage to know route-id?
I would create such a route in a method, supply the MLC as a parameter, then when needed, remove that route gracefully and recreate it with new parameters. Changing the parameters does not really make much sense.

What are the pros/cons when defining an Endpoint as a URI over defining it programatically?

Consider the following:
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
FileEndpoint dropLocation = new FileEndpoint();
dropLocation.setCamelContext(getContext());
dropLocation.setFile(new File("/data"));
dropLocation.setRecursive(true);
dropLocation.setPreMove(".polled");
dropLocation.setNoop(true);
dropLocation.setMaxMessagesPerPoll(1);
from(dropLocation).to(...
versus
public class MyBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("file://data?recursive=true&preMove=.polled&noop=true&maxMessagesPerPoll=1").to(...
Programatically I get code completion and the like, whereas with the URI everything is in a single line. Are these the only pros/cons or are there others to consider?
Pretty much all the examples I see utilise the URI method - is there a strong reason for this?
generally you rely on the Component to create the Endpoint instance (via route definitions), but it can be done programmatically if there is a desire to integrate with legacy code, create Endpoints through class structures/instances, etc.
overall, a major benefit of Camel is to leverage it's concise DSL route capabilities to describe processes/interactions all in one place (a route). the more programmatic the route definitions are, the more verbose/spread out these definitions become...
overall, I prefer the URI approach because its more concise, easier to follow and nice to manipulate route parameters all in one place...otherwise, its entirely a preference/style decision.

Resources