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