org.apache.tomcat.util.mx.DynamicMBeanProxy.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.tomcat.util.mx.DynamicMBeanProxy.java

Source

/*
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:  
 *       "This product includes software developed by the 
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */

package org.apache.tomcat.util.mx;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.ReflectionException;

/**
 * DynamicMBean implementation using introspection to manage any
 * component that follows the bean/ant/Interceptor/Valve/Jk2 patterns.
 *
 * The class will wrap any component conforming to those patterns.
 *
 * @deprecated The same functionality ( and more ) is now available in
 *         commons-modeler
 * @author Costin Manolache
 */
public class DynamicMBeanProxy implements DynamicMBean {
    Object real;
    String name;

    Method methods[] = null;

    Hashtable attMap = new Hashtable();

    // key: attribute val: getter method
    Hashtable getAttMap = new Hashtable();

    // key: attribute val: setter method
    Hashtable setAttMap = new Hashtable();

    // key: operation val: invoke method
    Hashtable invokeAttMap = new Hashtable();

    static MBeanServer mserver = null;

    static Hashtable instances = new Hashtable();

    /** Create a Dynamic proxy, using introspection to manage a
     *  real tomcat component.
     */
    public DynamicMBeanProxy() {

    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        if (name != null)
            return name;

        if (real == null)
            return null;

        name = generateName(real.getClass());
        return name;
    }

    /** If a name was not provided, generate a name based on the
     *  class name and a sequence number.
     */
    public static String generateName(Class realClass) {
        String name = realClass.getName();
        name = name.substring(name.lastIndexOf(".") + 1);
        Integer iInt = (Integer) instances.get(name);
        int seq = 0;
        if (iInt != null) {
            seq = iInt.intValue();
            seq++;
            instances.put(name, new Integer(seq));
        } else {
            instances.put(name, new Integer(0));
        }
        return "name=" + name + ",seq=" + seq;
    }

    public static String createMBean(Object proxy, String domain, String name) {
        try {
            DynamicMBeanProxy mbean = new DynamicMBeanProxy();
            mbean.setReal(proxy);
            if (name != null) {
                mbean.setName(name);
            } else {
                mbean.setName(generateName(proxy.getClass()));
            }

            return mbean.registerMBean(domain);
        } catch (Throwable t) {
            log.error("Error creating mbean ", t);
            return null;
        }
    }

    public String registerMBean(String domain) {
        try {
            // XXX use aliases, suffix only, proxy.getName(), etc
            String fullName = domain + ": " + getName();
            ObjectName oname = new ObjectName(fullName);

            if (getMBeanServer().isRegistered(oname)) {
                log.info("Unregistering " + oname);
                getMBeanServer().unregisterMBean(oname);
            }
            getMBeanServer().registerMBean(this, oname);
            return fullName;
        } catch (Throwable t) {
            log.error("Error creating mbean ", t);
            return null;
        }
    }

    public static void unregisterMBean(Object o, String name) {
        try {
            ObjectName oname = new ObjectName(name);

            getMBeanServer().unregisterMBean(oname);
        } catch (Throwable t) {
            log.error("Error unregistering mbean ", t);
        }
    }

    public static MBeanServer getMBeanServer() {
        if (mserver == null) {
            if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
                mserver = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
            } else {
                mserver = MBeanServerFactory.createMBeanServer();
            }
        }

        return mserver;
    }

    private boolean supportedType(Class ret) {
        return ret == String.class || ret == Integer.class || ret == Integer.TYPE || ret == Long.class
                || ret == Long.TYPE || ret == java.io.File.class || ret == Boolean.class || ret == Boolean.TYPE;
    }

    /** Set the managed object.
     *
     * @todo Read an XML ( or .properties ) file containing descriptions,
     *       generated from source comments
     * @todo Also filter methods based on config ( hide methods/attributes )
     * @todo Adapters for notifications ( Interceptor hooks, etc ). 
     */
    public void setReal(Object realBean) {
        real = realBean;
    }

    private void init() {
        if (methods != null)
            return;
        methods = real.getClass().getMethods();
        for (int j = 0; j < methods.length; ++j) {
            String name = methods[j].getName();

            if (name.startsWith("get")) {
                if (methods[j].getParameterTypes().length != 0) {
                    continue;
                }
                if (!Modifier.isPublic(methods[j].getModifiers())) {
                    //log.debug("not public " + methods[j] );
                    continue;
                }
                Class ret = methods[j].getReturnType();
                if (!supportedType(ret)) {
                    if (log.isDebugEnabled())
                        log.debug("Unsupported " + ret);
                    continue;
                }
                name = unCapitalize(name.substring(3));

                getAttMap.put(name, methods[j]);
                // just a marker, we don't use the value 
                attMap.put(name, methods[j]);
            } else if (name.startsWith("is")) {
                // not used in our code. Add later

            } else if (name.startsWith("set")) {
                Class params[] = methods[j].getParameterTypes();
                if (params.length != 1) {
                    continue;
                }
                if (!Modifier.isPublic(methods[j].getModifiers()))
                    continue;
                Class ret = params[0];
                if (!supportedType(ret)) {
                    continue;
                }
                name = unCapitalize(name.substring(3));
                setAttMap.put(name, methods[j]);
                attMap.put(name, methods[j]);
            } else {
                if (methods[j].getParameterTypes().length != 0) {
                    continue;
                }
                if (methods[j].getDeclaringClass() == Object.class)
                    continue;
                if (!Modifier.isPublic(methods[j].getModifiers()))
                    continue;
                invokeAttMap.put(name, methods[j]);
            }
        }
    }

    /**
     * @todo Find if the 'className' is the name of the MBean or
     *       the real class ( I suppose first )
     * @todo Read (optional) descriptions from a .properties, generated
     *       from source
     * @todo Deal with constructors
     *       
     */
    public MBeanInfo getMBeanInfo() {
        if (methods == null) {
            init();
        }
        try {
            MBeanAttributeInfo attributes[] = new MBeanAttributeInfo[attMap.size()];

            Enumeration en = attMap.keys();
            int i = 0;
            while (en.hasMoreElements()) {
                String name = (String) en.nextElement();
                attributes[i++] = new MBeanAttributeInfo(name, "Attribute " + name, (Method) getAttMap.get(name),
                        (Method) setAttMap.get(name));
            }

            MBeanOperationInfo operations[] = new MBeanOperationInfo[invokeAttMap.size()];

            en = invokeAttMap.keys();
            i = 0;
            while (en.hasMoreElements()) {
                String name = (String) en.nextElement();
                Method m = (Method) invokeAttMap.get(name);
                if (m != null && name != null) {
                    operations[i++] = new MBeanOperationInfo(name, m);
                } else {
                    System.out.println("Null arg " + name + " " + m);
                }
            }

            if (log.isDebugEnabled())
                log.debug(real.getClass().getName() + " getMBeanInfo()");

            return new MBeanInfo(real.getClass().getName(), /* ??? */
                    "MBean for " + getName(), attributes, new MBeanConstructorInfo[0], operations,
                    new MBeanNotificationInfo[0]);
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    static final Object[] NO_ARGS_PARAM = new Object[0];

    public Object getAttribute(String attribute)
            throws AttributeNotFoundException, MBeanException, ReflectionException {
        if (methods == null)
            init();
        Method m = (Method) getAttMap.get(attribute);
        if (m == null)
            throw new AttributeNotFoundException(attribute);

        try {
            if (log.isDebugEnabled())
                log.debug(real.getClass().getName() + " getAttribute " + attribute);
            return m.invoke(real, NO_ARGS_PARAM);
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
            throw new MBeanException(ex);
        } catch (InvocationTargetException ex1) {
            ex1.printStackTrace();
            throw new MBeanException(ex1);
        }
    }

    public void setAttribute(Attribute attribute)
            throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
        if (methods == null)
            init();
        // XXX Send notification !!!
        Method m = (Method) setAttMap.get(attribute.getName());
        if (m == null)
            throw new AttributeNotFoundException(attribute.getName());

        try {
            log.info(real.getClass().getName() + "setAttribute " + attribute.getName());
            m.invoke(real, new Object[] { attribute.getValue() });
        } catch (IllegalAccessException ex) {
            ex.printStackTrace();
            throw new MBeanException(ex);
        } catch (InvocationTargetException ex1) {
            ex1.printStackTrace();
            throw new MBeanException(ex1);
        }
    }

    /**
     * Invoke a method. Only no param methods are supported at the moment
     * ( init, start, execute, etc ) ( that's the most common pattern we have
     *  in tomcat/ant/etc )
     *
     * @todo Implement invoke for methods with more arguments.
     */
    public Object invoke(String method, Object[] arguments, String[] params)
            throws MBeanException, ReflectionException {
        if (methods == null)
            init();
        Method m = (Method) invokeAttMap.get(method);
        if (m == null)
            return null;

        try {
            log.info(real.getClass().getName() + "invoke " + m.getName());
            return m.invoke(real, NO_ARGS_PARAM);
        } catch (IllegalAccessException ex) {
            throw new MBeanException(ex);
        } catch (InvocationTargetException ex1) {
            throw new MBeanException(ex1);
        }
    }

    // -------------------- Auxiliary methods --------------------

    public AttributeList setAttributes(AttributeList attributes) {
        Iterator attE = attributes.iterator();
        while (attE.hasNext()) {
            Attribute att = (Attribute) attE.next();

            try {
                setAttribute(att);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return attributes;
    }

    public AttributeList getAttributes(String[] attributes) {
        AttributeList al = new AttributeList();
        if (attributes == null)
            return null;

        for (int i = 0; i < attributes.length; i++) {
            try {
                Attribute att = new Attribute(attributes[i], getAttribute(attributes[i]));
                al.add(att);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return al;
    }

    // -------------------- Utils --------------------

    public static String unCapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        char chars[] = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }

    private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
            .getLog(DynamicMBeanProxy.class);

}