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