/* Licensed 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.activiti.management.jmx;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.List;
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.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.activiti.management.jmx.mbeans.JobExecutorMBean;
import org.activiti.management.jmx.mbeans.ProcessDefinitionsMBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Saeid Mirzaei
*/
public class DefaultManagementAgent implements ManagementAgent {
private static final Logger LOG = LoggerFactory.getLogger(DefaultManagementAgent.class);
protected MBeanServer server;
protected final ConcurrentMap<ObjectName, ObjectName> mbeansRegistered = new ConcurrentHashMap<ObjectName, ObjectName>();
protected JMXConfigurator jmxConfigurator;
protected Registry registry;
protected JMXConnectorServer cs;
protected ManagementMBeanAssembler assembler;
public DefaultManagementAgent(JMXConfigurator jmxConfigurator) {
this.jmxConfigurator = jmxConfigurator;
this.assembler = new DefaultManagementMBeanAssembler();
}
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 {
Object mbean = assembler.assemble(obj, name);
if (mbean != null)
// and register the mbean
registerMBeanWithServer(mbean, name, forceRegistration);
else
registerMBeanWithServer(obj, name, forceRegistration);
} catch (NotCompliantMBeanException e) {
LOG.error("Mbean " + name + " is not compliant MBean.", e);
registerMBeanWithServer(obj, name, forceRegistration);
}
}
private void registerMBeanWithServer(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
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);
}
}
public boolean isRegistered(ObjectName name) {
ObjectName on = mbeansRegistered.get(name);
return (on != null && server.isRegistered(on)) || server.isRegistered(name);
}
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);
}
}
@Override
public MBeanServer getMBeanServer() {
return server;
}
@Override
public void setMBeanServer(MBeanServer mbeanServer) {
this.server = mbeanServer;
}
public void doStart() {
createMBeanServer();
}
protected void createMBeanServer() {
server = findOrCreateMBeanServer();
try {
// Create the connector if we need
if (jmxConfigurator.getCreateConnector()) {
createJmxConnector(Utils.getHostName());
}
} catch (IOException ioe) {
LOG.warn("Could not create and start JMX connector.", ioe);
}
}
protected MBeanServer findOrCreateMBeanServer() {
// look for the first mbean server that has match default domain name
if (jmxConfigurator.getMbeanDomain().equals(JMXConfigurator.DEFAUL_JMX_DOMAIN))
return ManagementFactory.getPlatformMBeanServer();
List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
for (MBeanServer server : servers) {
LOG.debug("Found MBeanServer with default domain {}", server.getDefaultDomain());
System.out.println(server.getDefaultDomain());
if (jmxConfigurator.getMbeanDomain().equals(server.getDefaultDomain())) {
return server;
}
}
// create a mbean server with the given default domain name
return MBeanServerFactory.createMBeanServer(jmxConfigurator.getMbeanDomain());
}
@Override
public void findAndRegisterMbeans() throws Exception {
register(new ProcessDefinitionsMBean(jmxConfigurator.getProcessEngineConfig()), new ObjectName(jmxConfigurator.getDomain(), "type", "Deployments"));
register(new JobExecutorMBean(jmxConfigurator.getProcessEngineConfig()), new ObjectName(jmxConfigurator.getDomain(), "type", "JobExecutor"));
}
public void createJmxConnector(String host) throws IOException {
String serviceUrlPath = jmxConfigurator.getServiceUrlPath();
Integer registryPort = jmxConfigurator.getRegistryPort();
Integer connectorPort = jmxConfigurator.getConnectorPort();
if (serviceUrlPath == null) {
LOG.warn("Service url path is null. JMX connector creation skipped");
return;
}
if (registryPort == null) {
LOG.warn("Registery port is null. JMX connector creation skipped.");
return;
}
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)
Thread thread = new Thread(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) {
if (ioe.getCause() instanceof javax.naming.NameAlreadyBoundException) {
LOG.warn("JMX connection:" + url + " already exists.");
} else {
LOG.warn("Could not start JMXConnector thread at: " + url + ". JMX Connector not in use.", ioe);
}
}
}
}, "jmxConnectorStarterThread");
thread.start();
}
}