001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.impl;
018
019 import java.util.ArrayList;
020 import java.util.Arrays;
021 import java.util.Collections;
022 import java.util.EventObject;
023 import java.util.HashMap;
024 import java.util.List;
025 import java.util.Map;
026 import java.util.concurrent.CopyOnWriteArrayList;
027
028 import org.apache.camel.CamelContext;
029 import org.apache.camel.CamelContextAware;
030 import org.apache.camel.Exchange;
031 import org.apache.camel.LoggingLevel;
032 import org.apache.camel.Processor;
033 import org.apache.camel.RouteNode;
034 import org.apache.camel.management.event.AbstractExchangeEvent;
035 import org.apache.camel.management.event.ExchangeCompletedEvent;
036 import org.apache.camel.management.event.ExchangeCreatedEvent;
037 import org.apache.camel.model.ProcessorDefinition;
038 import org.apache.camel.processor.interceptor.Tracer;
039 import org.apache.camel.spi.Breakpoint;
040 import org.apache.camel.spi.Condition;
041 import org.apache.camel.spi.Debugger;
042 import org.apache.camel.support.EventNotifierSupport;
043 import org.apache.camel.util.ObjectHelper;
044 import org.slf4j.Logger;
045 import org.slf4j.LoggerFactory;
046
047 /**
048 * The default implementation of the {@link Debugger}.
049 *
050 * @version
051 */
052 public class DefaultDebugger implements Debugger, CamelContextAware {
053
054 private static final Logger LOG = LoggerFactory.getLogger(DefaultDebugger.class);
055 private final List<BreakpointConditions> breakpoints = new CopyOnWriteArrayList<BreakpointConditions>();
056 private final int maxConcurrentSingleSteps = 1;
057 private final Map<String, Breakpoint> singleSteps = new HashMap<String, Breakpoint>(maxConcurrentSingleSteps);
058 private CamelContext camelContext;
059
060 /**
061 * Holder class for breakpoint and the associated conditions
062 */
063 private final class BreakpointConditions {
064 private Breakpoint breakpoint;
065 private List<Condition> conditions;
066
067 private BreakpointConditions(Breakpoint breakpoint) {
068 this(breakpoint, null);
069 }
070
071 private BreakpointConditions(Breakpoint breakpoint, List<Condition> conditions) {
072 this.breakpoint = breakpoint;
073 this.conditions = conditions;
074 }
075
076 public Breakpoint getBreakpoint() {
077 return breakpoint;
078 }
079
080 public List<Condition> getConditions() {
081 return conditions;
082 }
083 }
084
085 public DefaultDebugger() {
086 }
087
088 public DefaultDebugger(CamelContext camelContext) {
089 this.camelContext = camelContext;
090 }
091
092 public CamelContext getCamelContext() {
093 return camelContext;
094 }
095
096 public void setCamelContext(CamelContext camelContext) {
097 this.camelContext = camelContext;
098 }
099
100 public void addBreakpoint(Breakpoint breakpoint) {
101 breakpoints.add(new BreakpointConditions(breakpoint));
102 }
103
104 public void addBreakpoint(Breakpoint breakpoint, Condition... conditions) {
105 if (conditions != null && conditions.length > 0) {
106 breakpoints.add(new BreakpointConditions(breakpoint, Arrays.asList(conditions)));
107 } else {
108 breakpoints.add(new BreakpointConditions(breakpoint));
109 }
110 }
111
112 public void addSingleStepBreakpoint(final Breakpoint breakpoint) {
113 breakpoints.add(new BreakpointConditions(breakpoint));
114 }
115
116 public void addSingleStepBreakpoint(final Breakpoint breakpoint, Condition... conditions) {
117 // wrap the breakpoint into single step breakpoint so we can automatic enable/disable the single step mode
118 Breakpoint singlestep = new Breakpoint() {
119 public State getState() {
120 return breakpoint.getState();
121 }
122
123 public void suspend() {
124 breakpoint.suspend();
125 }
126
127 public void activate() {
128 breakpoint.activate();
129 }
130
131 public void beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition) {
132 breakpoint.beforeProcess(exchange, processor, definition);
133 }
134
135 public void afterProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, long timeTaken) {
136 breakpoint.afterProcess(exchange, processor, definition, timeTaken);
137 }
138
139 public void onEvent(Exchange exchange, EventObject event, ProcessorDefinition<?> definition) {
140 if (event instanceof ExchangeCreatedEvent) {
141 exchange.getContext().getDebugger().startSingleStepExchange(exchange.getExchangeId(), this);
142 } else if (event instanceof ExchangeCompletedEvent) {
143 exchange.getContext().getDebugger().stopSingleStepExchange(exchange.getExchangeId());
144 }
145 breakpoint.onEvent(exchange, event, definition);
146 }
147
148 @Override
149 public String toString() {
150 return breakpoint.toString();
151 }
152 };
153
154 addBreakpoint(singlestep, conditions);
155 }
156
157 public void removeBreakpoint(Breakpoint breakpoint) {
158 for (BreakpointConditions condition : breakpoints) {
159 if (condition.getBreakpoint().equals(breakpoint)) {
160 breakpoints.remove(condition);
161 }
162 }
163 }
164
165 public void suspendAllBreakpoints() {
166 for (BreakpointConditions breakpoint : breakpoints) {
167 breakpoint.getBreakpoint().suspend();
168 }
169 }
170
171 public void activateAllBreakpoints() {
172 for (BreakpointConditions breakpoint : breakpoints) {
173 breakpoint.getBreakpoint().activate();
174 }
175 }
176
177 public List<Breakpoint> getBreakpoints() {
178 List<Breakpoint> answer = new ArrayList<Breakpoint>(breakpoints.size());
179 for (BreakpointConditions e : breakpoints) {
180 answer.add(e.getBreakpoint());
181 }
182 return Collections.unmodifiableList(answer);
183 }
184
185 public boolean startSingleStepExchange(String exchangeId, Breakpoint breakpoint) {
186 // can we accept single stepping the given exchange?
187 if (singleSteps.size() >= maxConcurrentSingleSteps) {
188 return false;
189 }
190
191 singleSteps.put(exchangeId, breakpoint);
192 return true;
193 }
194
195 public void stopSingleStepExchange(String exchangeId) {
196 singleSteps.remove(exchangeId);
197 }
198
199 public boolean beforeProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition) {
200 // is the exchange in single step mode?
201 Breakpoint singleStep = singleSteps.get(exchange.getExchangeId());
202 if (singleStep != null) {
203 onBeforeProcess(exchange, processor, definition, singleStep);
204 return true;
205 }
206
207 // does any of the breakpoints apply?
208 boolean match = false;
209 for (BreakpointConditions breakpoint : breakpoints) {
210 // breakpoint must be active
211 if (Breakpoint.State.Active.equals(breakpoint.getBreakpoint().getState())) {
212 if (matchConditions(exchange, processor, definition, breakpoint)) {
213 match = true;
214 onBeforeProcess(exchange, processor, definition, breakpoint.getBreakpoint());
215 }
216 }
217 }
218
219 return match;
220 }
221
222 public boolean afterProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, long timeTaken) {
223 // is the exchange in single step mode?
224 Breakpoint singleStep = singleSteps.get(exchange.getExchangeId());
225 if (singleStep != null) {
226 onAfterProcess(exchange, processor, definition, timeTaken, singleStep);
227 return true;
228 }
229
230 // does any of the breakpoints apply?
231 boolean match = false;
232 for (BreakpointConditions breakpoint : breakpoints) {
233 // breakpoint must be active
234 if (Breakpoint.State.Active.equals(breakpoint.getBreakpoint().getState())) {
235 if (matchConditions(exchange, processor, definition, breakpoint)) {
236 match = true;
237 onAfterProcess(exchange, processor, definition, timeTaken, breakpoint.getBreakpoint());
238 }
239 }
240 }
241
242 return match;
243 }
244
245 public boolean onEvent(Exchange exchange, EventObject event) {
246 // is the exchange in single step mode?
247 Breakpoint singleStep = singleSteps.get(exchange.getExchangeId());
248 if (singleStep != null) {
249 onEvent(exchange, event, singleStep);
250 return true;
251 }
252
253 // does any of the breakpoints apply?
254 boolean match = false;
255 for (BreakpointConditions breakpoint : breakpoints) {
256 // breakpoint must be active
257 if (Breakpoint.State.Active.equals(breakpoint.getBreakpoint().getState())) {
258 if (matchConditions(exchange, event, breakpoint)) {
259 match = true;
260 onEvent(exchange, event, breakpoint.getBreakpoint());
261 }
262 }
263 }
264
265 return match;
266 }
267
268 protected void onBeforeProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, Breakpoint breakpoint) {
269 try {
270 breakpoint.beforeProcess(exchange, processor, definition);
271 } catch (Throwable e) {
272 LOG.warn("Exception occurred in breakpoint: " + breakpoint + ". This exception will be ignored.", e);
273 }
274 }
275
276 protected void onAfterProcess(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, long timeTaken, Breakpoint breakpoint) {
277 try {
278 breakpoint.afterProcess(exchange, processor, definition, timeTaken);
279 } catch (Throwable e) {
280 LOG.warn("Exception occurred in breakpoint: " + breakpoint + ". This exception will be ignored.", e);
281 }
282 }
283
284 protected void onEvent(Exchange exchange, EventObject event, Breakpoint breakpoint) {
285 ProcessorDefinition<?> definition = null;
286
287 // try to get the last known definition
288 if (exchange.getUnitOfWork() != null && exchange.getUnitOfWork().getTracedRouteNodes() != null) {
289 RouteNode node = exchange.getUnitOfWork().getTracedRouteNodes().getLastNode();
290 if (node != null) {
291 definition = node.getProcessorDefinition();
292 }
293 }
294
295 try {
296 breakpoint.onEvent(exchange, event, definition);
297 } catch (Throwable e) {
298 LOG.warn("Exception occurred in breakpoint: " + breakpoint + ". This exception will be ignored.", e);
299 }
300 }
301
302 private boolean matchConditions(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, BreakpointConditions breakpoint) {
303 if (breakpoint.getConditions() != null && !breakpoint.getConditions().isEmpty()) {
304 for (Condition condition : breakpoint.getConditions()) {
305 if (!condition.matchProcess(exchange, processor, definition)) {
306 return false;
307 }
308 }
309 }
310
311 return true;
312 }
313
314 private boolean matchConditions(Exchange exchange, EventObject event, BreakpointConditions breakpoint) {
315 if (breakpoint.getConditions() != null && !breakpoint.getConditions().isEmpty()) {
316 for (Condition condition : breakpoint.getConditions()) {
317 if (!condition.matchEvent(exchange, event)) {
318 return false;
319 }
320 }
321 }
322
323 return true;
324 }
325
326 public void start() throws Exception {
327 ObjectHelper.notNull(camelContext, "CamelContext", this);
328 // register our event notifier
329 camelContext.getManagementStrategy().addEventNotifier(new DebugEventNotifier());
330 Tracer tracer = Tracer.getTracer(camelContext);
331 if (tracer == null) {
332 // tracer is disabled so enable it silently so we can leverage it to trace the Exchanges for us
333 tracer = Tracer.createTracer(camelContext);
334 tracer.setLogLevel(LoggingLevel.OFF);
335 camelContext.addService(tracer);
336 camelContext.addInterceptStrategy(tracer);
337 }
338 // make sure tracer is enabled so the debugger can leverage the tracer for debugging purposes
339 tracer.setEnabled(true);
340 }
341
342 public void stop() throws Exception {
343 breakpoints.clear();
344 singleSteps.clear();
345 }
346
347 @Override
348 public String toString() {
349 return "DefaultDebugger";
350 }
351
352 private final class DebugEventNotifier extends EventNotifierSupport {
353
354 private DebugEventNotifier() {
355 setIgnoreCamelContextEvents(true);
356 setIgnoreServiceEvents(true);
357 }
358
359 public void notify(EventObject event) throws Exception {
360 AbstractExchangeEvent aee = (AbstractExchangeEvent) event;
361 Exchange exchange = aee.getExchange();
362 onEvent(exchange, event);
363
364 if (event instanceof ExchangeCompletedEvent) {
365 // fail safe to ensure we remove single steps when the Exchange is complete
366 singleSteps.remove(exchange.getExchangeId());
367 }
368 }
369
370 public boolean isEnabled(EventObject event) {
371 return event instanceof AbstractExchangeEvent;
372 }
373
374 protected void doStart() throws Exception {
375 // noop
376 }
377
378 protected void doStop() throws Exception {
379 // noop
380 }
381 }
382
383 }