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