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.Collection;
020    import java.util.HashMap;
021    import java.util.List;
022    import java.util.Map;
023    import javax.management.JMException;
024    
025    import org.apache.camel.CamelContext;
026    import org.apache.camel.Component;
027    import org.apache.camel.Consumer;
028    import org.apache.camel.Endpoint;
029    import org.apache.camel.ManagementStatisticsLevel;
030    import org.apache.camel.Processor;
031    import org.apache.camel.Producer;
032    import org.apache.camel.Route;
033    import org.apache.camel.Service;
034    import org.apache.camel.builder.ErrorHandlerBuilder;
035    import org.apache.camel.impl.EventDrivenConsumerRoute;
036    import org.apache.camel.impl.ScheduledPollConsumer;
037    import org.apache.camel.impl.ThrottlingInflightRoutePolicy;
038    import org.apache.camel.management.mbean.ManagedBrowsableEndpoint;
039    import org.apache.camel.management.mbean.ManagedCamelContext;
040    import org.apache.camel.management.mbean.ManagedComponent;
041    import org.apache.camel.management.mbean.ManagedConsumer;
042    import org.apache.camel.management.mbean.ManagedDelayer;
043    import org.apache.camel.management.mbean.ManagedEndpoint;
044    import org.apache.camel.management.mbean.ManagedErrorHandler;
045    import org.apache.camel.management.mbean.ManagedPerformanceCounter;
046    import org.apache.camel.management.mbean.ManagedProcessor;
047    import org.apache.camel.management.mbean.ManagedProducer;
048    import org.apache.camel.management.mbean.ManagedRoute;
049    import org.apache.camel.management.mbean.ManagedScheduledPollConsumer;
050    import org.apache.camel.management.mbean.ManagedSendProcessor;
051    import org.apache.camel.management.mbean.ManagedService;
052    import org.apache.camel.management.mbean.ManagedThrottler;
053    import org.apache.camel.management.mbean.ManagedThrottlingInflightRoutePolicy;
054    import org.apache.camel.management.mbean.ManagedTracer;
055    import org.apache.camel.model.AOPDefinition;
056    import org.apache.camel.model.InterceptDefinition;
057    import org.apache.camel.model.OnCompletionDefinition;
058    import org.apache.camel.model.OnExceptionDefinition;
059    import org.apache.camel.model.PolicyDefinition;
060    import org.apache.camel.model.ProcessorDefinition;
061    import org.apache.camel.model.RouteDefinition;
062    import org.apache.camel.processor.Delayer;
063    import org.apache.camel.processor.ErrorHandler;
064    import org.apache.camel.processor.SendProcessor;
065    import org.apache.camel.processor.Throttler;
066    import org.apache.camel.processor.interceptor.Tracer;
067    import org.apache.camel.spi.BrowsableEndpoint;
068    import org.apache.camel.spi.LifecycleStrategy;
069    import org.apache.camel.spi.ManagementAware;
070    import org.apache.camel.spi.ManagementStrategy;
071    import org.apache.camel.spi.RouteContext;
072    import org.apache.camel.util.KeyValueHolder;
073    import org.apache.camel.util.ObjectHelper;
074    import org.apache.commons.logging.Log;
075    import org.apache.commons.logging.LogFactory;
076    
077    /**
078     * Default JMX managed lifecycle strategy that registered objects using the configured
079     * {@link org.apache.camel.spi.ManagementStrategy}.
080     *
081     * @see org.apache.camel.spi.ManagementStrategy
082     * @version $Revision: 883288 $
083     */
084    public class DefaultManagementLifecycleStrategy implements LifecycleStrategy, Service {
085    
086        private static final Log LOG = LogFactory.getLog(DefaultManagementLifecycleStrategy.class);
087        private final Map<Processor, KeyValueHolder<ProcessorDefinition, InstrumentationProcessor>> wrappedProcessors =
088                new HashMap<Processor, KeyValueHolder<ProcessorDefinition, InstrumentationProcessor>>();
089        private final CamelContext context;
090        private boolean initialized;
091    
092        public DefaultManagementLifecycleStrategy(CamelContext context) {
093            this.context = context;
094        }
095    
096        public void onContextStart(CamelContext context) {
097            try {
098                initialized = true;
099    
100                ManagedCamelContext mc = new ManagedCamelContext(context);
101                mc.init(context.getManagementStrategy());
102                getManagementStrategy().manageObject(mc);
103    
104            } catch (Exception e) {
105                // must rethrow to allow CamelContext fallback to non JMX agent to allow
106                // Camel to continue to run
107                throw ObjectHelper.wrapRuntimeCamelException(e);
108            }
109        }
110    
111        public void onContextStop(CamelContext context) {
112            // the agent hasn't been started
113            if (!initialized) {
114                return;
115            }
116            try {
117                ManagedCamelContext mc = new ManagedCamelContext(context);
118                mc.init(context.getManagementStrategy());
119                // the context could have been removed already
120                if (getManagementStrategy().isManaged(null, mc)) {
121                    getManagementStrategy().unmanageObject(mc);
122                }
123            } catch (Exception e) {
124                LOG.warn("Could not unregister CamelContext MBean", e);
125            }
126        }
127    
128        public void onComponentAdd(String name, Component component) {
129            // the agent hasn't been started
130            if (!initialized) {
131                return;
132            }
133            try {
134                Object mc = getManagedObjectForComponent(name, component);
135                getManagementStrategy().manageObject(mc);
136            } catch (Exception e) {
137                LOG.warn("Could not register Component MBean", e);
138            }
139        }
140    
141        public void onComponentRemove(String name, Component component) {
142            // the agent hasn't been started
143            if (!initialized) {
144                return;
145            }
146            try {
147                Object mc = getManagedObjectForComponent(name, component);
148                getManagementStrategy().unmanageObject(mc);
149            } catch (Exception e) {
150                LOG.warn("Could not unregister Component MBean", e);
151            }
152        }
153    
154        @SuppressWarnings("unchecked")
155        private Object getManagedObjectForComponent(String name, Component component) {
156            if (component instanceof ManagementAware) {
157                return ((ManagementAware) component).getManagedObject(component);
158            } else {
159                ManagedComponent mc = new ManagedComponent(name, component);
160                mc.init(getManagementStrategy());
161                return mc;
162            }
163        }
164    
165        /**
166         * If the endpoint is an instance of ManagedResource then register it with the
167         * mbean server, if it is not then wrap the endpoint in a {@link ManagedEndpoint} and
168         * register that with the mbean server.
169         *
170         * @param endpoint the Endpoint attempted to be added
171         */
172        @SuppressWarnings("unchecked")
173        public void onEndpointAdd(Endpoint endpoint) {
174            // the agent hasn't been started
175            if (!initialized) {
176                return;
177            }
178    
179            try {
180                Object managedObject = getManagedObjectForEndpoint(endpoint);
181                if (managedObject == null) {
182                    // endpoint should not be managed
183                    return;
184                }
185                getManagementStrategy().manageObject(managedObject);
186            } catch (Exception e) {
187                LOG.warn("Could not register Endpoint MBean for uri: " + endpoint.getEndpointUri(), e);
188            }
189        }
190    
191        public void onEndpointRemove(Endpoint endpoint) {
192            // the agent hasn't been started
193            if (!initialized) {
194                return;
195            }
196    
197            try {
198                Object me = getManagedObjectForEndpoint(endpoint);
199                getManagementStrategy().unmanageObject(me);
200            } catch (Exception e) {
201                LOG.warn("Could not unregister Endpoint MBean for uri: " + endpoint.getEndpointUri(), e);
202            }
203        }
204    
205        @SuppressWarnings("unchecked")
206        private Object getManagedObjectForEndpoint(Endpoint endpoint) {
207            // we only want to manage singleton endpoints
208            if (!endpoint.isSingleton()) {
209                return null;
210            }
211    
212            if (endpoint instanceof ManagementAware) {
213                return ((ManagementAware) endpoint).getManagedObject(endpoint);
214            } else if (endpoint instanceof BrowsableEndpoint) {
215                ManagedBrowsableEndpoint me = new ManagedBrowsableEndpoint((BrowsableEndpoint) endpoint);
216                me.init(getManagementStrategy());
217                return me;
218            } else {
219                ManagedEndpoint me = new ManagedEndpoint(endpoint);
220                me.init(getManagementStrategy());
221                return me;
222            }
223        }
224    
225        public void onServiceAdd(CamelContext context, Service service, Route route) {
226            // services can by any kind of misc type but also processors
227            // so we have special logic when its a processor
228    
229            // the agent hasn't been started
230            if (!initialized) {
231                return;
232            }
233    
234            Object managedObject = getManagedObjectForService(context, service, route);
235            if (managedObject == null) {
236                // service should not be managed
237                return;
238            }
239    
240            // skip already managed services, for example if a route has been restarted
241            if (getManagementStrategy().isManaged(managedObject, null)) {
242                if (LOG.isTraceEnabled()) {
243                    LOG.trace("The service is already managed: " + service);
244                }
245                return;
246            }
247    
248            try {
249                getManagementStrategy().manageObject(managedObject);
250            } catch (Exception e) {
251                LOG.warn("Could not register service: " + service + " as Service MBean.", e);
252            }
253        }
254    
255        public void onServiceRemove(CamelContext context, Service service, Route route) {
256            // the agent hasn't been started
257            if (!initialized) {
258                return;
259            }
260    
261            Object managedObject = getManagedObjectForService(context, service, route);
262            if (managedObject != null) {
263                try {
264                    getManagementStrategy().unmanageObject(managedObject);
265                } catch (Exception e) {
266                    LOG.warn("Could not unregister service: " + service + " as Service MBean.", e);
267                }
268            }
269        }
270    
271        @SuppressWarnings("unchecked")
272        private Object getManagedObjectForService(CamelContext context, Service service, Route route) {
273            ManagedService answer = null;
274    
275            if (service instanceof ManagementAware) {
276                return ((ManagementAware) service).getManagedObject(service);
277            } else if (service instanceof Tracer) {
278                // special for tracer
279                ManagedTracer mt = new ManagedTracer(context, (Tracer) service);
280                mt.init(getManagementStrategy());
281                return mt;
282            } else if (service instanceof Producer) {
283                answer = new ManagedProducer(context, (Producer) service);
284            } else if (service instanceof ScheduledPollConsumer) {
285                answer = new ManagedScheduledPollConsumer(context, (ScheduledPollConsumer) service);
286            } else if (service instanceof Consumer) {
287                answer = new ManagedConsumer(context, (Consumer) service);
288            } else if (service instanceof Processor) {
289                // special for processors
290                return getManagedObjectForProcessor(context, (Processor) service, route);
291            } else if (service instanceof ThrottlingInflightRoutePolicy) {
292                answer = new ManagedThrottlingInflightRoutePolicy(context, (ThrottlingInflightRoutePolicy) service);
293            } else if (service != null) {
294                // fallback as generic service
295                answer = new ManagedService(context, service);
296            }
297    
298            if (answer != null) {
299                answer.setRoute(route);
300                answer.init(getManagementStrategy());
301                return answer;
302            } else {
303                // not supported
304                return null;
305            }
306        }
307    
308        private Object getManagedObjectForProcessor(CamelContext context, Processor processor, Route route) {
309            // a bit of magic here as the processors we want to manage have already been registered
310            // in the wrapped processors map when Camel have instrumented the route on route initialization
311            // so the idea is now to only manage the processors from the map
312            KeyValueHolder<ProcessorDefinition, InstrumentationProcessor> holder = wrappedProcessors.get(processor);
313            if (holder == null) {
314                // skip as its not an well known processor we want to manage anyway, such as Channel/UnitOfWork/Pipeline etc.
315                return null;
316            }
317    
318            // get the managed object as it can be a specialized type such as a Delayer/Throttler etc.
319            Object managedObject = createManagedObjectForProcessor(context, processor, holder.getKey(), route);
320            // only manage if we have a name for it as otherwise we do not want to manage it anyway
321            if (managedObject != null) {
322                // is it a performance counter then we need to set our counter
323                if (managedObject instanceof PerformanceCounter) {
324                    InstrumentationProcessor counter = holder.getValue();
325                    if (counter != null) {
326                        // change counter to us
327                        counter.setCounter((ManagedPerformanceCounter) managedObject);
328                    }
329                }
330            }
331    
332            return managedObject;
333        }
334    
335        private Object createManagedObjectForProcessor(CamelContext context, Processor processor,
336                                                       ProcessorDefinition definition, Route route) {
337            // skip error handlers
338            if (processor instanceof ErrorHandler) {
339                return false;
340            }
341    
342            ManagedProcessor answer = null;
343            if (processor instanceof Delayer) {
344                answer = new ManagedDelayer(context, (Delayer) processor, definition);
345            } else if (processor instanceof Throttler) {
346                answer = new ManagedThrottler(context, (Throttler) processor, definition);
347            } else if (processor instanceof SendProcessor) {
348                answer = new ManagedSendProcessor(context, (SendProcessor) processor, definition);
349            }
350    
351            if (answer == null) {
352                // fallback to a generic processor
353                answer = new ManagedProcessor(context, processor, definition);
354            }
355    
356            answer.setRoute(route);
357            answer.init(getManagementStrategy());
358            return answer;
359        }
360    
361        public void onRoutesAdd(Collection<Route> routes) {
362            // the agent hasn't been started
363            if (!initialized) {
364                return;
365            }
366    
367            for (Route route : routes) {
368                ManagedRoute mr = new ManagedRoute(context, route);
369                mr.init(getManagementStrategy());
370    
371                // skip already managed routes, for example if the route has been restarted
372                if (getManagementStrategy().isManaged(mr, null)) {
373                    if (LOG.isTraceEnabled()) {
374                        LOG.trace("The route is already managed: " + route);
375                    }
376                    continue;
377                }
378    
379                // get the wrapped instrumentation processor from this route
380                // and set me as the counter
381                if (route instanceof EventDrivenConsumerRoute) {
382                    EventDrivenConsumerRoute edcr = (EventDrivenConsumerRoute) route;
383                    Processor processor = edcr.getProcessor();
384                    if (processor instanceof InstrumentationProcessor) {
385                        InstrumentationProcessor ip = (InstrumentationProcessor) processor;
386                        ip.setCounter(mr);
387                    }
388                }
389    
390                try {
391                    getManagementStrategy().manageObject(mr);
392                } catch (JMException e) {
393                    LOG.warn("Could not register Route MBean", e);
394                } catch (Exception e) {
395                    LOG.warn("Could not create Route MBean", e);
396                }
397            }
398        }
399    
400        public void onRoutesRemove(Collection<Route> routes) {
401            // noop - keep the route in the mbean so its still there, it will still be unregistered
402            // when camel itself is shutting down
403        }
404    
405        public void onErrorHandlerAdd(RouteContext routeContext, Processor errorHandler, ErrorHandlerBuilder errorHandlerBuilder) {
406            // the agent hasn't been started
407            if (!initialized) {
408                return;
409            }
410    
411            ManagedErrorHandler me = new ManagedErrorHandler(routeContext, errorHandler, errorHandlerBuilder);
412            me.init(getManagementStrategy());
413    
414            // skip already managed services, for example if a route has been restarted
415            if (getManagementStrategy().isManaged(me, null)) {
416                if (LOG.isTraceEnabled()) {
417                    LOG.trace("The error handler builder is already managed: " + errorHandlerBuilder);
418                }
419                return;
420            }
421    
422            try {
423                getManagementStrategy().manageObject(me);
424            } catch (Exception e) {
425                LOG.warn("Could not register error handler builder: " + errorHandlerBuilder + " as ErrorHandlerMBean.", e);
426            }
427        }
428    
429        public void onRouteContextCreate(RouteContext routeContext) {
430            // the agent hasn't been started
431            if (!initialized) {
432                return;
433            }
434    
435            // Create a map (ProcessorType -> PerformanceCounter)
436            // to be passed to InstrumentationInterceptStrategy.
437            Map<ProcessorDefinition, PerformanceCounter> registeredCounters =
438                    new HashMap<ProcessorDefinition, PerformanceCounter>();
439    
440            // Each processor in a route will have its own performance counter.
441            // These performance counter will be embedded to InstrumentationProcessor
442            // and wrap the appropriate processor by InstrumentationInterceptStrategy.
443            RouteDefinition route = routeContext.getRoute();
444    
445            // register performance counters for all processors and its children
446            for (ProcessorDefinition processor : route.getOutputs()) {
447                registerPerformanceCounters(routeContext, processor, registeredCounters);
448            }
449    
450            // set this managed intercept strategy that executes the JMX instrumentation for performance metrics
451            // so our registered counters can be used for fine grained performance instrumentation
452            routeContext.setManagedInterceptStrategy(new InstrumentationInterceptStrategy(registeredCounters, wrappedProcessors));
453        }
454    
455        @SuppressWarnings("unchecked")
456        private void registerPerformanceCounters(RouteContext routeContext, ProcessorDefinition processor,
457                                                 Map<ProcessorDefinition, PerformanceCounter> registeredCounters) {
458    
459            // traverse children if any exists
460            List<ProcessorDefinition> children = processor.getOutputs();
461            for (ProcessorDefinition child : children) {
462                registerPerformanceCounters(routeContext, child, registeredCounters);
463            }
464    
465            // skip processors that should not be registered
466            if (!registerProcessor(processor)) {
467                return;
468            }
469    
470            // okay this is a processor we would like to manage so create the
471            // a delegate performance counter that acts as the placeholder in the interceptor
472            // that then delegates to the real mbean which we register later in the onServiceAdd method
473            DelegatePerformanceCounter pc = new DelegatePerformanceCounter();
474            // set statistics enabled depending on the option
475            boolean enabled = context.getManagementStrategy().getStatisticsLevel() == ManagementStatisticsLevel.All;
476            pc.setStatisticsEnabled(enabled);
477    
478            // and add it as a a registered counter that will be used lazy when Camel
479            // does the instrumentation of the route and adds the InstrumentationProcessor
480            // that does the actual performance metrics gatherings at runtime
481            registeredCounters.put(processor, pc);
482        }
483    
484        /**
485         * Should the given processor be registered.
486         */
487        protected boolean registerProcessor(ProcessorDefinition processor) {
488            // skip on exception
489            if (processor instanceof OnExceptionDefinition) {
490                return false;
491            }
492            // skip on completion
493            if (processor instanceof OnCompletionDefinition) {
494                return false;
495            }
496            // skip intercept
497            if (processor instanceof InterceptDefinition) {
498                return false;
499            }
500            // skip aop
501            if (processor instanceof AOPDefinition) {
502                return false;
503            }
504            // skip policy
505            if (processor instanceof PolicyDefinition) {
506                return false;
507            }
508    
509            // only if custom id assigned
510            if (getManagementStrategy().isOnlyManageProcessorWithCustomId()) {
511                return processor.hasCustomIdAssigned();
512            }
513    
514            // use customer filter
515            return getManagementStrategy().manageProcessor(processor);
516        }
517    
518        private ManagementStrategy getManagementStrategy() {
519            return context.getManagementStrategy();
520        }
521    
522        public void start() throws Exception {
523        }
524    
525        public void stop() throws Exception {
526            initialized = false;
527        }
528    }
529