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 */ 017package org.apache.camel.management; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027import java.util.concurrent.ThreadPoolExecutor; 028import javax.management.JMException; 029import javax.management.MalformedObjectNameException; 030import javax.management.ObjectName; 031 032import org.apache.camel.CamelContext; 033import org.apache.camel.CamelContextAware; 034import org.apache.camel.Channel; 035import org.apache.camel.Component; 036import org.apache.camel.Consumer; 037import org.apache.camel.Endpoint; 038import org.apache.camel.ErrorHandlerFactory; 039import org.apache.camel.ManagementStatisticsLevel; 040import org.apache.camel.Processor; 041import org.apache.camel.Producer; 042import org.apache.camel.Route; 043import org.apache.camel.Service; 044import org.apache.camel.StartupListener; 045import org.apache.camel.TimerListener; 046import org.apache.camel.VetoCamelContextStartException; 047import org.apache.camel.api.management.PerformanceCounter; 048import org.apache.camel.impl.ConsumerCache; 049import org.apache.camel.impl.DefaultCamelContext; 050import org.apache.camel.impl.DefaultEndpointRegistry; 051import org.apache.camel.impl.EventDrivenConsumerRoute; 052import org.apache.camel.impl.ProducerCache; 053import org.apache.camel.impl.ThrottlingInflightRoutePolicy; 054import org.apache.camel.management.mbean.ManagedAsyncProcessorAwaitManager; 055import org.apache.camel.management.mbean.ManagedBacklogDebugger; 056import org.apache.camel.management.mbean.ManagedBacklogTracer; 057import org.apache.camel.management.mbean.ManagedCamelContext; 058import org.apache.camel.management.mbean.ManagedConsumerCache; 059import org.apache.camel.management.mbean.ManagedEndpoint; 060import org.apache.camel.management.mbean.ManagedEndpointRegistry; 061import org.apache.camel.management.mbean.ManagedInflightRepository; 062import org.apache.camel.management.mbean.ManagedProducerCache; 063import org.apache.camel.management.mbean.ManagedRestRegistry; 064import org.apache.camel.management.mbean.ManagedRoute; 065import org.apache.camel.management.mbean.ManagedRuntimeEndpointRegistry; 066import org.apache.camel.management.mbean.ManagedService; 067import org.apache.camel.management.mbean.ManagedStreamCachingStrategy; 068import org.apache.camel.management.mbean.ManagedThrottlingInflightRoutePolicy; 069import org.apache.camel.management.mbean.ManagedTracer; 070import org.apache.camel.management.mbean.ManagedTypeConverterRegistry; 071import org.apache.camel.model.AOPDefinition; 072import org.apache.camel.model.InterceptDefinition; 073import org.apache.camel.model.OnCompletionDefinition; 074import org.apache.camel.model.OnExceptionDefinition; 075import org.apache.camel.model.PolicyDefinition; 076import org.apache.camel.model.ProcessorDefinition; 077import org.apache.camel.model.ProcessorDefinitionHelper; 078import org.apache.camel.model.RouteDefinition; 079import org.apache.camel.processor.CamelInternalProcessor; 080import org.apache.camel.processor.interceptor.BacklogDebugger; 081import org.apache.camel.processor.interceptor.BacklogTracer; 082import org.apache.camel.processor.interceptor.Tracer; 083import org.apache.camel.spi.AsyncProcessorAwaitManager; 084import org.apache.camel.spi.DataFormat; 085import org.apache.camel.spi.EventNotifier; 086import org.apache.camel.spi.InflightRepository; 087import org.apache.camel.spi.LifecycleStrategy; 088import org.apache.camel.spi.ManagementAgent; 089import org.apache.camel.spi.ManagementAware; 090import org.apache.camel.spi.ManagementNameStrategy; 091import org.apache.camel.spi.ManagementObjectStrategy; 092import org.apache.camel.spi.ManagementStrategy; 093import org.apache.camel.spi.RestRegistry; 094import org.apache.camel.spi.RouteContext; 095import org.apache.camel.spi.RuntimeEndpointRegistry; 096import org.apache.camel.spi.StreamCachingStrategy; 097import org.apache.camel.spi.TypeConverterRegistry; 098import org.apache.camel.spi.UnitOfWork; 099import org.apache.camel.support.ServiceSupport; 100import org.apache.camel.support.TimerListenerManager; 101import org.apache.camel.util.KeyValueHolder; 102import org.apache.camel.util.ObjectHelper; 103import org.slf4j.Logger; 104import org.slf4j.LoggerFactory; 105 106/** 107 * Default JMX managed lifecycle strategy that registered objects using the configured 108 * {@link org.apache.camel.spi.ManagementStrategy}. 109 * 110 * @see org.apache.camel.spi.ManagementStrategy 111 * @version 112 */ 113@SuppressWarnings("deprecation") 114public class DefaultManagementLifecycleStrategy extends ServiceSupport implements LifecycleStrategy, CamelContextAware { 115 116 private static final Logger LOG = LoggerFactory.getLogger(DefaultManagementLifecycleStrategy.class); 117 // the wrapped processors is for performance counters, which are in use for the created routes 118 // when a route is removed, we should remove the associated processors from this map 119 private final Map<Processor, KeyValueHolder<ProcessorDefinition<?>, InstrumentationProcessor>> wrappedProcessors = 120 new HashMap<Processor, KeyValueHolder<ProcessorDefinition<?>, InstrumentationProcessor>>(); 121 private final List<PreRegisterService> preServices = new ArrayList<PreRegisterService>(); 122 private final TimerListenerManager loadTimer = new ManagedLoadTimer(); 123 private final TimerListenerManagerStartupListener loadTimerStartupListener = new TimerListenerManagerStartupListener(); 124 private volatile CamelContext camelContext; 125 private volatile ManagedCamelContext camelContextMBean; 126 private volatile boolean initialized; 127 private final Set<String> knowRouteIds = new HashSet<String>(); 128 private final Map<Tracer, ManagedTracer> managedTracers = new HashMap<Tracer, ManagedTracer>(); 129 private final Map<BacklogTracer, ManagedBacklogTracer> managedBacklogTracers = new HashMap<BacklogTracer, ManagedBacklogTracer>(); 130 private final Map<BacklogDebugger, ManagedBacklogDebugger> managedBacklogDebuggers = new HashMap<BacklogDebugger, ManagedBacklogDebugger>(); 131 private final Map<ThreadPoolExecutor, Object> managedThreadPools = new HashMap<ThreadPoolExecutor, Object>(); 132 133 public DefaultManagementLifecycleStrategy() { 134 } 135 136 public DefaultManagementLifecycleStrategy(CamelContext camelContext) { 137 this.camelContext = camelContext; 138 } 139 140 public CamelContext getCamelContext() { 141 return camelContext; 142 } 143 144 public void setCamelContext(CamelContext camelContext) { 145 this.camelContext = camelContext; 146 } 147 148 public void onContextStart(CamelContext context) throws VetoCamelContextStartException { 149 Object mc = getManagementObjectStrategy().getManagedObjectForCamelContext(context); 150 151 String name = context.getName(); 152 String managementName = context.getManagementNameStrategy().getName(); 153 154 try { 155 boolean done = false; 156 while (!done) { 157 ObjectName on = getManagementStrategy().getManagementNamingStrategy().getObjectNameForCamelContext(managementName, name); 158 boolean exists = getManagementStrategy().isManaged(mc, on); 159 if (!exists) { 160 done = true; 161 } else { 162 // okay there exists already a CamelContext with this name, we can try to fix it by finding a free name 163 boolean fixed = false; 164 // if we use the default name strategy we can find a free name to use 165 String newName = findFreeName(mc, context.getManagementNameStrategy(), name); 166 if (newName != null) { 167 // use this as the fixed name 168 fixed = true; 169 done = true; 170 managementName = newName; 171 } 172 // we could not fix it so veto starting camel 173 if (!fixed) { 174 throw new VetoCamelContextStartException("CamelContext (" + context.getName() + ") with ObjectName[" + on + "] is already registered." 175 + " Make sure to use unique names on CamelContext when using multiple CamelContexts in the same MBeanServer.", context); 176 } else { 177 LOG.warn("This CamelContext(" + context.getName() + ") will be registered using the name: " + managementName 178 + " due to clash with an existing name already registered in MBeanServer."); 179 } 180 } 181 } 182 } catch (VetoCamelContextStartException e) { 183 // rethrow veto 184 throw e; 185 } catch (Exception e) { 186 // must rethrow to allow CamelContext fallback to non JMX agent to allow 187 // Camel to continue to run 188 throw ObjectHelper.wrapRuntimeCamelException(e); 189 } 190 191 // set the name we are going to use 192 if (context instanceof DefaultCamelContext) { 193 ((DefaultCamelContext) context).setManagementName(managementName); 194 } 195 196 try { 197 manageObject(mc); 198 } catch (Exception e) { 199 // must rethrow to allow CamelContext fallback to non JMX agent to allow 200 // Camel to continue to run 201 throw ObjectHelper.wrapRuntimeCamelException(e); 202 } 203 204 // yes we made it and are initialized 205 initialized = true; 206 207 if (mc instanceof ManagedCamelContext) { 208 camelContextMBean = (ManagedCamelContext) mc; 209 } 210 211 // register any pre registered now that we are initialized 212 enlistPreRegisteredServices(); 213 } 214 215 private String findFreeName(Object mc, ManagementNameStrategy strategy, String name) throws MalformedObjectNameException { 216 // we cannot find a free name for fixed named strategies 217 if (strategy.isFixedName()) { 218 return null; 219 } 220 221 // okay try to find a free name 222 boolean done = false; 223 String newName = null; 224 while (!done) { 225 // compute the next name 226 newName = strategy.getNextName(); 227 ObjectName on = getManagementStrategy().getManagementNamingStrategy().getObjectNameForCamelContext(newName, name); 228 done = !getManagementStrategy().isManaged(mc, on); 229 if (LOG.isTraceEnabled()) { 230 LOG.trace("Using name: {} in ObjectName[{}] exists? {}", new Object[]{name, on, done}); 231 } 232 } 233 return newName; 234 } 235 236 /** 237 * After {@link CamelContext} has been enlisted in JMX using {@link #onContextStart(org.apache.camel.CamelContext)} 238 * then we can enlist any pre registered services as well, as we had to wait for {@link CamelContext} to be 239 * enlisted first. 240 * <p/> 241 * A component/endpoint/service etc. can be pre registered when using dependency injection and annotations such as 242 * {@link org.apache.camel.Produce}, {@link org.apache.camel.EndpointInject}. Therefore we need to capture those 243 * registrations up front, and then afterwards enlist in JMX when {@link CamelContext} is being started. 244 */ 245 private void enlistPreRegisteredServices() { 246 if (preServices.isEmpty()) { 247 return; 248 } 249 250 LOG.debug("Registering {} pre registered services", preServices.size()); 251 for (PreRegisterService pre : preServices) { 252 if (pre.getComponent() != null) { 253 onComponentAdd(pre.getName(), pre.getComponent()); 254 } else if (pre.getEndpoint() != null) { 255 onEndpointAdd(pre.getEndpoint()); 256 } else if (pre.getService() != null) { 257 onServiceAdd(pre.getCamelContext(), pre.getService(), pre.getRoute()); 258 } 259 } 260 261 // we are done so clear the list 262 preServices.clear(); 263 } 264 265 public void onContextStop(CamelContext context) { 266 // the agent hasn't been started 267 if (!initialized) { 268 return; 269 } 270 try { 271 Object mc = getManagementObjectStrategy().getManagedObjectForCamelContext(context); 272 // the context could have been removed already 273 if (getManagementStrategy().isManaged(mc, null)) { 274 unmanageObject(mc); 275 } 276 } catch (Exception e) { 277 LOG.warn("Could not unregister CamelContext MBean", e); 278 } 279 280 camelContextMBean = null; 281 } 282 283 public void onComponentAdd(String name, Component component) { 284 // always register components as there are only a few of those 285 if (!initialized) { 286 // pre register so we can register later when we have been initialized 287 PreRegisterService pre = new PreRegisterService(); 288 pre.onComponentAdd(name, component); 289 preServices.add(pre); 290 return; 291 } 292 try { 293 Object mc = getManagementObjectStrategy().getManagedObjectForComponent(camelContext, component, name); 294 manageObject(mc); 295 } catch (Exception e) { 296 LOG.warn("Could not register Component MBean", e); 297 } 298 } 299 300 public void onComponentRemove(String name, Component component) { 301 // the agent hasn't been started 302 if (!initialized) { 303 return; 304 } 305 try { 306 Object mc = getManagementObjectStrategy().getManagedObjectForComponent(camelContext, component, name); 307 unmanageObject(mc); 308 } catch (Exception e) { 309 LOG.warn("Could not unregister Component MBean", e); 310 } 311 } 312 313 /** 314 * If the endpoint is an instance of ManagedResource then register it with the 315 * mbean server, if it is not then wrap the endpoint in a {@link ManagedEndpoint} and 316 * register that with the mbean server. 317 * 318 * @param endpoint the Endpoint attempted to be added 319 */ 320 public void onEndpointAdd(Endpoint endpoint) { 321 if (!initialized) { 322 // pre register so we can register later when we have been initialized 323 PreRegisterService pre = new PreRegisterService(); 324 pre.onEndpointAdd(endpoint); 325 preServices.add(pre); 326 return; 327 } 328 329 if (!shouldRegister(endpoint, null)) { 330 // avoid registering if not needed 331 return; 332 } 333 334 try { 335 Object me = getManagementObjectStrategy().getManagedObjectForEndpoint(camelContext, endpoint); 336 if (me == null) { 337 // endpoint should not be managed 338 return; 339 } 340 manageObject(me); 341 } catch (Exception e) { 342 LOG.warn("Could not register Endpoint MBean for endpoint: " + endpoint + ". This exception will be ignored.", e); 343 } 344 } 345 346 public void onEndpointRemove(Endpoint endpoint) { 347 // the agent hasn't been started 348 if (!initialized) { 349 return; 350 } 351 352 try { 353 Object me = getManagementObjectStrategy().getManagedObjectForEndpoint(camelContext, endpoint); 354 unmanageObject(me); 355 } catch (Exception e) { 356 LOG.warn("Could not unregister Endpoint MBean for endpoint: " + endpoint + ". This exception will be ignored.", e); 357 } 358 } 359 360 public void onServiceAdd(CamelContext context, Service service, Route route) { 361 if (!initialized) { 362 // pre register so we can register later when we have been initialized 363 PreRegisterService pre = new PreRegisterService(); 364 pre.onServiceAdd(context, service, route); 365 preServices.add(pre); 366 return; 367 } 368 369 // services can by any kind of misc type but also processors 370 // so we have special logic when its a processor 371 372 if (!shouldRegister(service, route)) { 373 // avoid registering if not needed 374 return; 375 } 376 377 Object managedObject = getManagedObjectForService(context, service, route); 378 if (managedObject == null) { 379 // service should not be managed 380 return; 381 } 382 383 // skip already managed services, for example if a route has been restarted 384 if (getManagementStrategy().isManaged(managedObject, null)) { 385 LOG.trace("The service is already managed: {}", service); 386 return; 387 } 388 389 try { 390 manageObject(managedObject); 391 } catch (Exception e) { 392 LOG.warn("Could not register service: " + service + " as Service MBean.", e); 393 } 394 } 395 396 public void onServiceRemove(CamelContext context, Service service, Route route) { 397 // the agent hasn't been started 398 if (!initialized) { 399 return; 400 } 401 402 Object managedObject = getManagedObjectForService(context, service, route); 403 if (managedObject != null) { 404 try { 405 unmanageObject(managedObject); 406 } catch (Exception e) { 407 LOG.warn("Could not unregister service: " + service + " as Service MBean.", e); 408 } 409 } 410 } 411 412 @SuppressWarnings("unchecked") 413 private Object getManagedObjectForService(CamelContext context, Service service, Route route) { 414 // skip channel, UoW and dont double wrap instrumentation 415 if (service instanceof Channel || service instanceof UnitOfWork || service instanceof InstrumentationProcessor) { 416 return null; 417 } 418 419 Object answer = null; 420 421 if (service instanceof ManagementAware) { 422 return ((ManagementAware<Service>) service).getManagedObject(service); 423 } else if (service instanceof Tracer) { 424 // special for tracer 425 Tracer tracer = (Tracer) service; 426 ManagedTracer mt = managedTracers.get(tracer); 427 if (mt == null) { 428 mt = new ManagedTracer(context, tracer); 429 mt.init(getManagementStrategy()); 430 managedTracers.put(tracer, mt); 431 } 432 return mt; 433 } else if (service instanceof BacklogTracer) { 434 // special for backlog tracer 435 BacklogTracer backlogTracer = (BacklogTracer) service; 436 ManagedBacklogTracer mt = managedBacklogTracers.get(backlogTracer); 437 if (mt == null) { 438 mt = new ManagedBacklogTracer(context, backlogTracer); 439 mt.init(getManagementStrategy()); 440 managedBacklogTracers.put(backlogTracer, mt); 441 } 442 return mt; 443 } else if (service instanceof BacklogDebugger) { 444 // special for backlog debugger 445 BacklogDebugger backlogDebugger = (BacklogDebugger) service; 446 ManagedBacklogDebugger md = managedBacklogDebuggers.get(backlogDebugger); 447 if (md == null) { 448 md = new ManagedBacklogDebugger(context, backlogDebugger); 449 md.init(getManagementStrategy()); 450 managedBacklogDebuggers.put(backlogDebugger, md); 451 } 452 return md; 453 } else if (service instanceof DataFormat) { 454 answer = getManagementObjectStrategy().getManagedObjectForDataFormat(context, (DataFormat) service); 455 } else if (service instanceof Producer) { 456 answer = getManagementObjectStrategy().getManagedObjectForProducer(context, (Producer) service); 457 } else if (service instanceof Consumer) { 458 answer = getManagementObjectStrategy().getManagedObjectForConsumer(context, (Consumer) service); 459 } else if (service instanceof Processor) { 460 // special for processors as we need to do some extra work 461 return getManagedObjectForProcessor(context, (Processor) service, route); 462 } else if (service instanceof ThrottlingInflightRoutePolicy) { 463 answer = new ManagedThrottlingInflightRoutePolicy(context, (ThrottlingInflightRoutePolicy) service); 464 } else if (service instanceof ConsumerCache) { 465 answer = new ManagedConsumerCache(context, (ConsumerCache) service); 466 } else if (service instanceof ProducerCache) { 467 answer = new ManagedProducerCache(context, (ProducerCache) service); 468 } else if (service instanceof DefaultEndpointRegistry) { 469 answer = new ManagedEndpointRegistry(context, (DefaultEndpointRegistry) service); 470 } else if (service instanceof TypeConverterRegistry) { 471 answer = new ManagedTypeConverterRegistry(context, (TypeConverterRegistry) service); 472 } else if (service instanceof RestRegistry) { 473 answer = new ManagedRestRegistry(context, (RestRegistry) service); 474 } else if (service instanceof InflightRepository) { 475 answer = new ManagedInflightRepository(context, (InflightRepository) service); 476 } else if (service instanceof AsyncProcessorAwaitManager) { 477 answer = new ManagedAsyncProcessorAwaitManager(context, (AsyncProcessorAwaitManager) service); 478 } else if (service instanceof RuntimeEndpointRegistry) { 479 answer = new ManagedRuntimeEndpointRegistry(context, (RuntimeEndpointRegistry) service); 480 } else if (service instanceof StreamCachingStrategy) { 481 answer = new ManagedStreamCachingStrategy(context, (StreamCachingStrategy) service); 482 } else if (service instanceof EventNotifier) { 483 answer = getManagementObjectStrategy().getManagedObjectForEventNotifier(context, (EventNotifier) service); 484 } else if (service != null) { 485 // fallback as generic service 486 answer = getManagementObjectStrategy().getManagedObjectForService(context, service); 487 } 488 489 if (answer != null && answer instanceof ManagedService) { 490 ManagedService ms = (ManagedService) answer; 491 ms.setRoute(route); 492 ms.init(getManagementStrategy()); 493 } 494 495 return answer; 496 } 497 498 private Object getManagedObjectForProcessor(CamelContext context, Processor processor, Route route) { 499 // a bit of magic here as the processors we want to manage have already been registered 500 // in the wrapped processors map when Camel have instrumented the route on route initialization 501 // so the idea is now to only manage the processors from the map 502 KeyValueHolder<ProcessorDefinition<?>, InstrumentationProcessor> holder = wrappedProcessors.get(processor); 503 if (holder == null) { 504 // skip as its not an well known processor we want to manage anyway, such as Channel/UnitOfWork/Pipeline etc. 505 return null; 506 } 507 508 // get the managed object as it can be a specialized type such as a Delayer/Throttler etc. 509 Object managedObject = getManagementObjectStrategy().getManagedObjectForProcessor(context, processor, holder.getKey(), route); 510 // only manage if we have a name for it as otherwise we do not want to manage it anyway 511 if (managedObject != null) { 512 // is it a performance counter then we need to set our counter 513 if (managedObject instanceof PerformanceCounter) { 514 InstrumentationProcessor counter = holder.getValue(); 515 if (counter != null) { 516 // change counter to us 517 counter.setCounter(managedObject); 518 } 519 } 520 } 521 522 return managedObject; 523 } 524 525 public void onRoutesAdd(Collection<Route> routes) { 526 for (Route route : routes) { 527 528 // if we are starting CamelContext or either of the two options has been 529 // enabled, then enlist the route as a known route 530 if (getCamelContext().getStatus().isStarting() 531 || getManagementStrategy().getManagementAgent().getRegisterAlways() 532 || getManagementStrategy().getManagementAgent().getRegisterNewRoutes()) { 533 // register as known route id 534 knowRouteIds.add(route.getId()); 535 } 536 537 if (!shouldRegister(route, route)) { 538 // avoid registering if not needed, skip to next route 539 continue; 540 } 541 542 Object mr = getManagementObjectStrategy().getManagedObjectForRoute(camelContext, route); 543 544 // skip already managed routes, for example if the route has been restarted 545 if (getManagementStrategy().isManaged(mr, null)) { 546 LOG.trace("The route is already managed: {}", route); 547 continue; 548 } 549 550 // get the wrapped instrumentation processor from this route 551 // and set me as the counter 552 if (route instanceof EventDrivenConsumerRoute) { 553 EventDrivenConsumerRoute edcr = (EventDrivenConsumerRoute) route; 554 Processor processor = edcr.getProcessor(); 555 if (processor instanceof CamelInternalProcessor && mr instanceof ManagedRoute) { 556 CamelInternalProcessor internal = (CamelInternalProcessor) processor; 557 ManagedRoute routeMBean = (ManagedRoute) mr; 558 559 CamelInternalProcessor.InstrumentationAdvice task = internal.getAdvice(CamelInternalProcessor.InstrumentationAdvice.class); 560 if (task != null) { 561 // we need to wrap the counter with the camel context so we get stats updated on the context as well 562 if (camelContextMBean != null) { 563 CompositePerformanceCounter wrapper = new CompositePerformanceCounter(routeMBean, camelContextMBean); 564 task.setCounter(wrapper); 565 } else { 566 task.setCounter(routeMBean); 567 } 568 } 569 } 570 } 571 572 try { 573 manageObject(mr); 574 } catch (JMException e) { 575 LOG.warn("Could not register Route MBean", e); 576 } catch (Exception e) { 577 LOG.warn("Could not create Route MBean", e); 578 } 579 } 580 } 581 582 public void onRoutesRemove(Collection<Route> routes) { 583 // the agent hasn't been started 584 if (!initialized) { 585 return; 586 } 587 588 for (Route route : routes) { 589 Object mr = getManagementObjectStrategy().getManagedObjectForRoute(camelContext, route); 590 591 // skip unmanaged routes 592 if (!getManagementStrategy().isManaged(mr, null)) { 593 LOG.trace("The route is not managed: {}", route); 594 continue; 595 } 596 597 try { 598 unmanageObject(mr); 599 } catch (Exception e) { 600 LOG.warn("Could not unregister Route MBean", e); 601 } 602 603 // remove from known routes ids, as the route has been removed 604 knowRouteIds.remove(route.getId()); 605 } 606 607 // after the routes has been removed, we should clear the wrapped processors as we no longer need them 608 // as they were just a provisional map used during creation of routes 609 removeWrappedProcessorsForRoutes(routes); 610 } 611 612 public void onErrorHandlerAdd(RouteContext routeContext, Processor errorHandler, ErrorHandlerFactory errorHandlerBuilder) { 613 if (!shouldRegister(errorHandler, null)) { 614 // avoid registering if not needed 615 return; 616 } 617 618 Object me = getManagementObjectStrategy().getManagedObjectForErrorHandler(camelContext, routeContext, errorHandler, errorHandlerBuilder); 619 620 // skip already managed services, for example if a route has been restarted 621 if (getManagementStrategy().isManaged(me, null)) { 622 LOG.trace("The error handler builder is already managed: {}", errorHandlerBuilder); 623 return; 624 } 625 626 try { 627 manageObject(me); 628 } catch (Exception e) { 629 LOG.warn("Could not register error handler builder: " + errorHandlerBuilder + " as ErrorHandler MBean.", e); 630 } 631 } 632 633 public void onErrorHandlerRemove(RouteContext routeContext, Processor errorHandler, ErrorHandlerFactory errorHandlerBuilder) { 634 if (!initialized) { 635 return; 636 } 637 638 Object me = getManagementObjectStrategy().getManagedObjectForErrorHandler(camelContext, routeContext, errorHandler, errorHandlerBuilder); 639 if (me != null) { 640 try { 641 unmanageObject(me); 642 } catch (Exception e) { 643 LOG.warn("Could not unregister error handler: " + me + " as ErrorHandler MBean.", e); 644 } 645 } 646 } 647 648 public void onThreadPoolAdd(CamelContext camelContext, ThreadPoolExecutor threadPool, String id, 649 String sourceId, String routeId, String threadPoolProfileId) { 650 651 if (!shouldRegister(threadPool, null)) { 652 // avoid registering if not needed 653 return; 654 } 655 656 Object mtp = getManagementObjectStrategy().getManagedObjectForThreadPool(camelContext, threadPool, id, sourceId, routeId, threadPoolProfileId); 657 658 // skip already managed services, for example if a route has been restarted 659 if (getManagementStrategy().isManaged(mtp, null)) { 660 LOG.trace("The thread pool is already managed: {}", threadPool); 661 return; 662 } 663 664 try { 665 manageObject(mtp); 666 // store a reference so we can unmanage from JMX when the thread pool is removed 667 // we need to keep track here, as we cannot re-construct the thread pool ObjectName when removing the thread pool 668 managedThreadPools.put(threadPool, mtp); 669 } catch (Exception e) { 670 LOG.warn("Could not register thread pool: " + threadPool + " as ThreadPool MBean.", e); 671 } 672 } 673 674 public void onThreadPoolRemove(CamelContext camelContext, ThreadPoolExecutor threadPool) { 675 if (!initialized) { 676 return; 677 } 678 679 // lookup the thread pool and remove it from JMX 680 Object mtp = managedThreadPools.remove(threadPool); 681 if (mtp != null) { 682 // skip unmanaged routes 683 if (!getManagementStrategy().isManaged(mtp, null)) { 684 LOG.trace("The thread pool is not managed: {}", threadPool); 685 return; 686 } 687 688 try { 689 unmanageObject(mtp); 690 } catch (Exception e) { 691 LOG.warn("Could not unregister ThreadPool MBean", e); 692 } 693 } 694 } 695 696 public void onRouteContextCreate(RouteContext routeContext) { 697 if (!initialized) { 698 return; 699 } 700 701 // Create a map (ProcessorType -> PerformanceCounter) 702 // to be passed to InstrumentationInterceptStrategy. 703 Map<ProcessorDefinition<?>, PerformanceCounter> registeredCounters = 704 new HashMap<ProcessorDefinition<?>, PerformanceCounter>(); 705 706 // Each processor in a route will have its own performance counter. 707 // These performance counter will be embedded to InstrumentationProcessor 708 // and wrap the appropriate processor by InstrumentationInterceptStrategy. 709 RouteDefinition route = routeContext.getRoute(); 710 711 // register performance counters for all processors and its children 712 for (ProcessorDefinition<?> processor : route.getOutputs()) { 713 registerPerformanceCounters(routeContext, processor, registeredCounters); 714 } 715 716 // set this managed intercept strategy that executes the JMX instrumentation for performance metrics 717 // so our registered counters can be used for fine grained performance instrumentation 718 routeContext.setManagedInterceptStrategy(new InstrumentationInterceptStrategy(registeredCounters, wrappedProcessors)); 719 } 720 721 /** 722 * Removes the wrapped processors for the given routes, as they are no longer in use. 723 * <p/> 724 * This is needed to avoid accumulating memory, if a lot of routes is being added and removed. 725 * 726 * @param routes the routes 727 */ 728 private void removeWrappedProcessorsForRoutes(Collection<Route> routes) { 729 // loop the routes, and remove the route associated wrapped processors, as they are no longer in use 730 for (Route route : routes) { 731 String id = route.getId(); 732 733 Iterator<KeyValueHolder<ProcessorDefinition<?>, InstrumentationProcessor>> it = wrappedProcessors.values().iterator(); 734 while (it.hasNext()) { 735 KeyValueHolder<ProcessorDefinition<?>, InstrumentationProcessor> holder = it.next(); 736 RouteDefinition def = ProcessorDefinitionHelper.getRoute(holder.getKey()); 737 if (def != null && id.equals(def.getId())) { 738 it.remove(); 739 } 740 } 741 } 742 743 } 744 745 private void registerPerformanceCounters(RouteContext routeContext, ProcessorDefinition<?> processor, 746 Map<ProcessorDefinition<?>, PerformanceCounter> registeredCounters) { 747 748 // traverse children if any exists 749 List<ProcessorDefinition<?>> children = processor.getOutputs(); 750 for (ProcessorDefinition<?> child : children) { 751 registerPerformanceCounters(routeContext, child, registeredCounters); 752 } 753 754 // skip processors that should not be registered 755 if (!registerProcessor(processor)) { 756 return; 757 } 758 759 // okay this is a processor we would like to manage so create the 760 // a delegate performance counter that acts as the placeholder in the interceptor 761 // that then delegates to the real mbean which we register later in the onServiceAdd method 762 DelegatePerformanceCounter pc = new DelegatePerformanceCounter(); 763 // set statistics enabled depending on the option 764 boolean enabled = camelContext.getManagementStrategy().getManagementAgent().getStatisticsLevel().isDefaultOrExtended(); 765 pc.setStatisticsEnabled(enabled); 766 767 // and add it as a a registered counter that will be used lazy when Camel 768 // does the instrumentation of the route and adds the InstrumentationProcessor 769 // that does the actual performance metrics gatherings at runtime 770 registeredCounters.put(processor, pc); 771 } 772 773 /** 774 * Should the given processor be registered. 775 */ 776 protected boolean registerProcessor(ProcessorDefinition<?> processor) { 777 // skip on exception 778 if (processor instanceof OnExceptionDefinition) { 779 return false; 780 } 781 // skip on completion 782 if (processor instanceof OnCompletionDefinition) { 783 return false; 784 } 785 // skip intercept 786 if (processor instanceof InterceptDefinition) { 787 return false; 788 } 789 // skip aop 790 if (processor instanceof AOPDefinition) { 791 return false; 792 } 793 // skip policy 794 if (processor instanceof PolicyDefinition) { 795 return false; 796 } 797 798 // only if custom id assigned 799 boolean only = getManagementStrategy().getManagementAgent().getOnlyRegisterProcessorWithCustomId() != null 800 && getManagementStrategy().getManagementAgent().getOnlyRegisterProcessorWithCustomId(); 801 if (only) { 802 return processor.hasCustomIdAssigned(); 803 } 804 805 // use customer filter 806 return getManagementStrategy().manageProcessor(processor); 807 } 808 809 private ManagementStrategy getManagementStrategy() { 810 ObjectHelper.notNull(camelContext, "CamelContext"); 811 return camelContext.getManagementStrategy(); 812 } 813 814 private ManagementObjectStrategy getManagementObjectStrategy() { 815 ObjectHelper.notNull(camelContext, "CamelContext"); 816 return camelContext.getManagementStrategy().getManagementObjectStrategy(); 817 } 818 819 /** 820 * Strategy for managing the object 821 * 822 * @param me the managed object 823 * @throws Exception is thrown if error registering the object for management 824 */ 825 protected void manageObject(Object me) throws Exception { 826 getManagementStrategy().manageObject(me); 827 if (me instanceof TimerListener) { 828 TimerListener timer = (TimerListener) me; 829 loadTimer.addTimerListener(timer); 830 } 831 } 832 833 /** 834 * Un-manages the object. 835 * 836 * @param me the managed object 837 * @throws Exception is thrown if error unregistering the managed object 838 */ 839 protected void unmanageObject(Object me) throws Exception { 840 if (me instanceof TimerListener) { 841 TimerListener timer = (TimerListener) me; 842 loadTimer.removeTimerListener(timer); 843 } 844 getManagementStrategy().unmanageObject(me); 845 } 846 847 /** 848 * Whether or not to register the mbean. 849 * <p/> 850 * The {@link ManagementAgent} has options which controls when to register. 851 * This allows us to only register mbeans accordingly. For example by default any 852 * dynamic endpoints is not registered. This avoids to register excessive mbeans, which 853 * most often is not desired. 854 * 855 * @param service the object to register 856 * @param route an optional route the mbean is associated with, can be <tt>null</tt> 857 * @return <tt>true</tt> to register, <tt>false</tt> to skip registering 858 */ 859 protected boolean shouldRegister(Object service, Route route) { 860 // the agent hasn't been started 861 if (!initialized) { 862 return false; 863 } 864 865 LOG.trace("Checking whether to register {} from route: {}", service, route); 866 867 ManagementAgent agent = getManagementStrategy().getManagementAgent(); 868 if (agent == null) { 869 // do not register if no agent 870 return false; 871 } 872 873 // always register if we are starting CamelContext 874 if (getCamelContext().getStatus().isStarting()) { 875 return true; 876 } 877 878 // always register if we are setting up routes 879 if (getCamelContext().isSetupRoutes()) { 880 return true; 881 } 882 883 // register if always is enabled 884 if (agent.getRegisterAlways()) { 885 return true; 886 } 887 888 // is it a known route then always accept 889 if (route != null && knowRouteIds.contains(route.getId())) { 890 return true; 891 } 892 893 // only register if we are starting a new route, and current thread is in starting routes mode 894 if (agent.getRegisterNewRoutes()) { 895 // no specific route, then fallback to see if this thread is starting routes 896 // which is kept as state on the camel context 897 return getCamelContext().isStartingRoutes(); 898 } 899 900 return false; 901 } 902 903 @Override 904 protected void doStart() throws Exception { 905 ObjectHelper.notNull(camelContext, "CamelContext"); 906 907 // defer starting the timer manager until CamelContext has been fully started 908 camelContext.addStartupListener(loadTimerStartupListener); 909 } 910 911 private final class TimerListenerManagerStartupListener implements StartupListener { 912 913 @Override 914 public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception { 915 // we are disabled either if configured explicit, or if level is off 916 boolean load = camelContext.getManagementStrategy().getManagementAgent().getLoadStatisticsEnabled() != null 917 && camelContext.getManagementStrategy().getManagementAgent().getLoadStatisticsEnabled(); 918 boolean disabled = !load || camelContext.getManagementStrategy().getStatisticsLevel() == ManagementStatisticsLevel.Off; 919 920 LOG.debug("Load performance statistics {}", disabled ? "disabled" : "enabled"); 921 if (!disabled) { 922 // must use 1 sec interval as the load statistics is based on 1 sec calculations 923 loadTimer.setInterval(1000); 924 // we have to defer enlisting timer lister manager as a service until CamelContext has been started 925 getCamelContext().addService(loadTimer); 926 } 927 } 928 } 929 930 @Override 931 protected void doStop() throws Exception { 932 initialized = false; 933 knowRouteIds.clear(); 934 preServices.clear(); 935 wrappedProcessors.clear(); 936 managedTracers.clear(); 937 managedBacklogTracers.clear(); 938 managedBacklogDebuggers.clear(); 939 managedThreadPools.clear(); 940 } 941 942 /** 943 * Class which holds any pre registration details. 944 * 945 * @see org.apache.camel.management.DefaultManagementLifecycleStrategy#enlistPreRegisteredServices() 946 */ 947 private static final class PreRegisterService { 948 949 private String name; 950 private Component component; 951 private Endpoint endpoint; 952 private CamelContext camelContext; 953 private Service service; 954 private Route route; 955 956 public void onComponentAdd(String name, Component component) { 957 this.name = name; 958 this.component = component; 959 } 960 961 public void onEndpointAdd(Endpoint endpoint) { 962 this.endpoint = endpoint; 963 } 964 965 public void onServiceAdd(CamelContext camelContext, Service service, Route route) { 966 this.camelContext = camelContext; 967 this.service = service; 968 this.route = route; 969 } 970 971 public String getName() { 972 return name; 973 } 974 975 public Component getComponent() { 976 return component; 977 } 978 979 public Endpoint getEndpoint() { 980 return endpoint; 981 } 982 983 public CamelContext getCamelContext() { 984 return camelContext; 985 } 986 987 public Service getService() { 988 return service; 989 } 990 991 public Route getRoute() { 992 return route; 993 } 994 } 995 996} 997