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