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