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