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