Camel 2.x - Debugger API
Available as of Camel 2.4
There is a new org.apache.camel.spi.Debugger
API which allows 3rd party to attach a debugger tooling to debug Exchanges in Camel routes.
There is a default implementation in camel-core as the org.apache.camel.impl.DefaultDebugger
.
Enabling
You can enable the debugger from the CamelContext
using the setDebugger
. We may add a nicer API for this in the future.
Breakpoint
There is a org.apache.camel.spi.Breakpoint
API in which the 3rd party tooling implement logic what should happen when the breakpoint is hit.
The breakpoint can be invoked in 2 kind of styles in Camel:
- processing based
- event based
The processing is based on a before
and after
callback when the Exchange is being routed and thus a Processor is invoked.
The event is based on the EventNotifier
emitting events such as ExchangeCreatedEvent
, ExchangeFailureEvent
etc. This allows you to have breakpoints when a given Exchange has failed. Or when they are done, so you can inspect the result etc.
There is a org.apache.camel.impl.BreakpointSupport
class which can be used to extend, to avoid implementing all the methods from the interface.
Condition
There is a org.apache.camel.spi.Condition
API in which the 3rd party tooling can provide conditions when the breakpoints should apply. For example a conditional breakpoint which only triggers if the message body is a certain message / type etc.
The condition can be invoked in 2 kind of styles in Camel:
- processing based
- event based
Where there is a match
method for each style.
There is a org.apache.camel.impl.ConditionSupport
class which can be used to extend, to avoid implementing all the methods from the interface.
Example
For example we can have this breakpoint
breakpoint = new BreakpointSupport() { public void beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { String body = exchange.getIn().getBody(String.class); logs.add("Breakpoint at " + definition + " with body: " + body); } }
In which we want to trigger when the message contains Camel. So we can create this Condition
:
camelCondition = new ConditionSupport() { public boolean matchProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { return body().contains("Camel").matches(exchange); } };
And to use this we just tell the Debugger
as follows:
public void testDebug() throws Exception { context.getDebugger().addBreakpoint(breakpoint, camelCondition); getMockEndpoint("mock:result").expectedBodiesReceived("Hello World", "Hello Camel"); template.sendBody("direct:start", "Hello World"); template.sendBody("direct:start", "Hello Camel"); assertMockEndpointsSatisfied(); assertEquals(2, logs.size()); assertEquals("Breakpoint at To[log:foo] with body: Hello Camel", logs.get(0)); assertEquals("Breakpoint at To[mock:result] with body: Hello Camel", logs.get(1)); }
Which then will only invoke the breakpoint callback for the "Hello Camel"
message.
Single step
In the Java editor the debugger is capable of single stepping when a breakpoint is hit. The idea in Camel is that you want to single step an Exchange so you can step through how its routed and follow it from the 3rd party tooling.
Only one single step active
The Debugger only allows one active breakpoint during single step, which means that if you try to activate a 2nd single step, then it will disregarded. In this case the addSingleStepBreakpoint
method will return false
to indicate this.
The Debugger
has API for single stepping. For example to single step the first message arrived in Camel you can do
context.getDebugger().addSingleStepBreakpoint(breakpoint);
However what if you have multiple routes or the likes and only want to single step from route X, well you can just add a Condition
:
This beerCondition
will only match Exchange coming from the beer
route:
beerCondition = new ConditionSupport() { public boolean matchProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { return "beer".equals(exchange.getFromRouteId()); } };
So you just provide the condition when adding the breakpoint:
// we only want to single step the beer route context.getDebugger().addSingleStepBreakpoint(breakpoint, beerCondition);
And the routes could be as follows:
public void configure() throws Exception { // use debugger context.setDebugger(new DefaultDebugger()); from("direct:start").routeId("foo").to("log:foo").to("log:bar").to("mock:result"); from("direct:beer").routeId("beer").to("log:beer").to("mock:result"); }
Which will cause the debugger to only single step when a message arrives on beer
route:
public void testDebug() throws Exception { // we only want to single step the beer route context.getDebugger().addSingleStepBreakpoint(breakpoint, beerCondition); getMockEndpoint("mock:result").expectedBodiesReceived("Hello World", "Carlsberg"); template.sendBody("direct:start", "Hello World"); template.sendBody("direct:beer", "Carlsberg"); assertMockEndpointsSatisfied(); assertEquals(2, logs.size()); assertEquals("Single stepping at log:beer with body: Carlsberg", logs.get(0)); assertEquals("Single stepping at mock:result with body: Carlsberg", logs.get(1)); }