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