| /* |
| * Copyright 2002-2005 The Apache Software Foundation |
| * 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.apache.cocoon.util; |
| |
| import org.apache.avalon.framework.logger.ConsoleLogger; |
| import org.apache.avalon.framework.logger.Logger; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.cocoon.components.ComponentInfo; |
| |
| import org.apache.cocoon.core.container.CoreServiceManager; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.management.InstanceAlreadyExistsException; |
| import javax.management.MBeanRegistrationException; |
| import javax.management.MBeanServer; |
| import javax.management.MBeanServerFactory; |
| import javax.management.MalformedObjectNameException; |
| import javax.management.NotCompliantMBeanException; |
| import javax.management.ObjectInstance; |
| import javax.management.ObjectName; |
| |
| |
| /** |
| * Utility methods for JMX |
| * |
| * @version $Id$ |
| * |
| * @since 2.2 |
| */ |
| public class JMXUtils { |
| //~ Static fields/initializers ---------------------------------------------------------------------- |
| |
| /** The {@link MBeanServer} first found */ |
| private static MBeanServer mbeanServer = getInitialMBeanServer(); |
| |
| //~ Constructors ------------------------------------------------------------------------------------ |
| |
| /** |
| * Private c'tor: its a Utility class |
| */ |
| private JMXUtils() { |
| super(); |
| } |
| |
| //~ Methods ----------------------------------------------------------------------------------------- |
| |
| /** |
| * Find a JMX domain name at various places |
| * |
| * @param pJmxDomain Default JMX domain name |
| * @param serviceManager The ServiceManager used |
| * |
| * @return The JMX domain name |
| */ |
| public static String findJmxDomain(final String pJmxDomain, |
| final ServiceManager serviceManager) { |
| // try to find a JMX domain name first from this component configuration give as parameter |
| String jmxDomain = pJmxDomain; |
| |
| if(jmxDomain == null) { |
| // next from the CoreServiceManager managing this component |
| if((serviceManager != null) && serviceManager instanceof CoreServiceManager) { |
| // next from the CoreServiceManager managing this component |
| jmxDomain = ((CoreServiceManager)serviceManager).getJmxDefaultDomain(); |
| } else { |
| // otherwise use default one |
| jmxDomain = CoreServiceManager.JMX_DEFAULT_DOMAIN_NAME; |
| } |
| } |
| |
| return jmxDomain; |
| } |
| |
| /** |
| * Find or construct a JMX object name at various places (excluding the JMX domain) |
| * |
| * @param pJmxName Default JMX object name |
| * @param pClassName Class name to use if JMX object name has to be constructed |
| * |
| * @return The JMX obejct name (excluding the JMX domain name |
| */ |
| public static String findJmxName(final String pJmxName, |
| final String pClassName) { |
| String jmxName = pJmxName; |
| final String className = ((pClassName == null) ? "unknown" : pClassName); |
| |
| if(jmxName == null) { |
| // otherwise construct one from the service class name |
| final StringBuffer sb = new StringBuffer(); |
| final List groups = new ArrayList(); |
| int i = className.indexOf('.'); |
| int j = 0; |
| |
| while(i > 0) { |
| groups.add(className.substring(j, i)); |
| j = i + 1; |
| i = className.indexOf('.', i + 1); |
| } |
| |
| groups.add(className.substring(j)); |
| |
| for(i = 0; i < (groups.size() - 1); i++) { |
| sb.append("group"); |
| |
| if(i > 0) { |
| sb.append(i); |
| } |
| |
| sb.append('='); |
| sb.append(groups.get(i)); |
| sb.append(','); |
| } |
| |
| sb.append("item=").append(groups.get(groups.size() - 1)); |
| jmxName = sb.toString(); |
| } |
| |
| return jmxName; |
| } |
| |
| /** |
| * generate a default JMX object name (excluding JMX domain name) from a FQCN |
| * |
| * @param clazz The class name |
| * |
| * @return The generated JMX object name (excluding JMX domain name) |
| */ |
| public static String genDefaultJmxName(final Class clazz) { |
| return genDefaultJmxName(clazz.getName()); |
| } |
| |
| /** |
| * generate a default JMX object name (excluding JMX domain name) from a FQCN |
| * |
| * @param className The class name |
| * |
| * @return The generated JMX object name (excluding JMX domain name) |
| */ |
| public static String genDefaultJmxName(final String className) { |
| final StringBuffer nameBuf = new StringBuffer(); |
| final List groups = new ArrayList(); |
| int i = className.indexOf('.'); |
| int j = 0; |
| |
| while(i > 0) { |
| groups.add(className.substring(j, i)); |
| j = i + 1; |
| i = className.indexOf('.', i + 1); |
| } |
| |
| groups.add(className.substring(j)); |
| |
| for(i = 0; i < (groups.size() - 1); i++) { |
| nameBuf.append("group"); |
| |
| if(i > 0) { |
| nameBuf.append(i); |
| } |
| |
| nameBuf.append('='); |
| nameBuf.append(groups.get(i)); |
| nameBuf.append(','); |
| } |
| |
| nameBuf.append("item=").append(groups.get(groups.size() - 1)); |
| |
| return nameBuf.toString(); |
| } |
| |
| /** |
| * Get the ev. found {@link MBeanServer} |
| * |
| * @return DOCUMENT ME! |
| */ |
| public static MBeanServer getMBeanServer() { |
| return JMXUtils.mbeanServer; |
| } |
| |
| /** |
| * Setup a component for possible JMX managability |
| * |
| * @param bean The bean to estabilsh JMX for |
| * @param info The component info |
| * |
| * @return DOCUMENT ME! |
| */ |
| public static ObjectInstance setupJmxFor(final Object bean, |
| final ComponentInfo info) { |
| return setupJmxFor(bean, info, new ConsoleLogger(ConsoleLogger.LEVEL_INFO)); |
| } |
| |
| /** |
| * Setup a component for possible JMX managability |
| * |
| * @param bean The bean to estabilsh JMX for |
| * @param info The component info |
| * @param logger The Logger |
| */ |
| public static ObjectInstance setupJmxFor(final Object bean, |
| final ComponentInfo info, |
| final Logger logger) { |
| if(getMBeanServer() != null) { |
| final Class beanClass = bean.getClass(); |
| final String beanClassName = beanClass.getName(); |
| final String mbeanClassName = beanClassName + "MBean"; |
| ObjectName on = null; |
| |
| try { |
| // try to find a MBean for bean |
| final Class mbeanClass = beanClass.getClassLoader().loadClass(mbeanClassName); |
| Constructor ctor = null; |
| Object mbean = null; |
| |
| try { |
| ctor = mbeanClass.getConstructor(new Class[] {beanClass, ComponentInfo.class}); |
| mbean = ctor.newInstance(new Object[] {bean, info}); |
| } catch(final Exception e) { |
| // ignore this |
| } |
| |
| if(ctor == null) { |
| ctor = mbeanClass.getConstructor(new Class[] {beanClass}); |
| mbean = ctor.newInstance(new Object[] {bean}); |
| } |
| |
| // see if MBean supplies some JMX ObjectName parts |
| final String mBeanSuppliedJmxDomain = callGetter(mbean, "getJmxDomain", logger); |
| final String mBeanSuppliedJmxName = callGetter(mbean, "getJmxName", logger); |
| final String mBeanSuppliedJmxNameAdditions = |
| callGetter(mbean, "getJmxNameAddition", logger); |
| |
| // construct a JMX ObjectName instance |
| final StringBuffer objectNameBuf = new StringBuffer(); |
| |
| if(mBeanSuppliedJmxDomain != null) { |
| objectNameBuf.append(mBeanSuppliedJmxDomain); |
| } else { |
| objectNameBuf.append(info.getJmxDomain()); |
| } |
| |
| objectNameBuf.append(':'); |
| |
| if(mBeanSuppliedJmxName != null) { |
| objectNameBuf.append(mBeanSuppliedJmxName); |
| } else if(info.getConfiguration() |
| .getAttribute(CoreServiceManager.JMX_NAME_ATTR_NAME, null) != null) { |
| objectNameBuf.append(info.getConfiguration() |
| .getAttribute(CoreServiceManager.JMX_NAME_ATTR_NAME, null)); |
| } else { |
| // if we do not have the name parts we'll construct one from the bean class name |
| objectNameBuf.append(genDefaultJmxName(beanClass)); |
| } |
| |
| if(mBeanSuppliedJmxNameAdditions != null) { |
| objectNameBuf.append(','); |
| objectNameBuf.append(mBeanSuppliedJmxNameAdditions); |
| } |
| |
| on = new ObjectName(objectNameBuf.toString()); |
| |
| int instance = 1; |
| |
| while(mbeanServer.isRegistered(on)) { |
| instance++; |
| on = new ObjectName(objectNameBuf.toString() + ",instance=" + instance); |
| } |
| |
| return mbeanServer.registerMBean(mbean, on); |
| } catch(final ClassNotFoundException cnfe) { |
| // happens if a component doesn't have a MBean to support it for management |
| if(logger.isDebugEnabled()) { |
| logger.debug("Class " + beanClass.getName() + |
| " doesn't have a supporting MBean called " + mbeanClassName); |
| } |
| } catch(final NoSuchMethodException nsme) { |
| logger.warn("MBean " + mbeanClassName + |
| " doesn't have a constructor that accepts an instance of " + |
| info.getServiceClassName(), nsme); |
| } catch(final InvocationTargetException ite) { |
| logger.warn("Cannot invoke constructor on class " + mbeanClassName, ite); |
| } catch(final InstantiationException ie) { |
| logger.warn("Cannot instantiate class " + mbeanClassName, ie); |
| } catch(final IllegalAccessException iae) { |
| logger.warn("Cannot access class " + mbeanClassName, iae); |
| } catch(final MalformedObjectNameException mone) { |
| logger.warn("Invalid ObjectName '" + on + "' for MBean " + mbeanClassName, mone); |
| } catch(final InstanceAlreadyExistsException iaee) { |
| logger.warn("Instance for MBean " + mbeanClassName + "already exists", iaee); |
| } catch(final NotCompliantMBeanException ncme) { |
| logger.warn("Not compliant MBean " + mbeanClassName, ncme); |
| } catch(final MBeanRegistrationException mre) { |
| logger.warn("Cannot register MBean " + mbeanClassName, mre); |
| } catch(final SecurityException se) { |
| logger.warn("Instantiation of MBean " + mbeanClassName + |
| " is prevented by a SecurityManager", se); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Call a getter method on a bean |
| * |
| * @param mbean The bean to call a method from |
| * @param name The name of the method to call |
| * @param logger Logger for diagnostic messages |
| * |
| * @return DOCUMENT ME! |
| */ |
| private static String callGetter(final Object mbean, |
| final String name, |
| final Logger logger) { |
| final Method[] methods = mbean.getClass().getMethods(); |
| |
| for(int i = 0; i < methods.length; i++) { |
| if(methods[i].getName().equals(name)) { |
| try { |
| return methods[i].invoke(mbean, null).toString(); |
| } catch(final Exception e) { |
| logger.warn("Method '" + name + "' cannot be accessed on MBean " + |
| mbean.getClass().getName()); |
| |
| return null; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Try to find a JMX Agent (MBeanServer) |
| * |
| * @return The first MBeanServer found or null |
| */ |
| private static MBeanServer getInitialMBeanServer() { |
| final List servers = MBeanServerFactory.findMBeanServer(null); |
| |
| if(servers.size() > 0) { |
| return (MBeanServer)servers.get(0); |
| } |
| |
| return null; |
| } |
| } |