/**
* <a href="http://servicemix.org">ServiceMix: The open source ESB</a>
*
* Copyright 2005 RAJD Consultancy Ltd
*
* 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.servicemix.jbi.management;
import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.servicemix.jbi.container.EnvironmentContext;
import org.servicemix.jbi.container.JBIContainer;
import org.servicemix.jbi.framework.ComponentMBeanImpl;
import javax.jbi.JBIException;
import javax.management.Attribute;
import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.registry.LocateRegistry;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* A Flow provides different dispatch policies within the NMR
*
* @version $Revision: 738 $
*/
public class ManagementContext extends BaseLifeCycle implements ManagementContextMBean {
/**
* Default servicemix domain
*/
public static final String DEFAULT_DOMAIN = "org.servicemix";
private final static Log log = LogFactory.getLog(ManagementContext.class);
private JBIContainer container;
private MBeanServer beanServer;
private int namingPort = 1099;
private String jndiPath = "/jmxconnector";
private JMXConnectorServer connectorServer;
private String jmxDomainName = DEFAULT_DOMAIN;
private Map beanMap = new ConcurrentHashMap();
protected Map systemServices = new ConcurrentHashMap();
private boolean useMBeanServer = true;
private boolean createMBeanServer = false;
private boolean locallyCreateMBeanServer = false;
/**
* Default Constructor
*/
public ManagementContext() {
}
/**
* Get the Description of the item
*
* @return the description
*/
public String getDescription() {
return "JMX Management";
}
/**
* @return Returns the jmxDomainName.
*/
public String getJmxDomainName() {
return jmxDomainName;
}
/**
* @param jmxDomainName The jmxDomainName to set.
*/
public void setJmxDomainName(String jmxDomainName) {
this.jmxDomainName = jmxDomainName;
}
/**
* Get the MBeanServer
*
* @return the MBeanServer
*/
public MBeanServer getMBeanServer() {
return beanServer;
}
/**
* @return Returns the namingPort.
*/
public int getNamingPort() {
return namingPort;
}
/**
* @param namingPort The namingPort to set.
*/
public void setNamingPort(int namingPort) {
this.namingPort = namingPort;
}
/**
* @return Returns the jndiPath.
*/
public String getJndiPath() {
return jndiPath;
}
/**
* @param jndiPath The jndiPath to set.
*/
public void setJndiPath(String jndiPath) {
this.jndiPath = jndiPath;
}
/**
* @return Returns the useMBeanServer.
*/
public boolean isUseMBeanServer() {
return useMBeanServer;
}
/**
* @param useMBeanServer The useMBeanServer to set.
*/
public void setUseMBeanServer(boolean useMBeanServer) {
this.useMBeanServer = useMBeanServer;
}
/**
* @return Returns the createMBeanServer flag.
*/
public boolean isCreateMBeanServer() {
return createMBeanServer;
}
/**
* @param enableJMX Set createMBeanServer.
*/
public void setCreateMBeanServer(boolean enableJMX) {
this.createMBeanServer = enableJMX;
}
/**
* Initialize the ManagementContext
*
* @param container
* @param server
* @throws JBIException
*/
public void init(JBIContainer container, MBeanServer server) throws JBIException {
this.container = container;
jndiPath = "/" + container.getName() + "JMX";
this.beanServer = server != null ? server : findMBeanServer();
// register self as a System service
registerSystemService(this, ManagementContextMBean.class);
}
/**
* Start the item.
*
* @exception JBIException if the item fails to start.
*/
public void start() throws JBIException {
super.start();
}
/**
* Stop the item. This suspends current messaging activities.
*
* @exception JBIException if the item fails to stop.
*/
public void stop() throws JBIException {
super.stop();
}
/**
* Shut down the item. The releases resources, preparatory to uninstallation.
*
* @exception JBIException if the item fails to shut down.
*/
public void shutDown() throws JBIException {
super.shutDown();
// Unregister all mbeans
Object[] beans = beanMap.keySet().toArray();
for (int i = 0; i < beans.length; i++) {
try {
unregisterMBean(beans[i]);
} catch (Exception e) {
log.debug("Could not unregister mbean", e);
}
}
if (connectorServer != null) {
try {
connectorServer.stop();
}
catch (IOException e) {
log.error("Problem stopping the JMX ConnectorServer", e);
}
}
if (locallyCreateMBeanServer && beanServer != null) {
try {
beanServer.invoke(ObjectName.getInstance("naming:type=rmiregistry"), "stop", null, null);
} catch (Exception e) {
log.debug("Could not stop naming service", e);
}
// check to see if the factory knows about this server
List list = MBeanServerFactory.findMBeanServer(null);
if (list != null && !list.isEmpty() && list.contains(beanServer)) {
MBeanServerFactory.releaseMBeanServer(beanServer);
}
}
}
/**
* Get a list of all binding components currently installed.
*
* @return array of JMX object names of all installed BCs.
*/
public ObjectName[] getBindingComponents() {
return container.getRegistry().getBindingComponents();
}
/**
* Lookup a JBI Installable Component by its unique name.
*
* @param name - is the name of the BC or SE.
* @return the JMX object name of the component's LifeCycle MBean or null.
*/
public ObjectName getComponentByName(String name) {
return container.getRegistry().getComponentObjectName(name);
}
/**
* Get a list of all engines currently installed.
*
* @return array of JMX object names of all installed SEs.
*/
public ObjectName[] getEngineComponents() {
return container.getRegistry().getEngineComponents();
}
/**
* Return current version and other info about this JBI Framework.
*
* @return info String
*/
public String getSystemInfo() {
return "ServiceMix JBI Container: version: " + EnvironmentContext.getVersion();
}
/**
* Lookup a system service by name.
*
* @param serviceName - is the name of the system service
* @return the JMX object name of the service or null
*/
public ObjectName getSystemService(String serviceName) {
return (ObjectName) systemServices.get(serviceName);
}
/**
* Looks up all JBI Framework System Services currently installed.
*
* @return array of JMX object names of system services
*/
public ObjectName[] getSystemServices() {
ObjectName[] result = null;
Collection col = systemServices.values();
result = new ObjectName[col.size()];
col.toArray(result);
return result;
}
/**
* Check if a given JBI Installable Component is a Binding Component.
*
* @param componentName - the unique name of the component
* @return true if the component is a binding
*/
public boolean isBinding(String componentName) {
return container.getRegistry().isBinding(componentName);
}
/**
* Check if a given JBI Component is a service engine.
*
* @param componentName - the unique name of the component
* @return true if the component is a service engine
*/
public boolean isEngine(String componentName) {
return container.getRegistry().isEngine(componentName);
}
/**
* Start a Component
*
* @param componentName
* @return the status
* @throws JBIException
*/
public String startComponent(String componentName) throws JBIException {
String result = "NOT FOUND: " + componentName;
ObjectName objName = getComponentByName(componentName);
if (objName != null) {
ComponentMBeanImpl mbean = (ComponentMBeanImpl) beanMap.get(objName);
if (mbean != null) {
mbean.start();
result = mbean.getCurrentState();
}
}
return result;
}
/**
* Stop a Component
*
* @param componentName
* @return the status
* @throws JBIException
*/
public String stopComponent(String componentName) throws JBIException {
String result = "NOT FOUND: " + componentName;
ObjectName objName = getComponentByName(componentName);
if (objName != null) {
ComponentMBeanImpl mbean = (ComponentMBeanImpl) beanMap.get(objName);
if (mbean != null) {
mbean.stop();
result = mbean.getCurrentState();
}
}
return result;
}
/**
* Shutdown a Component
*
* @param componentName
* @return the status
* @throws JBIException
*/
public String shutDownComponent(String componentName) throws JBIException {
String result = "NOT FOUND: " + componentName;
ObjectName objName = getComponentByName(componentName);
if (objName != null) {
ComponentMBeanImpl mbean = (ComponentMBeanImpl) beanMap.get(objName);
if (mbean != null) {
mbean.shutDown();
result = mbean.getCurrentState();
}
}
return result;
}
/**
* Formulate and return the MBean ObjectName of a custom control MBean for a JBI component.
*
* @param type
* @param name
* @return the JMX ObjectName of the MBean, or <code>null</code> if <code>customName</code> is invalid.
*/
public ObjectName createCustomComponentMBeanName(String type, String name) {
ObjectName result = null;
String tmp = jmxDomainName + ":" + "type=" + sanitizeString(type) + ",name=" + sanitizeString(name);
try {
result = new ObjectName(tmp);
}
catch (MalformedObjectNameException e) {
log.error("Couldn't create ObjectName from: " + type + " , " + name);
}
return result;
}
/**
* Create an ObjectName
*
* @param provider
* @return the ObjectName
*/
public ObjectName createObjectName(MBeanInfoProvider provider) {
ObjectName result = null;
try {
String tmp = jmxDomainName + ":" + "type="
+ (provider.getClass().getName() + ",name=" + getRelativeName(provider));
result = new ObjectName(tmp);
}
catch (MalformedObjectNameException e) {
// shouldn't happen
String error = "Could not create ObjectName for " + provider.getClass() + ", " + provider.getName();
log.error(error, e);
throw new RuntimeException(error);
}
return result;
}
/**
* Get a qualified name
*
* @param provider
* @return the name
*/
public String getRelativeName(MBeanInfoProvider provider) {
return sanitizeString(container.getName() + "." + provider.getName());
}
/**
* The ':' and '/' characters are reserved in ObjectNames
*
* @param in
* @return sanitized String
*/
private static String sanitizeString(String in) {
String result = null;
if (in != null) {
result = in.replace(':', '_');
result = result.replace('/', '_');
result = result.replace('\\', '_');
}
return result;
}
/**
* Register an MBean
*
* @param resource
* @param name
* @param interfaceMBean
* @throws JMException
*/
public void registerMBean(ObjectName name, MBeanInfoProvider resource, Class interfaceMBean) throws JMException {
registerMBean(name, resource, interfaceMBean, resource.getDescription());
}
/**
* Register an MBean
*
* @param resource
* @param name
* @param interfaceMBean
* @param description
* @throws JMException
*/
public void registerMBean(ObjectName name, Object resource, Class interfaceMBean, String description)
throws JMException {
if (beanServer != null) {
Object mbean = MBeanBuilder.buildStandardMBean(resource, interfaceMBean, description);
if (!beanServer.isRegistered(name)) {
beanServer.registerMBean(mbean, name);
beanMap.put(resource, name);
}
}
}
/**
* Retrive an System ObjectName
*
* @param domainName
* @param containerName
* @param theClass
* @return the ObjectName
*/
public static ObjectName getSystemObjectName(String domainName, String containerName, Class theClass) {
String tmp = domainName + ":" + "type=" + theClass.getName() + ",name="
+ getRelativeName(containerName, theClass);
ObjectName result = null;
try {
result = new ObjectName(tmp);
}
catch (MalformedObjectNameException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (NullPointerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
private static String getRelativeName(String containerName, Class theClass) {
String name = theClass.getName();
int index = name.lastIndexOf(".");
if (index >= 0 && (index + 1) < name.length()) {
name = name.substring(index + 1);
}
return containerName + "." + name;
}
/**
* Register a System service
*
* @param service
* @param implementationType
* @throws JBIException
*/
public void registerSystemService(BaseLifeCycle service, Class implementationType) throws JBIException {
registerSystemService(service, implementationType, getRelativeName(service));
}
/**
* Register a System service
*
* @param service
* @param implementationType
* @param name
* @throws JBIException
*/
public void registerSystemService(BaseLifeCycle service, Class implementationType, String name) throws JBIException {
String tmp = jmxDomainName + ":" + "type=" + service.getClass().getName() + ",name=" + name;
try {
ObjectName objName = new ObjectName(tmp);
registerMBean(objName, service, implementationType, service.getDescription());
systemServices.put(name, objName);
}
catch (MalformedObjectNameException e) {
throw new JBIException(e);
}
catch (JMException e) {
throw new JBIException(e);
}
}
/**
* Unregister an MBean
*
* @param name
* @throws JMException
*/
public void unregisterMBean(ObjectName name) throws JBIException {
try {
if (beanServer != null && beanServer.isRegistered(name)) {
beanServer.unregisterMBean(name);
for (Iterator i = beanMap.entrySet().iterator();i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
if (entry.getValue().equals(name)) {
beanMap.remove(entry.getKey());
break;
}
}
}
} catch (JMException e) {
throw new JBIException("Could not unregister mbean", e);
}
}
/**
* Unregister an MBean
*
* @param bean
* @throws JMException
*/
public void unregisterMBean(Object bean) throws JBIException {
try {
ObjectName name = (ObjectName) beanMap.remove(bean);
if (name != null && beanServer != null) {
beanServer.unregisterMBean(name);
}
} catch (JMException e) {
throw new JBIException("Could not unregister mbean", e);
}
}
protected synchronized MBeanServer findMBeanServer() {
MBeanServer result = null;
// create the mbean server and start an rmi connector
if (useMBeanServer) {
try {
// lets piggy back on another MBeanServer - we could be in an appserver!
List list = MBeanServerFactory.findMBeanServer(null);
if (list != null && list.size() > 0) {
result = (MBeanServer) list.get(0);
}
if (result == null && createMBeanServer) {
result = MBeanServerFactory.createMBeanServer(jmxDomainName);
locallyCreateMBeanServer = true;
// Register and start the rmiregistry MBean, needed by JSR 160 RMIConnectorServer
ObjectName namingName = ObjectName.getInstance("naming:type=rmiregistry");
if (!result.isRegistered(namingName)) {
try {
// Do not use the createMBean as the mx4j jar may not be in the
// same class loader than the server
Class cl = Class.forName("mx4j.tools.naming.NamingService");
result.registerMBean(cl.newInstance(), namingName);
//result.createMBean("mx4j.tools.naming.NamingService", namingName, null);
// set the naming port
Attribute attr = new Attribute("Port", new Integer(namingPort));
result.setAttribute(namingName, attr);
result.invoke(namingName, "start", null, null);
}
catch (Exception e) {
// could already be in use
log.debug("Could not start naming service", e);
// Now, make sure a registry is loaded
try {
LocateRegistry.createRegistry(namingPort);
}
catch (Throwable t) {
// proably exists already
log.debug("Failed to create local registry", t);
}
}
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + namingPort
+ jndiPath);
// Create and start the RMIConnectorServer
connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, result);
connectorServer.start();
}
}
}
catch (NoClassDefFoundError e) {
log.error("Could not load MBeanServer", e);
}
catch (JMException e) {
log.error("Could not start the remote: JMX ConnectorServer", e);
}
catch (MalformedURLException e) {
log.error("Bad URL:", e);
}
catch (IOException e) {
log.error("Could not start the remote: JMX ConnectorServer", e);
}
catch (Throwable e) {
// probably don't have access to system properties
log.error("Failed to initialize MBeanServer", e);
}
}
return result;
}
/**
* Get an array of MBeanOperationInfo
*
* @return array of OperationInfos
* @throws JMException
*/
public MBeanAttributeInfo[] getAttributeInfos() throws JMException {
AttributeInfoHelper helper = new AttributeInfoHelper();
helper.addAttribute(getObjectToManage(), "bindingComponents", "Get list of all binding components");
helper.addAttribute(getObjectToManage(), "engineComponents", "Get list of all engine components");
helper.addAttribute(getObjectToManage(), "systemInfo", "Return current version");
helper.addAttribute(getObjectToManage(), "systemServices", "Get list of system services");
return AttributeInfoHelper.join(super.getAttributeInfos(), helper.getAttributeInfos());
}
public MBeanOperationInfo[] getOperationInfos() throws JMException {
OperationInfoHelper helper = new OperationInfoHelper();
ParameterHelper ph = helper.addOperation(getObjectToManage(), "getComponentByName", 1, "look up Component by name");
ph.setDescription(0, "name", "Component name");
ph = helper.addOperation(getObjectToManage(), "getSystemService", 1, "look up System service by name");
ph.setDescription(0, "name", "System name");
ph = helper.addOperation(getObjectToManage(), "isBinding", 1, "Is Component a binding Component?");
ph.setDescription(0, "name", "Component name");
ph = helper.addOperation(getObjectToManage(), "isEngine", 1, "Is Component a service engine?");
ph.setDescription(0, "name", "Component name");
return OperationInfoHelper.join(super.getOperationInfos(), helper.getOperationInfos());
}
}