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