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