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 }