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