org.apache.camel.management.JmxManagementLifecycleStrategy Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.management;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import javax.management.JMException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.Channel;
import org.apache.camel.Component;
import org.apache.camel.Consumer;
import org.apache.camel.Endpoint;
import org.apache.camel.ManagementStatisticsLevel;
import org.apache.camel.NamedNode;
import org.apache.camel.NonManagedService;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.Route;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.Service;
import org.apache.camel.StartupListener;
import org.apache.camel.TimerListener;
import org.apache.camel.VetoCamelContextStartException;
import org.apache.camel.cluster.CamelClusterService;
import org.apache.camel.health.HealthCheckRegistry;
import org.apache.camel.impl.debugger.BacklogTracer;
import org.apache.camel.impl.debugger.DefaultBacklogDebugger;
import org.apache.camel.management.mbean.ManagedAsyncProcessorAwaitManager;
import org.apache.camel.management.mbean.ManagedBacklogDebugger;
import org.apache.camel.management.mbean.ManagedBacklogTracer;
import org.apache.camel.management.mbean.ManagedBeanIntrospection;
import org.apache.camel.management.mbean.ManagedCamelContext;
import org.apache.camel.management.mbean.ManagedConsumerCache;
import org.apache.camel.management.mbean.ManagedDumpRouteStrategy;
import org.apache.camel.management.mbean.ManagedEndpoint;
import org.apache.camel.management.mbean.ManagedEndpointRegistry;
import org.apache.camel.management.mbean.ManagedEndpointServiceRegistry;
import org.apache.camel.management.mbean.ManagedExchangeFactoryManager;
import org.apache.camel.management.mbean.ManagedInflightRepository;
import org.apache.camel.management.mbean.ManagedProducerCache;
import org.apache.camel.management.mbean.ManagedRestRegistry;
import org.apache.camel.management.mbean.ManagedRoute;
import org.apache.camel.management.mbean.ManagedRuntimeEndpointRegistry;
import org.apache.camel.management.mbean.ManagedService;
import org.apache.camel.management.mbean.ManagedShutdownStrategy;
import org.apache.camel.management.mbean.ManagedStreamCachingStrategy;
import org.apache.camel.management.mbean.ManagedThrottlingExceptionRoutePolicy;
import org.apache.camel.management.mbean.ManagedThrottlingInflightRoutePolicy;
import org.apache.camel.management.mbean.ManagedTracer;
import org.apache.camel.management.mbean.ManagedTransformerRegistry;
import org.apache.camel.management.mbean.ManagedTypeConverterRegistry;
import org.apache.camel.management.mbean.ManagedValidatorRegistry;
import org.apache.camel.management.mbean.ManagedVariableRepository;
import org.apache.camel.model.InterceptDefinition;
import org.apache.camel.model.OnCompletionDefinition;
import org.apache.camel.model.OnExceptionDefinition;
import org.apache.camel.model.PolicyDefinition;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.ProcessorDefinitionHelper;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.spi.AsyncProcessorAwaitManager;
import org.apache.camel.spi.BeanIntrospection;
import org.apache.camel.spi.BrowsableVariableRepository;
import org.apache.camel.spi.ConsumerCache;
import org.apache.camel.spi.DataFormat;
import org.apache.camel.spi.DumpRoutesStrategy;
import org.apache.camel.spi.EndpointRegistry;
import org.apache.camel.spi.EndpointServiceRegistry;
import org.apache.camel.spi.EventNotifier;
import org.apache.camel.spi.ExchangeFactoryManager;
import org.apache.camel.spi.InflightRepository;
import org.apache.camel.spi.InternalProcessor;
import org.apache.camel.spi.LifecycleStrategy;
import org.apache.camel.spi.ManagementAgent;
import org.apache.camel.spi.ManagementInterceptStrategy.InstrumentationProcessor;
import org.apache.camel.spi.ManagementNameStrategy;
import org.apache.camel.spi.ManagementObjectStrategy;
import org.apache.camel.spi.ManagementStrategy;
import org.apache.camel.spi.ProducerCache;
import org.apache.camel.spi.RestRegistry;
import org.apache.camel.spi.RuntimeEndpointRegistry;
import org.apache.camel.spi.ShutdownStrategy;
import org.apache.camel.spi.StreamCachingStrategy;
import org.apache.camel.spi.Tracer;
import org.apache.camel.spi.TransformerRegistry;
import org.apache.camel.spi.TypeConverterRegistry;
import org.apache.camel.spi.UnitOfWork;
import org.apache.camel.spi.ValidatorRegistry;
import org.apache.camel.support.TimerListenerManager;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.throttling.ThrottlingExceptionRoutePolicy;
import org.apache.camel.throttling.ThrottlingInflightRoutePolicy;
import org.apache.camel.util.KeyValueHolder;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default JMX managed lifecycle strategy that registered objects using the configured
* {@link org.apache.camel.spi.ManagementStrategy}.
*
* @see org.apache.camel.spi.ManagementStrategy
*/
public class JmxManagementLifecycleStrategy extends ServiceSupport implements LifecycleStrategy, CamelContextAware {
private static final Logger LOG = LoggerFactory.getLogger(JmxManagementLifecycleStrategy.class);
// the wrapped processors is for performance counters, which are in use for the created routes
// when a route is removed, we should remove the associated processors from this map
private final Map>> wrappedProcessors = new HashMap<>();
private final List> preServices = new ArrayList<>();
private final TimerListenerManager loadTimer = new ManagedLoadTimer();
private final TimerListenerManagerStartupListener loadTimerStartupListener = new TimerListenerManagerStartupListener();
private volatile CamelContext camelContext;
private volatile ManagedCamelContext camelContextMBean;
private volatile boolean initialized;
private final Set knowRouteIds = new HashSet<>();
private final Map managedBacklogTracers = new HashMap<>();
private final Map managedBacklogDebuggers = new HashMap<>();
private final Map managedThreadPools = new HashMap<>();
public JmxManagementLifecycleStrategy() {
}
public JmxManagementLifecycleStrategy(CamelContext camelContext) {
this.camelContext = camelContext;
}
// used for handing over pre-services between a provisional lifecycycle strategy
// and then later the actual strategy to be used when using XML
List> getPreServices() {
return preServices;
}
// used for handing over pre-services between a provisional lifecycycle strategy
// and then later the actual strategy to be used when using XML
void addPreService(java.util.function.Consumer preService) {
preServices.add(preService);
}
@Override
public CamelContext getCamelContext() {
return camelContext;
}
@Override
public void setCamelContext(CamelContext camelContext) {
this.camelContext = camelContext;
}
@Override
public void onContextStarting(CamelContext context) throws VetoCamelContextStartException {
Object mc = getManagementObjectStrategy().getManagedObjectForCamelContext(context);
String name = context.getName();
String managementName = context.getManagementName();
if (managementName == null) {
managementName = context.getManagementNameStrategy().getName();
}
try {
boolean done = false;
while (!done) {
ObjectName on = getManagementStrategy().getManagementObjectNameStrategy()
.getObjectNameForCamelContext(managementName, name);
boolean exists = getManagementStrategy().isManagedName(on);
if (!exists) {
done = true;
} else {
// okay there exists already a CamelContext with this name, we can try to fix it by finding a free name
boolean fixed = false;
// if we use the default name strategy we can find a free name to use
String newName = findFreeName(context.getManagementNameStrategy(), name);
if (newName != null) {
// use this as the fixed name
fixed = true;
done = true;
managementName = newName;
}
// we could not fix it so veto starting camel
if (!fixed) {
throw new VetoCamelContextStartException(
"CamelContext (" + context.getName() + ") with ObjectName[" + on + "] is already registered."
+ " Make sure to use unique names on CamelContext when using multiple CamelContexts in the same MBeanServer.",
context);
} else {
LOG.warn("This CamelContext({}) will be registered using the name: {} due to clash with an "
+ "existing name already registered in MBeanServer.",
context.getName(), managementName);
}
}
}
} catch (VetoCamelContextStartException e) {
// rethrow veto
throw e;
} catch (Exception e) {
// must rethrow to allow CamelContext fallback to non JMX agent to allow
// Camel to continue to run
throw RuntimeCamelException.wrapRuntimeCamelException(e);
}
// set the name we are going to use
context.setManagementName(managementName);
// yes we made it and are initialized
initialized = true;
try {
manageObject(mc);
} catch (Exception e) {
// must rethrow to allow CamelContext fallback to non JMX agent to allow
// Camel to continue to run
throw RuntimeCamelException.wrapRuntimeCamelException(e);
}
if (mc instanceof ManagedCamelContext managedCamelContext) {
camelContextMBean = managedCamelContext;
}
// register any pre-registered now that we are initialized
enlistPreRegisteredServices();
// register health check if detected
HealthCheckRegistry hcr = context.getCamelContextExtension().getContextPlugin(HealthCheckRegistry.class);
if (hcr != null) {
try {
Object me = getManagementObjectStrategy().getManagedObjectForCamelHealth(camelContext, hcr);
if (me == null) {
// endpoint should not be managed
return;
}
manageObject(me);
} catch (Exception e) {
LOG.warn("Could not register CamelHealth MBean. This exception will be ignored.", e);
}
}
try {
Object me = getManagementObjectStrategy().getManagedObjectForRouteController(camelContext,
camelContext.getRouteController());
if (me == null) {
// endpoint should not be managed
return;
}
manageObject(me);
} catch (Exception e) {
LOG.warn("Could not register RouteController MBean. This exception will be ignored.", e);
}
}
private String findFreeName(ManagementNameStrategy strategy, String name) throws MalformedObjectNameException {
// we cannot find a free name for fixed named strategies
if (strategy.isFixedName()) {
return null;
}
// okay try to find a free name
boolean done = false;
String newName = null;
while (!done) {
// compute the next name
newName = strategy.getNextName();
ObjectName on
= getManagementStrategy().getManagementObjectNameStrategy().getObjectNameForCamelContext(newName, name);
done = !getManagementStrategy().isManagedName(on);
if (LOG.isTraceEnabled()) {
LOG.trace("Using name: {} in ObjectName[{}] exists? {}", name, on, done);
}
}
return newName;
}
/**
* After {@link CamelContext} has been enlisted in JMX using
* {@link #onContextStarted(org.apache.camel.CamelContext)} then we can enlist any pre-registered services as well,
* as we had to wait for {@link CamelContext} to be enlisted first.
*
* A component/endpoint/service etc. can be pre-registered when using dependency injection and annotations such as
* {@link org.apache.camel.Produce}, {@link org.apache.camel.EndpointInject}. Therefore, we need to capture those
* registrations up front, and then afterward enlist in JMX when {@link CamelContext} is being started.
*/
private void enlistPreRegisteredServices() {
if (preServices.isEmpty()) {
return;
}
LOG.debug("Registering {} pre registered services", preServices.size());
for (java.util.function.Consumer pre : preServices) {
pre.accept(this);
}
// we are done so clear the list
preServices.clear();
}
@Override
public void onContextStopped(CamelContext context) {
// the agent hasn't been started
if (!initialized) {
return;
}
try {
Object mc = getManagementObjectStrategy().getManagedObjectForRouteController(context, context.getRouteController());
// the context could have been removed already
if (getManagementStrategy().isManaged(mc)) {
unmanageObject(mc);
}
} catch (Exception e) {
LOG.warn("Could not unregister RouteController MBean", e);
}
try {
Object mc = getManagementObjectStrategy().getManagedObjectForCamelContext(context);
// the context could have been removed already
if (getManagementStrategy().isManaged(mc)) {
unmanageObject(mc);
}
} catch (Exception e) {
LOG.warn("Could not unregister CamelContext MBean", e);
}
HealthCheckRegistry hcr = context.getCamelContextExtension().getContextPlugin(HealthCheckRegistry.class);
if (hcr != null) {
try {
Object mc = getManagementObjectStrategy().getManagedObjectForCamelHealth(context, hcr);
// the context could have been removed already
if (getManagementStrategy().isManaged(mc)) {
unmanageObject(mc);
}
} catch (Exception e) {
LOG.warn("Could not unregister CamelHealth MBean", e);
}
}
camelContextMBean = null;
}
@Override
public void onComponentAdd(String name, Component component) {
// always register components as there are only a few of those
if (!initialized) {
// pre register so we can register later when we have been initialized
preServices.add(lf -> lf.onComponentAdd(name, component));
return;
}
try {
Object mc = getManagementObjectStrategy().getManagedObjectForComponent(camelContext, component, name);
manageObject(mc);
} catch (Exception e) {
LOG.warn("Could not register Component MBean", e);
}
}
@Override
public void onComponentRemove(String name, Component component) {
// the agent hasn't been started
if (!initialized) {
return;
}
try {
Object mc = getManagementObjectStrategy().getManagedObjectForComponent(camelContext, component, name);
unmanageObject(mc);
} catch (Exception e) {
LOG.warn("Could not unregister Component MBean", e);
}
}
/**
* If the endpoint is an instance of ManagedResource then register it with the mbean server, if it is not then wrap
* the endpoint in a {@link ManagedEndpoint} and register that with the mbean server.
*
* @param endpoint the Endpoint attempted to be added
*/
@Override
public void onEndpointAdd(Endpoint endpoint) {
if (!initialized) {
// pre register so we can register later when we have been initialized
preServices.add(lf -> lf.onEndpointAdd(endpoint));
return;
}
if (!shouldRegister(endpoint, null)) {
// avoid registering if not needed
return;
}
try {
Object me = getManagementObjectStrategy().getManagedObjectForEndpoint(camelContext, endpoint);
if (me == null) {
// endpoint should not be managed
return;
}
manageObject(me);
} catch (Exception e) {
LOG.warn("Could not register Endpoint MBean for endpoint: {}. This exception will be ignored.", endpoint, e);
}
}
@Override
public void onEndpointRemove(Endpoint endpoint) {
// the agent hasn't been started
if (!initialized) {
return;
}
try {
Object me = getManagementObjectStrategy().getManagedObjectForEndpoint(camelContext, endpoint);
unmanageObject(me);
} catch (Exception e) {
LOG.warn("Could not unregister Endpoint MBean for endpoint: {}. This exception will be ignored.", endpoint, e);
}
}
@Override
public void onServiceAdd(CamelContext context, Service service, Route route) {
if (!initialized) {
// pre-register so we can register later when we have been initialized
preServices.add(lf -> lf.onServiceAdd(camelContext, service, route));
return;
}
// services can by any kind of misc type but also processors
// so we have special logic when its a processor
if (!shouldRegister(service, route)) {
// avoid registering if not needed
return;
}
Object managedObject = getManagedObjectForService(context, service, route);
if (managedObject == null) {
// service should not be managed
return;
}
// skip already managed services, for example if a route has been restarted
if (getManagementStrategy().isManaged(managedObject)) {
LOG.trace("The service is already managed: {}", service);
return;
}
try {
manageObject(managedObject);
} catch (Exception e) {
LOG.warn("Could not register service: {} as Service MBean.", service, e);
}
}
@Override
public void onServiceRemove(CamelContext context, Service service, Route route) {
// the agent hasn't been started
if (!initialized) {
return;
}
Object managedObject = getManagedObjectForService(context, service, route);
if (managedObject != null) {
try {
unmanageObject(managedObject);
} catch (Exception e) {
LOG.warn("Could not unregister service: {} as Service MBean.", service, e);
}
}
}
private Object getManagedObjectForService(CamelContext context, Service service, Route route) {
// skip channel, UoW and dont double wrap instrumentation
if (service instanceof Channel || service instanceof UnitOfWork || service instanceof InstrumentationProcessor) {
return null;
}
// skip non managed services
if (service instanceof NonManagedService) {
return null;
}
Object answer = null;
if (service instanceof BacklogTracer backlogTracer) {
// special for backlog tracer
ManagedBacklogTracer mt = managedBacklogTracers.get(backlogTracer);
if (mt == null) {
mt = new ManagedBacklogTracer(context, backlogTracer);
mt.init(getManagementStrategy());
managedBacklogTracers.put(backlogTracer, mt);
}
return mt;
} else if (service instanceof DefaultBacklogDebugger backlogDebugger) {
// special for backlog debugger
ManagedBacklogDebugger md = managedBacklogDebuggers.get(backlogDebugger);
if (md == null) {
md = new ManagedBacklogDebugger(context, backlogDebugger);
md.init(getManagementStrategy());
managedBacklogDebuggers.put(backlogDebugger, md);
}
return md;
} else if (service instanceof Tracer tracer) {
ManagedTracer mt = new ManagedTracer(camelContext, tracer);
mt.init(getManagementStrategy());
answer = mt;
} else if (service instanceof DumpRoutesStrategy dumpRoutesStrategy) {
ManagedDumpRouteStrategy mdrs = new ManagedDumpRouteStrategy(camelContext, dumpRoutesStrategy);
mdrs.init(getManagementStrategy());
answer = mdrs;
} else if (service instanceof DataFormat dataFormat) {
answer = getManagementObjectStrategy().getManagedObjectForDataFormat(context, dataFormat);
} else if (service instanceof Producer producer) {
answer = getManagementObjectStrategy().getManagedObjectForProducer(context, producer);
} else if (service instanceof Consumer consumer) {
answer = getManagementObjectStrategy().getManagedObjectForConsumer(context, consumer);
} else if (service instanceof Processor processor) {
// special for processors as we need to do some extra work
return getManagedObjectForProcessor(context, processor, route);
} else if (service instanceof ThrottlingInflightRoutePolicy throttlingInflightRoutePolicy) {
answer = new ManagedThrottlingInflightRoutePolicy(context, throttlingInflightRoutePolicy);
} else if (service instanceof ThrottlingExceptionRoutePolicy throttlingExceptionRoutePolicy) {
answer = new ManagedThrottlingExceptionRoutePolicy(context, throttlingExceptionRoutePolicy);
} else if (service instanceof ConsumerCache consumerCache) {
answer = new ManagedConsumerCache(context, consumerCache);
} else if (service instanceof ProducerCache producerCache) {
answer = new ManagedProducerCache(context, producerCache);
} else if (service instanceof ExchangeFactoryManager exchangeFactoryManager) {
answer = new ManagedExchangeFactoryManager(context, exchangeFactoryManager);
} else if (service instanceof EndpointRegistry endpointRegistry) {
answer = new ManagedEndpointRegistry(context, endpointRegistry);
} else if (service instanceof BeanIntrospection beanIntrospection) {
answer = new ManagedBeanIntrospection(context, beanIntrospection);
} else if (service instanceof TypeConverterRegistry typeConverterRegistry) {
answer = new ManagedTypeConverterRegistry(context, typeConverterRegistry);
} else if (service instanceof RestRegistry restRegistry) {
answer = new ManagedRestRegistry(context, restRegistry);
} else if (service instanceof EndpointServiceRegistry endpointServiceRegistry) {
answer = new ManagedEndpointServiceRegistry(context, endpointServiceRegistry);
} else if (service instanceof InflightRepository inflightRepository) {
answer = new ManagedInflightRepository(context, inflightRepository);
} else if (service instanceof AsyncProcessorAwaitManager asyncProcessorAwaitManager) {
answer = new ManagedAsyncProcessorAwaitManager(context, asyncProcessorAwaitManager);
} else if (service instanceof RuntimeEndpointRegistry runtimeEndpointRegistry) {
answer = new ManagedRuntimeEndpointRegistry(context, runtimeEndpointRegistry);
} else if (service instanceof StreamCachingStrategy streamCachingStrategy) {
answer = new ManagedStreamCachingStrategy(context, streamCachingStrategy);
} else if (service instanceof ShutdownStrategy shutdownStrategy) {
answer = new ManagedShutdownStrategy(context, shutdownStrategy);
} else if (service instanceof EventNotifier eventNotifier)
answer = getManagementObjectStrategy().getManagedObjectForEventNotifier(context, eventNotifier);
else if (service instanceof TransformerRegistry transformerRegistry) {
answer = new ManagedTransformerRegistry(context, transformerRegistry);
} else if (service instanceof ValidatorRegistry validatorRegistry) {
answer = new ManagedValidatorRegistry(context, validatorRegistry);
} else if (service instanceof BrowsableVariableRepository variableRepository) {
answer = new ManagedVariableRepository(context, variableRepository);
} else if (service instanceof CamelClusterService camelClusterService) {
answer = getManagementObjectStrategy().getManagedObjectForClusterService(context, camelClusterService);
} else if (service != null) {
// fallback as generic service
answer = getManagementObjectStrategy().getManagedObjectForService(context, service);
}
if (answer instanceof ManagedService ms) {
ms.setRoute(route);
ms.init(getManagementStrategy());
}
return answer;
}
private Object getManagedObjectForProcessor(CamelContext context, Processor processor, Route route) {
// a bit of magic here as the processors we want to manage have already been registered
// in the wrapped processors map when Camel have instrumented the route on route initialization
// so the idea is now to only manage the processors from the map
KeyValueHolder> holder = wrappedProcessors.get(processor);
if (holder == null) {
// skip as it's not a well known processor we want to manage anyway, such as Channel/UnitOfWork/Pipeline etc.
return null;
}
// get the managed object as it can be a specialized type such as a Delayer/Throttler etc.
Object managedObject
= getManagementObjectStrategy().getManagedObjectForProcessor(context, processor, holder.getKey(), route);
// only manage if we have a name for it as otherwise we do not want to manage it anyway
if (managedObject != null) {
// is it a performance counter then we need to set our counter
if (managedObject instanceof PerformanceCounter) {
InstrumentationProcessor> counter = holder.getValue();
if (counter != null) {
// change counter to us
counter.setCounter(managedObject);
}
}
}
return managedObject;
}
@Override
public void onRoutesAdd(Collection routes) {
for (Route route : routes) {
// if we are starting CamelContext or either of the two options has been
// enabled, then enlist the route as a known route
if (getCamelContext().getStatus().isStarting()
|| getManagementStrategy().getManagementAgent().getRegisterAlways()
|| getManagementStrategy().getManagementAgent().getRegisterNewRoutes()) {
// register as known route id
knowRouteIds.add(route.getId());
}
if (!shouldRegister(route, route)) {
// avoid registering if not needed, skip to next route
continue;
}
Object mr = getManagementObjectStrategy().getManagedObjectForRoute(camelContext, route);
// skip already managed routes, for example if the route has been restarted
if (getManagementStrategy().isManaged(mr)) {
LOG.trace("The route is already managed: {}", route);
continue;
}
// get the wrapped instrumentation processor from this route
// and set me as the counter
Processor processor = route.getProcessor();
if (processor instanceof InternalProcessor internal && mr instanceof ManagedRoute routeMBean) {
DefaultInstrumentationProcessor task = internal.getAdvice(DefaultInstrumentationProcessor.class);
if (task != null) {
// we need to wrap the counter with the camel context, so we get stats updated on the context as well
if (camelContextMBean != null) {
CompositePerformanceCounter wrapper = new CompositePerformanceCounter(routeMBean, camelContextMBean);
task.setCounter(wrapper);
} else {
task.setCounter(routeMBean);
}
}
}
try {
manageObject(mr);
} catch (JMException e) {
LOG.warn("Could not register Route MBean", e);
} catch (Exception e) {
LOG.warn("Could not create Route MBean", e);
}
}
}
@Override
public void onRoutesRemove(Collection routes) {
// the agent hasn't been started
if (!initialized) {
return;
}
for (Route route : routes) {
Object mr = getManagementObjectStrategy().getManagedObjectForRoute(camelContext, route);
// skip unmanaged routes
if (!getManagementStrategy().isManaged(mr)) {
LOG.trace("The route is not managed: {}", route);
continue;
}
try {
unmanageObject(mr);
} catch (Exception e) {
LOG.warn("Could not unregister Route MBean", e);
}
// remove from known routes ids, as the route has been removed
knowRouteIds.remove(route.getId());
}
// after the routes has been removed, we should clear the wrapped processors as we no longer need them
// as they were just a provisional map used during creation of routes
removeWrappedProcessorsForRoutes(routes);
}
@Override
public void onThreadPoolAdd(
CamelContext camelContext, ThreadPoolExecutor threadPool, String id,
String sourceId, String routeId, String threadPoolProfileId) {
if (!initialized) {
// pre register so we can register later when we have been initialized
preServices.add(lf -> lf.onThreadPoolAdd(camelContext, threadPool, id, sourceId, routeId, threadPoolProfileId));
return;
}
if (!shouldRegister(threadPool, null)) {
// avoid registering if not needed
return;
}
Object mtp = getManagementObjectStrategy().getManagedObjectForThreadPool(camelContext, threadPool, id, sourceId,
routeId, threadPoolProfileId);
// skip already managed services, for example if a route has been restarted
if (getManagementStrategy().isManaged(mtp)) {
LOG.trace("The thread pool is already managed: {}", threadPool);
return;
}
try {
manageObject(mtp);
// store a reference so we can unmanage from JMX when the thread pool is removed
// we need to keep track here, as we cannot re-construct the thread pool ObjectName when removing the thread pool
managedThreadPools.put(threadPool, mtp);
} catch (Exception e) {
LOG.warn("Could not register thread pool: {} as ThreadPool MBean.", threadPool, e);
}
}
@Override
public void onThreadPoolRemove(CamelContext camelContext, ThreadPoolExecutor threadPool) {
if (!initialized) {
return;
}
// lookup the thread pool and remove it from JMX
Object mtp = managedThreadPools.remove(threadPool);
if (mtp != null) {
// skip unmanaged routes
if (!getManagementStrategy().isManaged(mtp)) {
LOG.trace("The thread pool is not managed: {}", threadPool);
return;
}
try {
unmanageObject(mtp);
} catch (Exception e) {
LOG.warn("Could not unregister ThreadPool MBean", e);
}
}
}
@Override
public void onRouteContextCreate(Route route) {
// Create a map (ProcessorType -> PerformanceCounter)
// to be passed to InstrumentationInterceptStrategy.
Map registeredCounters = new HashMap<>();
// Each processor in a route will have its own performance counter.
// These performance counter will be embedded to InstrumentationProcessor
// and wrap the appropriate processor by InstrumentationInterceptStrategy.
RouteDefinition routeDefinition = (RouteDefinition) route.getRoute();
// register performance counters for all processors and its children
for (ProcessorDefinition> processor : routeDefinition.getOutputs()) {
registerPerformanceCounters(route, processor, registeredCounters);
}
// set this managed intercept strategy that executes the JMX instrumentation for performance metrics
// so our registered counters can be used for fine-grained performance instrumentation
route.setManagementInterceptStrategy(new InstrumentationInterceptStrategy(registeredCounters, wrappedProcessors));
}
/**
* Removes the wrapped processors for the given routes, as they are no longer in use.
*
* This is needed to avoid accumulating memory, if a lot of routes is being added and removed.
*
* @param routes the routes
*/
private void removeWrappedProcessorsForRoutes(Collection routes) {
// loop the routes, and remove the route associated wrapped processors, as they are no longer in use
for (Route route : routes) {
String id = route.getId();
Iterator>> it = wrappedProcessors.values().iterator();
while (it.hasNext()) {
KeyValueHolder> holder = it.next();
RouteDefinition def = ProcessorDefinitionHelper.getRoute(holder.getKey());
if (def != null && id.equals(def.getId())) {
it.remove();
}
}
}
}
private void registerPerformanceCounters(
Route route, ProcessorDefinition> processor,
Map registeredCounters) {
// traverse children if any exists
List> children = processor.getOutputs();
for (ProcessorDefinition> child : children) {
registerPerformanceCounters(route, child, registeredCounters);
}
// skip processors that should not be registered
if (!registerProcessor(processor)) {
return;
}
// okay this is a processor we would like to manage so create the
// a delegate performance counter that acts as the placeholder in the interceptor
// that then delegates to the real mbean which we register later in the onServiceAdd method
DelegatePerformanceCounter pc = new DelegatePerformanceCounter();
// set statistics enabled depending on the option
boolean enabled = camelContext.getManagementStrategy().getManagementAgent().getStatisticsLevel().isDefaultOrExtended();
pc.setStatisticsEnabled(enabled);
// and add it as a a registered counter that will be used lazy when Camel
// does the instrumentation of the route and adds the InstrumentationProcessor
// that does the actual performance metrics gatherings at runtime
registeredCounters.put(processor, pc);
}
/**
* Should the given processor be registered.
*/
protected boolean registerProcessor(ProcessorDefinition> processor) {
//skip processors according the ManagementMBeansLevel
if (!getManagementStrategy().getManagementAgent().getMBeansLevel().isProcessors()) {
return false;
}
// skip on exception
if (processor instanceof OnExceptionDefinition) {
return false;
}
// skip on completion
if (processor instanceof OnCompletionDefinition) {
return false;
}
// skip intercept
if (processor instanceof InterceptDefinition) {
return false;
}
// skip policy
if (processor instanceof PolicyDefinition) {
return false;
}
// only if custom id assigned
boolean only = getManagementStrategy().getManagementAgent().getOnlyRegisterProcessorWithCustomId() != null
&& getManagementStrategy().getManagementAgent().getOnlyRegisterProcessorWithCustomId();
if (only) {
return processor.hasCustomIdAssigned();
}
// use customer filter
return getManagementStrategy().manageProcessor(processor);
}
private ManagementStrategy getManagementStrategy() {
ObjectHelper.notNull(camelContext, "CamelContext");
return camelContext.getManagementStrategy();
}
private ManagementObjectStrategy getManagementObjectStrategy() {
ObjectHelper.notNull(camelContext, "CamelContext");
return camelContext.getManagementStrategy().getManagementObjectStrategy();
}
/**
* Strategy for managing the object
*
* @param me the managed object
* @throws Exception is thrown if error registering the object for management
*/
protected void manageObject(Object me) throws Exception {
getManagementStrategy().manageObject(me);
if (me instanceof TimerListener timer) {
loadTimer.addTimerListener(timer);
}
}
/**
* Un-manages the object.
*
* @param me the managed object
* @throws Exception is thrown if error unregistering the managed object
*/
protected void unmanageObject(Object me) throws Exception {
if (me instanceof TimerListener timer) {
loadTimer.removeTimerListener(timer);
}
getManagementStrategy().unmanageObject(me);
}
/**
* Whether to register the mbean.
*
* The {@link ManagementAgent} has options which controls when to register. This allows us to only register mbeans
* accordingly. For example by default any dynamic endpoints is not registered. This avoids to register excessive
* mbeans, which most often is not desired.
*
* @param service the object to register
* @param route an optional route the mbean is associated with, can be null
* @return true to register, false to skip registering
*/
protected boolean shouldRegister(Object service, Route route) {
// the agent hasn't been started
if (!initialized) {
return false;
}
LOG.trace("Checking whether to register {} from route: {}", service, route);
//skip route according the ManagementMBeansLevel
if (!getManagementStrategy().getManagementAgent().getMBeansLevel().isRoutes()) {
return false;
}
ManagementAgent agent = getManagementStrategy().getManagementAgent();
if (agent == null) {
// do not register if no agent
return false;
}
if (route != null && route.isCreatedByKamelet() && !agent.getRegisterRoutesCreateByKamelet()) {
// skip routes created from kamelets
return false;
}
if (route != null && route.isCreatedByRouteTemplate() && !agent.getRegisterRoutesCreateByTemplate()) {
// skip routes created from route templates
return false;
}
// always register if we are starting CamelContext
if (getCamelContext().getStatus().isStarting()
|| getCamelContext().getStatus().isInitializing()) {
return true;
}
// always register if we are setting up routes
if (getCamelContext().getCamelContextExtension().isSetupRoutes()) {
return true;
}
// register if always is enabled
if (agent.getRegisterAlways()) {
return true;
}
// is it a known route then always accept
if (route != null && knowRouteIds.contains(route.getId())) {
return true;
}
// only register if we are starting a new route, and current thread is in starting routes mode
if (agent.getRegisterNewRoutes()) {
// no specific route, then fallback to see if this thread is starting routes
// which is kept as state on the camel context
return getCamelContext().getRouteController().isStartingRoutes();
}
return false;
}
@Override
protected void doStart() throws Exception {
ObjectHelper.notNull(camelContext, "CamelContext");
// defer starting the timer manager until CamelContext has been fully started
camelContext.addStartupListener(loadTimerStartupListener);
}
private final class TimerListenerManagerStartupListener implements StartupListener {
@Override
public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception {
// we are disabled either if configured explicit, or if level is off
boolean load = camelContext.getManagementStrategy().getManagementAgent().getLoadStatisticsEnabled() != null
&& camelContext.getManagementStrategy().getManagementAgent().getLoadStatisticsEnabled();
boolean disabled = !load || camelContext.getManagementStrategy().getManagementAgent().getStatisticsLevel()
== ManagementStatisticsLevel.Off;
LOG.debug("Load performance statistics {}", disabled ? "disabled" : "enabled");
if (!disabled) {
// must use 1 sec interval as the load statistics is based on 1 sec calculations
loadTimer.setInterval(1000);
// we have to defer enlisting timer lister manager as a service until CamelContext has been started
getCamelContext().addService(loadTimer);
}
}
}
@Override
protected void doStop() throws Exception {
initialized = false;
knowRouteIds.clear();
preServices.clear();
wrappedProcessors.clear();
managedBacklogTracers.clear();
managedBacklogDebuggers.clear();
managedThreadPools.clear();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy