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