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