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