org.apache.camel.management.DefaultManagementAgent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of camel-core Show documentation
Show all versions of camel-core Show documentation
The Core Camel Java DSL based router
/**
* 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.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MBeanServerInvocationHandler;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.ManagementStatisticsLevel;
import org.apache.camel.spi.ManagementAgent;
import org.apache.camel.spi.ManagementMBeanAssembler;
import org.apache.camel.support.ServiceSupport;
import org.apache.camel.util.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of the Camel JMX service agent
*/
public class DefaultManagementAgent extends ServiceSupport implements ManagementAgent, CamelContextAware {
public static final String DEFAULT_DOMAIN = "org.apache.camel";
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_REGISTRY_PORT = 1099;
public static final int DEFAULT_CONNECTION_PORT = -1;
public static final String DEFAULT_SERVICE_URL_PATH = "/jmxrmi/camel";
private static final Logger LOG = LoggerFactory.getLogger(DefaultManagementAgent.class);
private CamelContext camelContext;
private MBeanServer server;
// need a name -> actual name mapping as some servers changes the names (such as WebSphere)
private final ConcurrentMap mbeansRegistered = new ConcurrentHashMap();
private JMXConnectorServer cs;
private Registry registry;
private Integer registryPort = DEFAULT_REGISTRY_PORT;
private Integer connectorPort = DEFAULT_CONNECTION_PORT;
private String mBeanServerDefaultDomain = DEFAULT_DOMAIN;
private String mBeanObjectDomainName = DEFAULT_DOMAIN;
private String serviceUrlPath = DEFAULT_SERVICE_URL_PATH;
private Boolean usePlatformMBeanServer = true;
private Boolean createConnector = false;
private Boolean onlyRegisterProcessorWithCustomId = false;
private Boolean loadStatisticsEnabled = false;
private Boolean endpointRuntimeStatisticsEnabled = true;
private Boolean registerAlways = false;
private Boolean registerNewRoutes = true;
private Boolean mask = true;
private Boolean includeHostName = false;
private Boolean useHostIPAddress = false;
private String managementNamePattern = "#name#";
private ManagementStatisticsLevel statisticsLevel = ManagementStatisticsLevel.Default;
public DefaultManagementAgent() {
}
public DefaultManagementAgent(CamelContext camelContext) {
this.camelContext = camelContext;
}
protected void finalizeSettings() throws Exception {
// JVM system properties take precedence over any configuration
Map values = new LinkedHashMap();
if (System.getProperty(JmxSystemPropertyKeys.REGISTRY_PORT) != null) {
registryPort = Integer.getInteger(JmxSystemPropertyKeys.REGISTRY_PORT);
values.put(JmxSystemPropertyKeys.REGISTRY_PORT, registryPort);
}
if (System.getProperty(JmxSystemPropertyKeys.CONNECTOR_PORT) != null) {
connectorPort = Integer.getInteger(JmxSystemPropertyKeys.CONNECTOR_PORT);
values.put(JmxSystemPropertyKeys.CONNECTOR_PORT, connectorPort);
}
if (System.getProperty(JmxSystemPropertyKeys.DOMAIN) != null) {
mBeanServerDefaultDomain = System.getProperty(JmxSystemPropertyKeys.DOMAIN);
values.put(JmxSystemPropertyKeys.DOMAIN, mBeanServerDefaultDomain);
}
if (System.getProperty(JmxSystemPropertyKeys.MBEAN_DOMAIN) != null) {
mBeanObjectDomainName = System.getProperty(JmxSystemPropertyKeys.MBEAN_DOMAIN);
values.put(JmxSystemPropertyKeys.MBEAN_DOMAIN, mBeanObjectDomainName);
}
if (System.getProperty(JmxSystemPropertyKeys.SERVICE_URL_PATH) != null) {
serviceUrlPath = System.getProperty(JmxSystemPropertyKeys.SERVICE_URL_PATH);
values.put(JmxSystemPropertyKeys.SERVICE_URL_PATH, serviceUrlPath);
}
if (System.getProperty(JmxSystemPropertyKeys.CREATE_CONNECTOR) != null) {
createConnector = Boolean.getBoolean(JmxSystemPropertyKeys.CREATE_CONNECTOR);
values.put(JmxSystemPropertyKeys.CREATE_CONNECTOR, createConnector);
}
if (System.getProperty(JmxSystemPropertyKeys.ONLY_REGISTER_PROCESSOR_WITH_CUSTOM_ID) != null) {
onlyRegisterProcessorWithCustomId = Boolean.getBoolean(JmxSystemPropertyKeys.ONLY_REGISTER_PROCESSOR_WITH_CUSTOM_ID);
values.put(JmxSystemPropertyKeys.ONLY_REGISTER_PROCESSOR_WITH_CUSTOM_ID, onlyRegisterProcessorWithCustomId);
}
if (System.getProperty(JmxSystemPropertyKeys.USE_PLATFORM_MBS) != null) {
usePlatformMBeanServer = Boolean.getBoolean(JmxSystemPropertyKeys.USE_PLATFORM_MBS);
values.put(JmxSystemPropertyKeys.USE_PLATFORM_MBS, usePlatformMBeanServer);
}
if (System.getProperty(JmxSystemPropertyKeys.REGISTER_ALWAYS) != null) {
registerAlways = Boolean.getBoolean(JmxSystemPropertyKeys.REGISTER_ALWAYS);
values.put(JmxSystemPropertyKeys.REGISTER_ALWAYS, registerAlways);
}
if (System.getProperty(JmxSystemPropertyKeys.REGISTER_NEW_ROUTES) != null) {
registerNewRoutes = Boolean.getBoolean(JmxSystemPropertyKeys.REGISTER_NEW_ROUTES);
values.put(JmxSystemPropertyKeys.REGISTER_NEW_ROUTES, registerNewRoutes);
}
if (System.getProperty(JmxSystemPropertyKeys.MASK) != null) {
mask = Boolean.getBoolean(JmxSystemPropertyKeys.MASK);
values.put(JmxSystemPropertyKeys.MASK, mask);
}
if (System.getProperty(JmxSystemPropertyKeys.INCLUDE_HOST_NAME) != null) {
includeHostName = Boolean.getBoolean(JmxSystemPropertyKeys.INCLUDE_HOST_NAME);
values.put(JmxSystemPropertyKeys.INCLUDE_HOST_NAME, includeHostName);
}
if (System.getProperty(JmxSystemPropertyKeys.CREATE_CONNECTOR) != null) {
createConnector = Boolean.getBoolean(JmxSystemPropertyKeys.CREATE_CONNECTOR);
values.put(JmxSystemPropertyKeys.CREATE_CONNECTOR, createConnector);
}
if (System.getProperty(JmxSystemPropertyKeys.LOAD_STATISTICS_ENABLED) != null) {
loadStatisticsEnabled = Boolean.getBoolean(JmxSystemPropertyKeys.LOAD_STATISTICS_ENABLED);
values.put(JmxSystemPropertyKeys.LOAD_STATISTICS_ENABLED, loadStatisticsEnabled);
}
if (System.getProperty(JmxSystemPropertyKeys.ENDPOINT_RUNTIME_STATISTICS_ENABLED) != null) {
endpointRuntimeStatisticsEnabled = Boolean.getBoolean(JmxSystemPropertyKeys.ENDPOINT_RUNTIME_STATISTICS_ENABLED);
values.put(JmxSystemPropertyKeys.ENDPOINT_RUNTIME_STATISTICS_ENABLED, endpointRuntimeStatisticsEnabled);
}
if (System.getProperty(JmxSystemPropertyKeys.STATISTICS_LEVEL) != null) {
statisticsLevel = camelContext.getTypeConverter().mandatoryConvertTo(ManagementStatisticsLevel.class, System.getProperty(JmxSystemPropertyKeys.STATISTICS_LEVEL));
values.put(JmxSystemPropertyKeys.STATISTICS_LEVEL, statisticsLevel);
}
if (System.getProperty(JmxSystemPropertyKeys.MANAGEMENT_NAME_PATTERN) != null) {
managementNamePattern = System.getProperty(JmxSystemPropertyKeys.MANAGEMENT_NAME_PATTERN);
values.put(JmxSystemPropertyKeys.MANAGEMENT_NAME_PATTERN, managementNamePattern);
}
if (System.getProperty(JmxSystemPropertyKeys.USE_HOST_IP_ADDRESS) != null) {
useHostIPAddress = Boolean.getBoolean(JmxSystemPropertyKeys.USE_HOST_IP_ADDRESS);
values.put(JmxSystemPropertyKeys.USE_HOST_IP_ADDRESS, useHostIPAddress);
}
if (!values.isEmpty()) {
LOG.info("ManagementAgent detected JVM system properties: {}", values);
}
}
public void setRegistryPort(Integer port) {
registryPort = port;
}
public Integer getRegistryPort() {
return registryPort;
}
public void setConnectorPort(Integer port) {
connectorPort = port;
}
public Integer getConnectorPort() {
return connectorPort;
}
public void setMBeanServerDefaultDomain(String domain) {
mBeanServerDefaultDomain = domain;
}
public String getMBeanServerDefaultDomain() {
return mBeanServerDefaultDomain;
}
public void setMBeanObjectDomainName(String domainName) {
mBeanObjectDomainName = domainName;
}
public String getMBeanObjectDomainName() {
return mBeanObjectDomainName;
}
public void setServiceUrlPath(String url) {
serviceUrlPath = url;
}
public String getServiceUrlPath() {
return serviceUrlPath;
}
public void setCreateConnector(Boolean flag) {
createConnector = flag;
}
public Boolean getCreateConnector() {
return createConnector;
}
public void setUsePlatformMBeanServer(Boolean flag) {
usePlatformMBeanServer = flag;
}
public Boolean getUsePlatformMBeanServer() {
return usePlatformMBeanServer;
}
public Boolean getOnlyRegisterProcessorWithCustomId() {
return onlyRegisterProcessorWithCustomId;
}
public void setOnlyRegisterProcessorWithCustomId(Boolean onlyRegisterProcessorWithCustomId) {
this.onlyRegisterProcessorWithCustomId = onlyRegisterProcessorWithCustomId;
}
public void setMBeanServer(MBeanServer mbeanServer) {
server = mbeanServer;
}
public MBeanServer getMBeanServer() {
return server;
}
public Boolean getRegisterAlways() {
return registerAlways != null && registerAlways;
}
public void setRegisterAlways(Boolean registerAlways) {
this.registerAlways = registerAlways;
}
public Boolean getRegisterNewRoutes() {
return registerNewRoutes != null && registerNewRoutes;
}
public void setRegisterNewRoutes(Boolean registerNewRoutes) {
this.registerNewRoutes = registerNewRoutes;
}
public Boolean getMask() {
return mask != null && mask;
}
public void setMask(Boolean mask) {
this.mask = mask;
}
public Boolean getIncludeHostName() {
return includeHostName != null && includeHostName;
}
public void setIncludeHostName(Boolean includeHostName) {
this.includeHostName = includeHostName;
}
public Boolean getUseHostIPAddress() {
return useHostIPAddress != null && useHostIPAddress;
}
public void setUseHostIPAddress(Boolean useHostIPAddress) {
this.useHostIPAddress = useHostIPAddress;
}
public String getManagementNamePattern() {
return managementNamePattern;
}
public void setManagementNamePattern(String managementNamePattern) {
this.managementNamePattern = managementNamePattern;
}
public Boolean getLoadStatisticsEnabled() {
return loadStatisticsEnabled;
}
public void setLoadStatisticsEnabled(Boolean loadStatisticsEnabled) {
this.loadStatisticsEnabled = loadStatisticsEnabled;
}
public Boolean getEndpointRuntimeStatisticsEnabled() {
return endpointRuntimeStatisticsEnabled;
}
public void setEndpointRuntimeStatisticsEnabled(Boolean endpointRuntimeStatisticsEnabled) {
this.endpointRuntimeStatisticsEnabled = endpointRuntimeStatisticsEnabled;
}
public ManagementStatisticsLevel getStatisticsLevel() {
return statisticsLevel;
}
public void setStatisticsLevel(ManagementStatisticsLevel statisticsLevel) {
this.statisticsLevel = statisticsLevel;
}
public CamelContext getCamelContext() {
return camelContext;
}
public void setCamelContext(CamelContext camelContext) {
this.camelContext = camelContext;
}
public void register(Object obj, ObjectName name) throws JMException {
register(obj, name, false);
}
public void register(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
try {
registerMBeanWithServer(obj, name, forceRegistration);
} catch (NotCompliantMBeanException e) {
// If this is not a "normal" MBean, then try to deploy it using JMX annotations
ManagementMBeanAssembler assembler = camelContext.getManagementMBeanAssembler();
ObjectHelper.notNull(assembler, "ManagementMBeanAssembler", camelContext);
Object mbean = assembler.assemble(server, obj, name);
if (mbean != null) {
// and register the mbean
registerMBeanWithServer(mbean, name, forceRegistration);
}
}
}
public void unregister(ObjectName name) throws JMException {
if (isRegistered(name)) {
ObjectName on = mbeansRegistered.remove(name);
server.unregisterMBean(on);
LOG.debug("Unregistered MBean with ObjectName: {}", name);
} else {
mbeansRegistered.remove(name);
}
}
public boolean isRegistered(ObjectName name) {
if (server == null) {
return false;
}
ObjectName on = mbeansRegistered.get(name);
return (on != null && server.isRegistered(on))
|| server.isRegistered(name);
}
public T newProxyClient(ObjectName name, Class mbean) {
if (isRegistered(name)) {
ObjectName on = mbeansRegistered.get(name);
return MBeanServerInvocationHandler.newProxyInstance(server, on != null ? on : name, mbean, false);
} else {
return null;
}
}
protected void doStart() throws Exception {
ObjectHelper.notNull(camelContext, "CamelContext");
// create mbean server if is has not be injected.
if (server == null) {
finalizeSettings();
createMBeanServer();
}
LOG.debug("Starting JMX agent on server: {}", getMBeanServer());
}
protected void doStop() throws Exception {
// close JMX Connector, if it was created
if (cs != null) {
try {
cs.stop();
LOG.debug("Stopped JMX Connector");
} catch (IOException e) {
LOG.debug("Error occurred during stopping JMXConnectorService: "
+ cs + ". This exception will be ignored.");
}
cs = null;
}
// Unexport JMX RMI registry, if it was created
if (registry != null) {
try {
UnicastRemoteObject.unexportObject(registry, true);
LOG.debug("Unexported JMX RMI Registry");
} catch (NoSuchObjectException e) {
LOG.debug("Error occurred while unexporting JMX RMI registry. This exception will be ignored.");
}
}
if (mbeansRegistered.isEmpty()) {
return;
}
// Using the array to hold the busMBeans to avoid the CurrentModificationException
ObjectName[] mBeans = mbeansRegistered.keySet().toArray(new ObjectName[mbeansRegistered.size()]);
int caught = 0;
for (ObjectName name : mBeans) {
try {
unregister(name);
} catch (Exception e) {
LOG.info("Exception unregistering MBean with name " + name, e);
caught++;
}
}
if (caught > 0) {
LOG.warn("A number of " + caught
+ " exceptions caught while unregistering MBeans during stop operation."
+ " See INFO log for details.");
}
}
private void registerMBeanWithServer(Object obj, ObjectName name, boolean forceRegistration)
throws JMException {
// have we already registered the bean, there can be shared instances in the camel routes
boolean exists = isRegistered(name);
if (exists) {
if (forceRegistration) {
LOG.info("ForceRegistration enabled, unregistering existing MBean with ObjectName: {}", name);
server.unregisterMBean(name);
} else {
// okay ignore we do not want to force it and it could be a shared instance
LOG.debug("MBean already registered with ObjectName: {}", name);
}
}
// register bean if by force or not exists
ObjectInstance instance = null;
if (forceRegistration || !exists) {
LOG.trace("Registering MBean with ObjectName: {}", name);
instance = server.registerMBean(obj, name);
}
// need to use the name returned from the server as some JEE servers may modify the name
if (instance != null) {
ObjectName registeredName = instance.getObjectName();
LOG.debug("Registered MBean with ObjectName: {}", registeredName);
mbeansRegistered.put(name, registeredName);
}
}
protected void createMBeanServer() {
String hostName;
boolean canAccessSystemProps = true;
try {
// we'll do it this way mostly to determine if we should lookup the hostName
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPropertiesAccess();
}
} catch (SecurityException se) {
canAccessSystemProps = false;
}
if (canAccessSystemProps) {
try {
if (useHostIPAddress) {
hostName = InetAddress.getLocalHost().getHostAddress();
} else {
hostName = InetAddress.getLocalHost().getHostName();
}
} catch (UnknownHostException uhe) {
LOG.info("Cannot determine localhost name or address. Using default: " + DEFAULT_REGISTRY_PORT, uhe);
hostName = DEFAULT_HOST;
}
} else {
hostName = DEFAULT_HOST;
}
server = findOrCreateMBeanServer();
try {
// Create the connector if we need
if (createConnector) {
createJmxConnector(hostName);
}
} catch (IOException ioe) {
LOG.warn("Could not create and start JMX connector.", ioe);
}
}
protected MBeanServer findOrCreateMBeanServer() {
// return platform mbean server if the option is specified.
if (usePlatformMBeanServer) {
return ManagementFactory.getPlatformMBeanServer();
}
// look for the first mbean server that has match default domain name
List servers = MBeanServerFactory.findMBeanServer(null);
for (MBeanServer server : servers) {
LOG.debug("Found MBeanServer with default domain {}", server.getDefaultDomain());
if (mBeanServerDefaultDomain.equals(server.getDefaultDomain())) {
return server;
}
}
// create a mbean server with the given default domain name
return MBeanServerFactory.createMBeanServer(mBeanServerDefaultDomain);
}
protected void createJmxConnector(String host) throws IOException {
ObjectHelper.notEmpty(serviceUrlPath, "serviceUrlPath");
ObjectHelper.notNull(registryPort, "registryPort");
try {
registry = LocateRegistry.createRegistry(registryPort);
LOG.debug("Created JMXConnector RMI registry on port {}", registryPort);
} catch (RemoteException ex) {
// The registry may had been created, we could get the registry instead
}
// must start with leading slash
String path = serviceUrlPath.startsWith("/") ? serviceUrlPath : "/" + serviceUrlPath;
// Create an RMI connector and start it
final JMXServiceURL url;
if (connectorPort > 0) {
url = new JMXServiceURL("service:jmx:rmi://" + host + ":" + connectorPort + "/jndi/rmi://" + host
+ ":" + registryPort + path);
} else {
url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + registryPort + path);
}
cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
// use async thread for starting the JMX Connector
// (no need to use a thread pool or enlist in JMX as this thread is terminated when the JMX connector has been started)
String threadName = camelContext.getExecutorServiceManager().resolveThreadName("JMXConnector: " + url);
Thread thread = getCamelContext().getExecutorServiceManager().newThread(threadName, new Runnable() {
public void run() {
try {
LOG.debug("Staring JMX Connector thread to listen at: {}", url);
cs.start();
LOG.info("JMX Connector thread started and listening at: {}", url);
} catch (IOException ioe) {
LOG.warn("Could not start JMXConnector thread at: " + url + ". JMX Connector not in use.", ioe);
}
}
});
thread.start();
}
}