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