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