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