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