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