Get the CamelContext for standalone Apache Camel instance - apache-camel

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.

Related

Using Camel Endpoint DSL with #EndpointInject creating different endpoints

What is the proper way to use endpoint DSL and then reference the endpoint with ProducerTemplate? When creating a route and using endpoint DSL, it seems that Camel is creating a different uri for the endpoint. My EndpointRouteBuilder class:
#Component
public class MyRoutes extends EndpointRouteBuilder {
#Override
public void configure() throws Exception {
from(seda("STATUS_ENDPOINT"))
.routeId("stateChangeRoute")
.to(activemq("topic:statusTopic"))
}
}
and then injecting the endpoint to ProducerTemplate
#Component
public class StateChangePublisher {
#EndpointInject(value="seda:STATUS_ENDPOINT")
private ProducerTemplate producer;
public void publish(String str) {
try {
producer.sendBody(str);
} catch(CamelExecutionException e) {
e.printStackTrace();
}
}
}
When camel starts, I see two entries in the log:
o.a.camel.component.seda.SedaEndpoint : Endpoint seda:STATUS_ENDPOINT is using shared queue: seda:STATUS_ENDPOINT with size: 1000
o.a.camel.component.seda.SedaEndpoint : Endpoint seda://STATUS_ENDPOINT is using shared queue: seda://STATUS_ENDPOINT with size: 1000
The queue eventually fills up and nothing gets delivered to the "to" endpoint.
If I define the route without using the endpoint DSL method "seda()"
from("seda:STATUS_ENDPOINT")
then it works.
Is this a bug or am I doing something wrong?
I'm using camel 3.2.0 and
This was a bug in the endpoint dsl. Try upgrading to camel 3.3.0. I think it was fixed in the new release.
https://issues.apache.org/jira/browse/CAMEL-14859

Camel-Cdi not injecting CamelContext with Registery

I am using camel-cdi and it is injecting the CamelContext, detecting all the routes in project.
But I want a CamelContext with a registry because I have some components that I use in camel routes like shown below.
SimpleRegistry registry = new SimpleRegistry();
registry.put("actionProcessor", actionProcessor);
registry.put("jpa", jpaComponent);
registry.put("jtaTransactionManager", platformTransactionManager);
CamelContext camelContext = new DefaultCamelContext(registry);
When I inject CamelContext the components like actionProcess, jpa are not recognized. when in my Route I have
.to("bean:actionProcessor?method=myMethod(${body})")
but my bean does not get executed.
I documentation I read use # before components name which are in registry but still it is not working.
Please suggest how can I achieve this using camel-cdi.
Did you already try with creating a CdiCamelContext (a subclass of DefaultCamelContext) ?
Otherwise, a more elegant would be to annotate your various classes, eg:
#Named("actionProcessor")
public class MyActionProcessor{
...
}
We have been using this for years without any problem
public class ContextFactory {
#Produces
#ApplicationScoped
#ContextName("Demo")
static final CamelContext createContext() {
CdiCamelContext context = new CdiCamelContext();
context.setStreamCaching(true);
context.disableJMX();
return context;
}
}
#ContextName("Demo")
public class MyRouteBuilder extends RouteBuilder {
from("...")
.to("bean:actionProcessor?method=myMethod")
}
#Named("actionProcessor")
public class MyActionProcessor{
public void myMethod(#Body String body) {}
}
Of course, in order to work, you need to activate the JEE bean discovery (=add a "beans.xml" file in META-INF or WEB-INF) !

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

Is there a way to override a processor during unit testing?

I'm trying to write a unit test for one of my camel routes. In the route there is a processor that I would like to replace with a stub. Is there a way I can do this? I'm thinking of using the intercept feature but I can't seem to nail down the best way.
Example:
from(start)
.process(myprocessor)
.to(end)
Thanks in advance.
Yes you can do that by using Camel Advicedwith weaveById functionality which is used to replace the node during testing.
you have to set the id for your processor in the route and using that id you can weave whatever you want. Below is the example,
#Before
protected void weaveMockPoints() throws Exception{
context.getRouteDefinition("Route_ID").adviceWith(context,new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveById("myprocessorId").replace().to(someEndpoint);
}
});
context().start();
}
Only thing is, you have to apply this to the route which is not started already. Better do the change whatever you want and then start your camelcontext as the above example.
IMHO, you need implementation of Detour EIP (http://camel.apache.org/detour.html).
from(start)
.when().method("controlBean", "isDetour")
.to("mock:detour")
.endChoice()
.otherwise()
.process(myprocessor)
.end()
.to(end)
First you need extends CamelTestSupport:
class MyTest extends CamelTestSupport {}
after in your test method:
context.getRouteDefinitions().get(0).adviceWith (context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveById("myprocessorId")
.replace().to("mock:myprocessor");
}
}
And in your route:
from(start)
.process(myprocessor).id("myprocessorId")
.to(end)
regards

Akka/Camel UntypedConsumerActor not consuming from file-based queue

I'm trying to put together my first Akka/Camel application from "scratch" (read, "noob") using the following lib versions:
akka-camel: 2.2.0-RC1
According to all of the documentation I can find (Akka docs, user groups, etc.) all I have to do to consume from a file-based queue is set up my system this way:
Main class:
actorSystem = ActorSystem.create("my-system");
Props props = new Props(Supervisor.class);
ActorRef supervisor = actorSystem.actorOf(props, "supervisor");
Camel camel = CamelExtension.get(actorSystem);
CamelContext camelContext = camel.context();
camelContext.start();
Supervisor class:
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.camel.javaapi.UntypedConsumerActor;
import org.apache.camel.Message;
/**
* Manages creation and supervision of UploadBatchWorkers.
*/
public class Supervisor extends UntypedConsumerActor {
#Override
public String getEndpointUri() {
return "file:///Users/myhome/queue";
}
#Override
public void preStart() {
String test = "test";
}
#Override
public void onReceive(Object message) {
if (message instanceof CamelMessage) {
// do something
}
}
My problem is that even though I know the supervisor object is being created and breaks during debugging on the preStart() method's "test" line (not to mention that if I explicitly "tell" it something it processes fine), it does not consume from the defined endpoint, even though I have another application producing messages to the same endpoint.
Any idea what I'm doing wrong?
Ok, the problem was my own fault and is clearly visible in the example code if you look at the Consumer trait from which the UntypedConsumerActor inherits.
This method:
#Override
public void preStart() {
String test = "test";
}
overrides its parent's preStart() method, right? Well, that parent method is actually the one that registers the consumer with the on-the-fly created endpoint, so while you can override it, you must call super() or it will not work.
Hope this is useful to someone down the road!
Try changing your instanceof inside of onReceive to this:
if (message instanceof CamelMessage){
//do processing here
}
Where CamelMessage is from package akka.camel. That's what the examples in the akka camel docs are doing.

Resources