I have two programs, made up of three routes.
[
one is a route, from JPA database to bean.
one is a copier, from file system endpoint to file system endpoint
]
[
one is a uploader, from file system endpoint to bean.
]
I want to run the one program based on input from my property file
<context:property-placeholder location="./run.properties"
ignore-resource-not-found="false" />
But all I can find for content-based-routing, is examples where the choose is below the from. eg.
from("direct:start")
.choice()
.when(body().contains("Camel"))
.loadBalance().roundRobin().to("mock:foo").to("mock:bar")
.otherwise()
.to("mock:result");
I want a way to rearrange to something like this:
choice()
.when(body().contains("Camel"))
from("direct:start1").loadBalance().roundRobin().to("mock:foo").to("mock:bar")
.otherwise()
from("direct:start2").to("mock:result");
you don't need content based routing to control whether routes are started...
just use the autoStartup(boolean) API to control this...
for example...
from("activemq:queue:special").autoStartup("{{startupRouteProperty}}").to("file://backup");
see http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
The way I ended up doing it:
static ClassPathXmlApplicationContext context;
static CamelContext camel;
...
context = new ClassPathXmlApplicationContext(
"META-INF/spring/camel-context.xml");
camel = new SpringCamelContext(context);
...
if (property) {
camel.addRoutes(new RouteBuilder() {
public void configure() {
from(..).to(..)
}
});
} else {
camel.addRoutes(new RouteBuilder() {
public void configure() {
from(..).to(..)
}
});
}
camel.start();
Related
I am new to apache camel so I am still struggling to write camel test cases.
I have below route defined
from("direct:routeToTest")
.id(ROUTE_ID)
.to(LOOK_UP_ROUTE)
.choice()
.when(some-condition)
.choice()
.when(condition)
.to(CREATE_ROUTE)
.otherwise().process(exchange -> exchange.getIn().setBody(prepareResponse(""))
.endChoice()
.otherwise()
.log("Some Issue")
.process(exchange -> unknownError(exchange))
.endChoice();
}
while testing I am trying to intercept to defined in my route and set some mock response to it. So after some search I found using adviceWith is the right way to achieve it.
So my test is like below. The outcome of the test is, it is still going to Look_up_route (direct:lookUpRoute, another route defined) for processing with the data passed but the expectation is code should skip this to and set response as "MockResponse"
#SpringBootTest
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#RunWith(CamelSpringBootRunner.class)
#UseAdviceWith
#MockEndpoints
#DisableJmx(false)
public class RouteTest {
#Autowired
private ProducerTemplate producerTemplate;
#Autowired
private CamelContext context;
#Test
public void testResponseToJSON() throws Exception {
SomeObject someObject = getObject();
context.getRouteDefinition(ROUTE_ID).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
interceptSendToEndpoint(LOOK_UP_ROUTE)
.skipSendToOriginalEndpoint()
.transform("MockOutput");
}
);
context.start();
Object object = producerTemplate.requestBody(direct:routeToTest, someObject);
}
}
I'd like to know how do I skip .to(LOOK_UP_ROUTE) and set mockResponse to it.
Your intercept statement looks good at first sight. However, have a look at this answer for an alternative approach.
You have to
Add an id to the LOOK_UP_ROUTE step in your route
Then you can use adviceWith to remove or manipulate the marked step in your tests
Set the message body (and header) to whatever it should be for your test
I am trying to create a Spring Boot application involving Apache Camel.
The Camel routes consists of a transformation of message using FreeMarker Template (for this I am using Camel's Freemarker integration)
from("direct:temporary").routeId("hello").to("freemarker:db:test-spot").end().to("stream:out");
The Freemarker templates are stored in Database and I am trying to load it using a Custom Template Loader (say DBTemplateLoader)
However, I haven't been successful yet! Reasons, even if I register my custom template loader, it resorts back to FreeMarkerComponent to load the templates, and of course it doesn't know my database.
Here is the error I am getting
freemarker.template.TemplateNotFoundException: Template not found for name "db:test-spot".
The name was interpreted by this TemplateLoader: org.apache.camel.component.freemarker.FreemarkerComponent$1#3dbbfc3a.
I have tried most of the resources on web. All these things work nicely isolation, but the Apache Camel + FreeeMarker + Custom Template Loader for Freemarker is just not working.
If anyone can point me to the right direction, then I would be grateful.
You can register custom configuration globally to component, which is then applied to all freemarker endpoints. Or locally to single endpoint using configuration URI param.
Register configuration to component:
FreemarkerComponent freemarkerComponent = getContext().getComponent("freemarker", FreemarkerComponent.class);
freemarkerComponent.setConfiguration(myConfiguration);
Register configuration to single endpoint:
.to("freemarker:dummy?configuration=#freemarkerConfiguration") // freemarkerConfiguration is custom bean in registry
I have created unit test for demonstration:
public class CamelCustomFreemarkerLoader extends CamelTestSupport {
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
Configuration myConfiguration = new Configuration();
StringTemplateLoader dummyLoader = new StringTemplateLoader();
dummyLoader.putTemplate("dummy", "Hello from dummy template");
myConfiguration.setTemplateLoader(dummyLoader);
FreemarkerComponent freemarkerComponent = getContext().getComponent("freemarker", FreemarkerComponent.class);
freemarkerComponent.setConfiguration(myConfiguration);
from("direct:test")
.to("freemarker:dummy")
.to("mock:result");
}
};
}
#Test
public void testCustomTemplateLoader() throws Exception{
MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
template.sendBody("direct:test",null);
mockEndpoint.setExpectedCount(1);
mockEndpoint.assertIsSatisfied();
Assert.assertEquals("Hello from dummy template", mockEndpoint.getExchanges().get(0).getIn().getBody());
}
}
I upgraded to camel 2.16 and one of my route Unit Tests started failing.
Here is my route definition:
public class Route extends RouteBuilder{
#Override
public void configure() throws Exception {
from(start).enrich("second");
from("direct:second")
.log(LoggingLevel.DEBUG, "foo", "Route [direct:second] started.");
}
}
Here is my test:
#RunWith(MockitoJUnitRunner.class)
public class RouteTest extends CamelTestSupport {
private Route builder;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Before
public void config() {
BasicConfigurator.configure();
}
#Override
protected RouteBuilder createRouteBuilder() {
builder = new Route();
return builder;
}
#Override
protected CamelContext createCamelContext() throws Exception {
SimpleRegistry registry = new SimpleRegistry();
return new DefaultCamelContext(registry);
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(new DefaultCamelContext()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
}
The error I'm getting when I run the test is:
org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://second]. Exchange[][Message: test]
Worthy of note is the following line in the camel 2.16 notes:
http://camel.apache.org/camel-2160-release.html
The resourceUri and resourceRef attributes on and has been removed as they now support a dynamic uris computed from an Expression.
Thanks in advance for any help.
Swap the order so the the direct route is started before the enrich.
http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
Or use seda instead of direct in your unit test: http://camel.apache.org/seda
Or use ?block=true in the direct uri to tell Camel to block and wait for a consumer to be started and ready before it sends a message to it: http://camel.apache.org/direct
This is a somewhat old issue, but since i pulled out most of my hair out last night, trying to figure out why it was ok to use to("direct:myEndpoint") but not enrich("direct:myEndpoint"), I'll post the answer anyway - maybe it'll save somebody else from getting bald spots ;-)
It turns out to be a test-issue. In case of Direct endpoints, enrich checks whether there is a running route in the context before passing the Exchange to it, but it does so by looking at the CamelContext held by the Exchange it is currently handling. Since you passed your ProducerTemplate an Exchange what was created with a new DefaultCamelContext(), it has no "direct:second" route available.
Luckily there is a couple of simple solutions. Either create the Exchange using the CamelContext from CamelTestSupport, or use the ProducerTemplate sendBody(...) method instead:
#Test
public void testWithSendBody() {
template.sendBody(new String("test"));
}
#Test
public void testPrimeRouteForSubscriptionId() {
Exchange exchange = ExchangeBuilder.anExchange(context()).build();
exchange.getIn().setBody(new String("test"));
template.send(exchange);
}
The blueprint test keeps throwing exception, No Consumers available.
My scenario was that I have an osgi svc which exposes a method which can be called from any another osgi svc.
So the exposed svc method makes a call to a direct:
#EndpointInject(uri = "direct-vm:toRestCall")
ProducerTemplate toRestCall;
svcMethod(Exchange xch){
exchange.setOut(
toRestCall.send("seda:toDirectCall", xch -> {
try{
xch.getIn().setBody("abc");
}catch (Exception ex){
ex.getMessage();
}
}
}).getIn());
And when I tested the direct that it calls, Blueprint advice with JUnit used to keep throwing the following exception:
org.apache.camel.component.direct.DirectConsumerNotAvailableException:
No consumers available on endpoint: Endpoint. Exchange[Message: {..........
I'm writing an application using camel for deployment (eventually) in a fuse container. The nature of the project requires that I mix and match Java and XML DSL.
I'm having trouble getting the mock framework to work properly with blueprint.
Here's my unit test, based completely on the example here.
public class MockNotWorking extends CamelBlueprintTestSupport {
#Test
public void testAdvisedMockEndpointsWithPattern() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
mockEndpoints("log*");
}
});
getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World");
getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
template.sendBody("direct:start", "Hello World");
// additional test to ensure correct endpoints in registry
assertNotNull(context.hasEndpoint("direct:start"));
assertNotNull(context.hasEndpoint("log:foo"));
assertNotNull(context.hasEndpoint("mock:result"));
// only the log:foo endpoint was mocked
assertNotNull(context.hasEndpoint("mock:log:foo"));
assertNull(context.hasEndpoint("mock:direct:start"));
assertNull(context.hasEndpoint("mock:direct:foo"));
assertMockEndpointsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").to("direct:foo").to("log:foo").to("mock:result");
from("direct:foo").transform(constant("Bye World"));
}
};
}
protected String getBlueprintDescriptor() {
return "OSGI-INF/blueprint/blueprint.xml";
}
}
I have copied verbatim the example here, and modified it very slightly so we extend CamelBlueprintTestSupport instead of CamelTestSupport. This requires over-riding getBlueprintDescriptor to point to my blueprint xml, in which I have defined one very basic (and completely irrelevant to the test) route:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<camelContext id="validationRoute" xmlns="http://camel.apache.org/schema/blueprint" >
<route id="validation">
<from uri="direct:validation" />
<log message="validating..." />
</route>
</camelContext>
</blueprint>
The test fails with:
java.lang.AssertionError: mock://log:foo Received message count. Expected: <1> but was: <0>
So this means the message did not reach the mock endpoint. Change CamelBlueprintTestSupport for CamelTestSupport and it works.
So how do I get mocks like this working correctly with blueprint?
When you use blueprint, it imports all the routes you have defined in the blueprint xml file(s), and adds them to the CamelContext.
The reason this breaks things is that the context.getRouteDefinitions().get(0) no longer refers to the only route - there are now more than one. So when you add the AdviceWithRouteBuilder, it could be added to the wrong route.
The following code fixes the problem (and will work for non-blueprint tests too):
List<RouteDefinition> routes = context.getRouteDefinitions();
for (int i=0; i<routes.size(); i++) {
context.getRouteDefinitions().get(i).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock all endpoints
mockEndpoints("log*");
}
});
}
======update======
A better way of doing this is rather than mocking all routes, find our specific route by id and then apply the advice. This means first setting the route id:
from("direct:start").routeId("start").to("direct:foo").to("log:foo").to("mock:result");
And then looking up the route by id and calling adviceWith as before:
context.getRouteDefinition("start").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock log endpoints
mockEndpoints("log*");
}
});
Actually it depends on what version of Camel you are using, some methods doesn't work as expected. Its improved a lot better after 2.12 if I remember right. (2.15 is way better)
After all my encounters with Camel unit testing, I ended up documenting all stuffs here:
http://bushorn.com/unit-testing-apache-camel/
http://bushorn.com/camel-unit-testing-using-mock-endpoint/
By the way, instead of this "mockEndpoints("log*");", I would do this:
weaveByToString(".direct:foo.").after().to("mock:log"); (This sounds weird, I know ;) )
or if you can set an endpoint id
weaveById("endpoint-id-of-direct-foo").after().to("mock:log");
or
weaveAddLast().to("mock:log");
Here is Apache Camel route:
ZooKeeperRoutePolicy routePolicy = new ZooKeeperRoutePolicy("zookeeper:localhost:2181/fuse-example/routePolicy", 1);
from("file:camelInpit").routeId("systemARoute")
.routePolicy(routePolicy)
.log(LoggingLevel.ERROR, "Starting route")
[...]
I want to remove routePolicy in my tests since there is no ZooKeeper in test environment, but this is not as easy as it seems
context.getRouteDefinition("systemARoute").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:aaa");
weaveByType(RouteDefinition.class).selectIndex(1).remove();
}
});
weaveById("policy") and setting the id routePolicy(...).id("policy") does not help.
How can I dynamically remove RoutePolicies while testing?
Is it not possible to do something like that?
from("file:camelInpit").routeId("systemARoute")
.choice()
.when(prodEnvironmentExpression)
.routePolicy(routePolicy)
.endChoice()
.end()
.log(LoggingLevel.ERROR, "Starting route")
You can access the original route and set its route policies to null
context.getRouteDefinition("systemARoute").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
getOriginalRoute().setRoutePolicies(null);
}
});
But we should maybe add fluent DSL builders for this to make it stand out?
If you bind it to the context you can easily make a mock of the policy in your tests by using, where myPolicy is a mock or a policy that does nothing.
Even easier if you make an abstract MyCamelTestSupport which overides that and then all your tests which needs to mock it extends MyCamelTestSupport
#Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry jndi = super.createRegistry();
jndi.bind("myPolicy", myPolicy);
return jndi;
}