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