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