Author: davsclaus Date: Thu Jul 8 12:23:02 2010 New Revision: 961732 URL: http://svn.apache.org/viewvc?rev=961732&view=rev Log: CAMEL-2919: Added single step feature to Debugger.
Added: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepConditionTest.java (with props) camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepTest.java (with props) Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultDebugger.java camel/trunk/camel-core/src/main/java/org/apache/camel/spi/Debugger.java Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultDebugger.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultDebugger.java?rev=961732&r1=961731&r2=961732&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultDebugger.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/impl/DefaultDebugger.java Thu Jul 8 12:23:02 2010 @@ -20,7 +20,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EventObject; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; @@ -30,6 +32,8 @@ import org.apache.camel.Processor; import org.apache.camel.RouteNode; import org.apache.camel.management.EventNotifierSupport; import org.apache.camel.management.event.AbstractExchangeEvent; +import org.apache.camel.management.event.ExchangeCompletedEvent; +import org.apache.camel.management.event.ExchangeCreatedEvent; import org.apache.camel.model.ProcessorDefinition; import org.apache.camel.processor.interceptor.Tracer; import org.apache.camel.spi.Breakpoint; @@ -48,6 +52,8 @@ public class DefaultDebugger implements private static final Log LOG = LogFactory.getLog(DefaultDebugger.class); private final List<BreakpointConditions> breakpoints = new ArrayList<BreakpointConditions>(); + // TODO: Should we support multiple single steps? + private final Map<String, Breakpoint> singleSteps = new HashMap<String, Breakpoint>(); private CamelContext camelContext; /** @@ -95,7 +101,56 @@ public class DefaultDebugger implements } public void addBreakpoint(Breakpoint breakpoint, Condition... conditions) { - breakpoints.add(new BreakpointConditions(breakpoint, Arrays.asList(conditions))); + if (conditions != null) { + breakpoints.add(new BreakpointConditions(breakpoint, Arrays.asList(conditions))); + } else { + breakpoints.add(new BreakpointConditions(breakpoint)); + } + } + + public void addSingleStepBreakpoint(final Breakpoint breakpoint) { + addSingleStepBreakpoint(breakpoint, null); + } + + public void addSingleStepBreakpoint(final Breakpoint breakpoint, Condition... conditions) { + // wrap the breakpoint into single step breakpoint so we can automatic enable/disable the single step mode + Breakpoint singlestep = new Breakpoint() { + public State getState() { + return breakpoint.getState(); + } + + public void suspend() { + breakpoint.suspend(); + } + + public void activate() { + breakpoint.activate(); + } + + public void beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { + breakpoint.beforeProcess(exchange, processor, definition); + } + + public void afterProcess(Exchange exchange, Processor processor, ProcessorDefinition definition, long timeTaken) { + breakpoint.afterProcess(exchange, processor, definition, timeTaken); + } + + public void onEvent(Exchange exchange, EventObject event, ProcessorDefinition definition) { + if (event instanceof ExchangeCreatedEvent) { + exchange.getContext().getDebugger().startSingleStepExchange(exchange.getExchangeId(), this); + } else if (event instanceof ExchangeCompletedEvent) { + exchange.getContext().getDebugger().stopSingleStepExchange(exchange.getExchangeId()); + } + breakpoint.onEvent(exchange, event, definition); + } + + @Override + public String toString() { + return breakpoint.toString(); + } + }; + + addBreakpoint(singlestep, conditions); } public void removeBreakpoint(Breakpoint breakpoint) { @@ -122,10 +177,24 @@ public class DefaultDebugger implements return Collections.unmodifiableList(answer); } + public void startSingleStepExchange(String exchangeId, Breakpoint breakpoint) { + singleSteps.put(exchangeId, breakpoint); + } + + public void stopSingleStepExchange(String exchangeId) { + singleSteps.remove(exchangeId); + } + public boolean beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { - boolean match = false; + // is the exchange in single step mode? + Breakpoint singleStep = singleSteps.get(exchange.getExchangeId()); + if (singleStep != null) { + onBeforeProcess(exchange, processor, definition, singleStep); + return true; + } // does any of the breakpoints apply? + boolean match = false; for (BreakpointConditions breakpoint : breakpoints) { // breakpoint must be active if (Breakpoint.State.Active.equals(breakpoint.getBreakpoint().getState())) { @@ -140,9 +209,15 @@ public class DefaultDebugger implements } public boolean afterProcess(Exchange exchange, Processor processor, ProcessorDefinition definition, long timeTaken) { - boolean match = false; + // is the exchange in single step mode? + Breakpoint singleStep = singleSteps.get(exchange.getExchangeId()); + if (singleStep != null) { + onAfterProcess(exchange, processor, definition, timeTaken, singleStep); + return true; + } // does any of the breakpoints apply? + boolean match = false; for (BreakpointConditions breakpoint : breakpoints) { // breakpoint must be active if (Breakpoint.State.Active.equals(breakpoint.getBreakpoint().getState())) { @@ -157,9 +232,15 @@ public class DefaultDebugger implements } public boolean onEvent(Exchange exchange, EventObject event) { - boolean match = false; + // is the exchange in single step mode? + Breakpoint singleStep = singleSteps.get(exchange.getExchangeId()); + if (singleStep != null) { + onEvent(exchange, event, singleStep); + return true; + } // does any of the breakpoints apply? + boolean match = false; for (BreakpointConditions breakpoint : breakpoints) { // breakpoint must be active if (Breakpoint.State.Active.equals(breakpoint.getBreakpoint().getState())) { @@ -247,6 +328,7 @@ public class DefaultDebugger implements public void stop() throws Exception { breakpoints.clear(); + singleSteps.clear(); } @Override @@ -265,6 +347,11 @@ public class DefaultDebugger implements AbstractExchangeEvent aee = (AbstractExchangeEvent) event; Exchange exchange = aee.getExchange(); onEvent(exchange, event); + + if (event instanceof ExchangeCompletedEvent) { + // failsafe to ensure we remote single steps when the Exchange is complete + singleSteps.remove(exchange.getExchangeId()); + } } public boolean isEnabled(EventObject event) { Modified: camel/trunk/camel-core/src/main/java/org/apache/camel/spi/Debugger.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/main/java/org/apache/camel/spi/Debugger.java?rev=961732&r1=961731&r2=961732&view=diff ============================================================================== --- camel/trunk/camel-core/src/main/java/org/apache/camel/spi/Debugger.java (original) +++ camel/trunk/camel-core/src/main/java/org/apache/camel/spi/Debugger.java Thu Jul 8 12:23:02 2010 @@ -49,6 +49,25 @@ public interface Debugger extends Servic void addBreakpoint(Breakpoint breakpoint, Condition... conditions); /** + * Add the given breakpoint which will be used in single step mode + * <p/> + * The debugger will single step the first message arriving. + * + * @param breakpoint the breakpoint + */ + void addSingleStepBreakpoint(Breakpoint breakpoint); + + /** + * Add the given breakpoint which will be used in single step mode + * <p/> + * The debugger will single step the first message arriving. + * + * @param breakpoint the breakpoint + * @param conditions a number of {...@link org.apache.camel.spi.Condition}s + */ + void addSingleStepBreakpoint(Breakpoint breakpoint, Condition... conditions); + + /** * Removes the given breakpoint * * @param breakpoint the breakpoint @@ -73,6 +92,23 @@ public interface Debugger extends Servic List<Breakpoint> getBreakpoints(); /** + * Starts the single step debug mode for the given exchange + * + * @param exchangeId the exchange id + * @param breakpoint the breakpoint + */ + void startSingleStepExchange(String exchangeId, Breakpoint breakpoint); + + /** + * Stops the single step debug mode for the given exchange. + * <p/> + * <b>Notice:</b> The default implementation of the debugger is capable of auto stopping when the exchange is complete. + * + * @param exchangeId the exchange id + */ + void stopSingleStepExchange(String exchangeId); + + /** * Callback invoked when an {...@link Exchange} is about to be processed which allows implementators * to notify breakpoints. * Added: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepConditionTest.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepConditionTest.java?rev=961732&view=auto ============================================================================== --- camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepConditionTest.java (added) +++ camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepConditionTest.java Thu Jul 8 12:23:02 2010 @@ -0,0 +1,91 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.processor.interceptor; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.BreakpointSupport; +import org.apache.camel.impl.ConditionSupport; +import org.apache.camel.impl.DefaultDebugger; +import org.apache.camel.model.ProcessorDefinition; +import org.apache.camel.spi.Breakpoint; +import org.apache.camel.spi.Condition; + +/** + * @version $Revision$ + */ +public class DebugSingleStepConditionTest extends ContextTestSupport { + + private List<String> logs = new ArrayList<String>(); + private Breakpoint breakpoint; + private Condition beerCondition; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + breakpoint = new BreakpointSupport() { + public void beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { + String body = exchange.getIn().getBody(String.class); + logs.add("Single stepping at " + definition.getLabel() + " with body: " + body); + } + }; + + beerCondition = new ConditionSupport() { + public boolean matchProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { + return "beer".equals(exchange.getFromRouteId()); + } + }; + } + + 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)); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + 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"); + } + }; + } + +} \ No newline at end of file Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepConditionTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepConditionTest.java ------------------------------------------------------------------------------ svn:keywords = Rev Date Added: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepTest.java URL: http://svn.apache.org/viewvc/camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepTest.java?rev=961732&view=auto ============================================================================== --- camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepTest.java (added) +++ camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepTest.java Thu Jul 8 12:23:02 2010 @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.processor.interceptor; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.BreakpointSupport; +import org.apache.camel.impl.DefaultDebugger; +import org.apache.camel.model.ProcessorDefinition; +import org.apache.camel.spi.Breakpoint; + +/** + * @version $Revision$ + */ +public class DebugSingleStepTest extends ContextTestSupport { + + private List<String> logs = new ArrayList<String>(); + private Breakpoint breakpoint; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + breakpoint = new BreakpointSupport() { + public void beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition definition) { + String body = exchange.getIn().getBody(String.class); + logs.add("Single stepping at " + definition.getLabel() + " with body: " + body); + } + }; + } + + public void testDebug() throws Exception { + context.getDebugger().addSingleStepBreakpoint(breakpoint); + + getMockEndpoint("mock:result").expectedBodiesReceived("Hello World", "Hello Camel"); + + template.sendBody("direct:start", "Hello World"); + template.sendBody("direct:start", "Hello Camel"); + + assertMockEndpointsSatisfied(); + + assertEquals(6, logs.size()); + assertEquals("Single stepping at log:foo with body: Hello World", logs.get(0)); + assertEquals("Single stepping at log:bar with body: Hello World", logs.get(1)); + assertEquals("Single stepping at mock:result with body: Hello World", logs.get(2)); + assertEquals("Single stepping at log:foo with body: Hello Camel", logs.get(3)); + assertEquals("Single stepping at log:bar with body: Hello Camel", logs.get(4)); + assertEquals("Single stepping at mock:result with body: Hello Camel", logs.get(5)); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + // use debugger + context.setDebugger(new DefaultDebugger()); + + from("direct:start").to("log:foo").to("log:bar").to("mock:result"); + } + }; + } + +} \ No newline at end of file Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: camel/trunk/camel-core/src/test/java/org/apache/camel/processor/interceptor/DebugSingleStepTest.java ------------------------------------------------------------------------------ svn:keywords = Rev Date