All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.browsermob.proxy.jetty.util.jmx.ModelMBeanImpl Maven / Gradle / Ivy

There is a newer version: 2.0-beta-7
Show newest version
// ========================================================================
// $Id: ModelMBeanImpl.java,v 1.18 2005/08/13 00:01:28 gregwilkins Exp $
// Copyright 1999-2004 Mort Bay Consulting Pty. 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.browsermob.proxy.jetty.util.jmx;

import org.apache.commons.logging.Log;
import org.browsermob.proxy.jetty.log.LogFactory;
import org.browsermob.proxy.jetty.util.LogSupport;
import org.browsermob.proxy.jetty.util.TypeUtil;

import javax.management.*;
import javax.management.modelmbean.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;


/* ------------------------------------------------------------ */
/** Model MBean Implementation.
 * This implementation of the JMX Model MBean API is designed to allow
 * easy creation of Model MBeans. From minimal descriptions of
 * operations and attributes, reflection is used to determine the full
 * signature and ResourceBundles are used to determine other meta data.
 *
 * This class is normally used in one of the following patterns:
    *
  • As a base class for a real MBean that contains the actual * attributes and operations of the MBean. Such an Object is only * usable in a JMX environment. *
  • As a proxy MBean to another non-JMX object. The attributes and * operations of the proxied object are defined in the MBean. This * pattern is used when an existing non-JMX objects API is to be * exposed as an MBean. *
  • As a base class for a proxy MBean. The attributes and oepration * of the MBean are implemented by the derived class but delegate to * one or more other objects. This pattern is used if existing objects * are to be managed by JMX, but a new management API needs to be * defined. *
* * @version $Revision: 1.18 $ * @author Greg Wilkins (gregw) */ public class ModelMBeanImpl implements ModelMBean, MBeanRegistration { private static Log log = LogFactory.getLog(ModelMBeanImpl.class); public final static int IMPACT_ACTION = MBeanOperationInfo.ACTION; public final static int IMPACT_ACTION_INFO = MBeanOperationInfo.ACTION_INFO; public final static int IMPACT_INFO = MBeanOperationInfo.INFO; public final static int IMPACT_UNKOWN = MBeanOperationInfo.UNKNOWN; public final static String STRING="java.lang.String"; public final static String OBJECT="java.lang.Object"; public final static String INT="int"; public final static String[] NO_PARAMS=new String[0]; public final static boolean READ_WRITE=true; public final static boolean READ_ONLY=false; public final static boolean ON_MBEAN=true; public final static boolean ON_OBJECT=false; private static HashMap __objectId = new HashMap(); private static String __defaultDomain="org.browsermob.proxy.jetty"; protected ModelMBeanInfoSupport _beanInfo; private MBeanServer _mBeanServer; private Object _object; private ObjectName _objectName; private boolean _dirty=false; private HashMap _getter = new HashMap(4); private HashMap _setter = new HashMap(4); private HashMap _method = new HashMap(4); private ArrayList _attributes = new ArrayList(4); private ArrayList _operations = new ArrayList(4); private ArrayList _notifications = new ArrayList(4); private String _baseObjectName=null; private Map _components = new HashMap(4); /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /** Create MBean for Object. * Attempts to create an MBean for the object by searching the * package and class name space. For example an object of the * type
     *   class com.acme.MyClass extends com.acme.util.BaseClass
     * 
* Then this method would look for the following * classes:
    *
  • com.acme.MyClassMBean *
  • com.acme.jmx.MyClassMBean *
  • com.acme.util.BaseClassMBean *
  • com.acme.util.jmx.BaseClassMBean *
* @param o The object * @return A new instance of an MBean for the object or null. */ public static ModelMBean mbeanFor(Object o) { try { Class oClass = o.getClass(); ClassLoader loader =oClass.getClassLoader(); ModelMBean mbean = null; boolean jmx=false; Class[] interfaces=null; int i=0; while (mbean==null && oClass!=null) { Class focus=interfaces==null?oClass:interfaces[i]; String pName = focus.getPackage().getName(); String cName = focus.getName().substring(pName.length()+1); String mName=pName+(jmx?".jmx.":".")+cName+"MBean"; try{ Class mClass=loader.loadClass(mName); if(log.isTraceEnabled())log.trace("mbeanFor "+o+" mClass="+mClass); mbean=(ModelMBean)mClass.newInstance(); mbean.setManagedResource(o,"objectReference"); if(log.isDebugEnabled())log.debug("mbeanFor "+o+" is "+mbean); return mbean; } catch(ClassNotFoundException e) { if (e.toString().endsWith("MBean")) { if(log.isTraceEnabled())log.trace(e.toString());} else log.warn(LogSupport.EXCEPTION,e); } catch(Error e) { log.warn(LogSupport.EXCEPTION,e); mbean=null; } catch(Exception e) { log.warn(LogSupport.EXCEPTION,e); mbean=null; } if (jmx) { if (interfaces!=null) { i++; if (i>=interfaces.length) { interfaces=null; oClass=oClass.getSuperclass(); } } else { interfaces=oClass.getInterfaces(); i=0; if (interfaces==null || interfaces.length==0) { interfaces=null; oClass=oClass.getSuperclass(); } } } jmx=!jmx; } } catch(Exception e) { LogSupport.ignore(log,e); } return null; } /* ------------------------------------------------------------ */ /** MBean Constructor. * No proxy object is defined. Attributes and operations are * defined on this instance. */ public ModelMBeanImpl() {} /* ------------------------------------------------------------ */ /** Proxy MBean Constructor. * @param proxyObject The actual object on which attributes and * operations are to be defined and called. */ public ModelMBeanImpl(Object proxyObject) { try { setManagedResource(proxyObject,"objectReference"); } catch(Exception e) { log.warn(LogSupport.EXCEPTION,e); throw new IllegalArgumentException(e.toString()); } } /* ------------------------------------------------------------ */ public static String getDefaultDomain() { return __defaultDomain; } /* ------------------------------------------------------------ */ public static void setDefaultDomain(String d) { __defaultDomain=d; } /* ------------------------------------------------------------ */ public MBeanServer getMBeanServer() { return _mBeanServer; } /* ------------------------------------------------------------ */ public ObjectName getObjectName() { return _objectName; } /* ------------------------------------------------------------ */ public Object getManagedResource() { return _object; } /* ------------------------------------------------------------ */ public void setManagedResource(Object proxyObject, String type) throws MBeanException, RuntimeOperationsException, InstanceNotFoundException, InvalidTargetObjectTypeException { if (proxyObject==null) { proxyObject=null; return; } log.debug("setManagedResource"); if (!"objectreference".equalsIgnoreCase(type)) throw new InvalidTargetObjectTypeException(type); if (_object==null) { // first set so define attributes etc. _object=proxyObject; defineManagedResource(); } else _object=proxyObject; } /* ------------------------------------------------------------ */ /** Define the Managed Resource. * This method is called the first time setManagedResource is * called with a non-null object. It should be implemented by a * derived ModelMBean to define the attributes and operations * after an initial object has been set. */ protected void defineManagedResource() {} /* ------------------------------------------------------------ */ /** Not Supported. * Use RequiredModelMBean for this style of MBean creation. */ public void setModelMBeanInfo(ModelMBeanInfo info) throws MBeanException, RuntimeOperationsException { throw new Error("setModelMBeanInfo not supported"); } /* ------------------------------------------------------------ */ /** Define an attribute on the managed object. * The meta data is defined by looking for standard getter and * setter methods. Descriptions are obtained with a call to * findDescription with the attribute name. * @param name The name of the attribute. Normal java bean * capitlization is enforced on this name. */ public synchronized void defineAttribute(String name) { defineAttribute(name,true,false); } /* ------------------------------------------------------------ */ /** Define an attribute on the managed object. * The meta data is defined by looking for standard getter and * setter methods. Descriptions are obtained with a call to * findDescription with the attribute name. * @param name The name of the attribute. Normal java bean * capitlization is enforced on this name. * @param writable If false, do not look for a setter. */ public synchronized void defineAttribute(String name, boolean writable) { defineAttribute(name,writable,false); } /* ------------------------------------------------------------ */ /** Define an attribute on the managed object. * The meta data is defined by looking for standard getter and * setter methods. Descriptions are obtained with a call to * findDescription with the attribute name. * @param name The name of the attribute. Normal java bean * capitlization is enforced on this name. * @param writable If false, do not look for a setter. * @param onMBean . */ public synchronized void defineAttribute(String name, boolean writable, boolean onMBean) { _dirty=true; String uName=name.substring(0,1).toUpperCase()+name.substring(1); name=java.beans.Introspector.decapitalize(name); Class oClass=onMBean?this.getClass():_object.getClass(); Class type=null; Method getter=null; Method setter=null; Method[] methods=oClass.getMethods(); for (int m=0;m0?",":"")+signature[i]; } methodKey+=")"; // Build param infos for (int i=0;i0?",":"")+pInfo[i].getType(); } method+=")"; _method.put(method,oClass.getMethod(opInfo.getName(),types)); _operations.add(opInfo); } catch(Exception e) { log.warn(LogSupport.EXCEPTION,e); throw new IllegalArgumentException(e.toString()); } } /* ------------------------------------------------------------ */ public synchronized MBeanInfo getMBeanInfo() { log.debug("getMBeanInfo"); if (_dirty) { _dirty=false; ModelMBeanAttributeInfo[] attributes = (ModelMBeanAttributeInfo[]) _attributes.toArray(new ModelMBeanAttributeInfo[_attributes.size()]); ModelMBeanOperationInfo[] operations = (ModelMBeanOperationInfo[]) _operations.toArray(new ModelMBeanOperationInfo[_operations.size()]); ModelMBeanNotificationInfo[] notifications =(ModelMBeanNotificationInfo[]) _notifications.toArray(new ModelMBeanNotificationInfo[_notifications.size()]); _beanInfo = new ModelMBeanInfoSupport(_object.getClass().getName(), findDescription(null), attributes, null, operations, notifications); } return _beanInfo; } /* ------------------------------------------------------------ */ public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException { if(log.isDebugEnabled())log.debug("getAttribute "+name); Method getter = (Method)_getter.get(name); if (getter==null) throw new AttributeNotFoundException(name); try { Object o=_object; if (getter.getDeclaringClass().isInstance(this)) o=this; return getter.invoke(o,(java.lang.Object[])null); } catch(IllegalAccessException e) { log.warn(LogSupport.EXCEPTION,e); throw new AttributeNotFoundException(e.toString()); } catch(InvocationTargetException e) { log.warn(LogSupport.EXCEPTION,e); throw new ReflectionException((Exception)e.getTargetException()); } } /* ------------------------------------------------------------ */ public AttributeList getAttributes(String[] names) { log.debug("getAttributes"); AttributeList results=new AttributeList(names.length); for (int i=0;i0?",":"")+signature[i]; methodKey+=")"; try { Method method = (Method)_method.get(methodKey); if (method==null) throw new NoSuchMethodException(methodKey); Object o=_object; if (method.getDeclaringClass().isInstance(this)) o=this; return method.invoke(o,params); } catch(NoSuchMethodException e) { log.warn(LogSupport.EXCEPTION,e); throw new ReflectionException(e); } catch(IllegalAccessException e) { log.warn(LogSupport.EXCEPTION,e); throw new MBeanException(e); } catch(InvocationTargetException e) { log.warn(LogSupport.EXCEPTION,e); throw new ReflectionException((Exception)e.getTargetException()); } } /* ------------------------------------------------------------ */ public void load() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException { log.debug("load"); } /* ------------------------------------------------------------ */ public void store() throws MBeanException, RuntimeOperationsException, InstanceNotFoundException { log.debug("store"); } /* ------------------------------------------------------------ */ public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { log.debug("addNotificationListener"); } /* ------------------------------------------------------------ */ public MBeanNotificationInfo[] getNotificationInfo() { log.debug("getNotificationInfo"); return null; } /* ------------------------------------------------------------ */ public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { log.debug("removeNotificationListener"); } /* ------------------------------------------------------------ */ public void addAttributeChangeNotificationListener(NotificationListener listener, String name, Object handback) throws MBeanException, RuntimeOperationsException, IllegalArgumentException { log.debug("addAttributeChangeNotificationListener"); } /* ------------------------------------------------------------ */ public void removeAttributeChangeNotificationListener(NotificationListener listener, String name) throws MBeanException, RuntimeOperationsException, ListenerNotFoundException { log.debug("removeAttributeChangeNotificationListener"); } /* ------------------------------------------------------------ */ public void sendAttributeChangeNotification(Attribute oldAttr, Attribute newAttr) throws MBeanException, RuntimeOperationsException { log.debug("sendAttributeChangeNotification"); } /* ------------------------------------------------------------ */ public void sendAttributeChangeNotification(AttributeChangeNotification notify) throws MBeanException, RuntimeOperationsException { log.debug("sendAttributeChangeNotification"); } /* ------------------------------------------------------------ */ public void sendNotification(String notify) throws MBeanException, RuntimeOperationsException { log.debug("sendNotification"); } /* ------------------------------------------------------------ */ public void sendNotification(Notification notify) throws MBeanException, RuntimeOperationsException { log.debug("sendNotification"); } /* ------------------------------------------------------------ */ /* Find MBean descriptions. * MBean descriptions are searched for in ResourceBundles. Bundles * are looked for in a mbean.property files within each package of * the MBean class inheritance hierachy. * Once a bundle is found, the key is added to object names in the * following order: fully qualied managed resource class name, tail * managed resource class name, tail mbean class name. The string * "MBean" is stripped from the tail of any name. *

For example, if the class a.b.C is managed by a MBean * p.q.RMBean which is derived from p.SMBean, then the seach order * for a key x is as follows:

     *   bundle: p.q.mbean    name: a.b.C.x
     *   bundle: p.q.mbean    name: C.x
     *   bundle: p.q.mbean    name: R.x
     *   bundle: p.mbean      name: a.b.C.x
     *   bundle: p.mbean      name: C.x
     *   bundle: p.mbean      name: S.x
     * 
*

The convention used for keys passed to this method are:

     *   null or empty         - Object description
     *   xxx                   - Attribute xxx description
     *   xxx()                 - Simple operation xxx description
     *   xxx(type,..)          - Operation xxx with signature desciption
     *   xxx(type,..)[n]       - Param n of operation xxx description
     * 
* @param key * @return Description string. */ private String findDescription(String key) { Class lookIn = this.getClass(); // Array of possible objectNames String[] objectNames=new String[3]; objectNames[0]=_object.getClass().getName(); if (objectNames[0].indexOf(".")>=0) objectNames[1]=objectNames[0].substring(objectNames[0].lastIndexOf(".")+1); while(lookIn!=null) { String pkg=lookIn.getName(); int lastDot= pkg.lastIndexOf("."); if (lastDot>0) { objectNames[2]=pkg.substring(lastDot+1); pkg=pkg.substring(0,lastDot); } else { objectNames[2]=pkg; pkg=null; } String resource=(pkg==null?"mbean":(pkg.replace('.','/')+"/mbean")); if(log.isTraceEnabled())log.trace("Look for: "+resource); try { ResourceBundle bundle= ResourceBundle.getBundle(resource, Locale.getDefault(), _object.getClass().getClassLoader()); if(log.isTraceEnabled())log.trace("Bundle "+resource); for (int i=0;i0) name+="."+key; try{ String description=bundle.getString(name); if (description!=null && description.length()>0) return description; } catch(Exception e) { if(log.isTraceEnabled())log.trace(e.toString()); } } } catch(Exception e) { if(log.isTraceEnabled())log.trace(e.toString()); } lookIn=lookIn.getSuperclass(); } if (key==null || key.length()==0) return objectNames[0]; return key; } /* ------------------------------------------------------------ */ /** Create a new ObjectName. * Return a new object name. The default implementation is the * results of uniqueObjectName(baseObjectName), if baseObjectName * is not set, then the results of uniqueObjectName(defaultDomain+":"); * @return The Object name */ protected ObjectName newObjectName(MBeanServer server) { // Create own ObjectName of the form: // package:class=id if (_baseObjectName!=null) { if (_baseObjectName.indexOf(':')>=0) return uniqueObjectName(server,_baseObjectName); return uniqueObjectName(server,getDefaultDomain()+":"+ _baseObjectName); } return uniqueObjectName(server,getDefaultDomain()+":"); } /* ------------------------------------------------------------ */ public void setBaseObjectName(String s) { _baseObjectName=s; } /* ------------------------------------------------------------ */ public String getBaseObjectName() { return _baseObjectName; } /* ------------------------------------------------------------ */ /** Pre registration notification. * If this method is specialized by a derived class that may set * the objectName, then it should call this implementation with * the new objectName. * @param server * @param oName * @return The ObjectName to use. */ public synchronized ObjectName preRegister(MBeanServer server, ObjectName oName) { _mBeanServer=server; _objectName=oName; if (_objectName==null) { try{oName=newObjectName(server);} catch(Exception e){log.warn(LogSupport.EXCEPTION,e);} } if(log.isDebugEnabled())log.debug("preRegister "+_objectName+" -> "+oName); _objectName=oName; return _objectName; } /* ------------------------------------------------------------ */ public void postRegister(Boolean ok) { if (ok.booleanValue()) log.info("Registered "+_objectName); else { _mBeanServer=null; _objectName=null; } } /* ------------------------------------------------------------ */ public void preDeregister() { log.info("Deregister "+_objectName); getComponentMBeans(null,_components); _components.clear(); } /* ------------------------------------------------------------ */ /** Post Deregister. * This implementation destroys this MBean and it cannot be used again. */ public void postDeregister() { _beanInfo=null; _mBeanServer=null; _object=null; _objectName=null; if (_getter!=null) _getter.clear(); _getter=null; if (_setter!=null) _setter.clear(); _setter=null; if (_method!=null) _method.clear(); _method=null; if (_attributes!=null) _attributes.clear(); _attributes=null; if (_operations!=null) _operations.clear(); _operations=null; if (_notifications!=null) _notifications.clear(); _notifications=null; } /* ------------------------------------------------------------ */ /** Add an id clause to a JMX object name. * Used to make unique objectnames when there are no other * distinguishing attributes. * If the passed object name ends with '=', just a unique ID is * added. Otherwise and classname= clause is added. * @param objectName * @return objectName with id= class. */ public synchronized ObjectName uniqueObjectName(MBeanServer server, String objectName) { return uniqueObjectName(server,_object,objectName); } /* ------------------------------------------------------------ */ public synchronized ObjectName uniqueObjectName(MBeanServer server, Object object, String objectName) { if (!objectName.endsWith("=")) { String className = object.getClass().getName(); if (className.indexOf(".")>0) className=className.substring(className.lastIndexOf(".")+1); if (className.endsWith("MBean")) className=className.substring(0,className.length()-5); if (!objectName.endsWith(":")) objectName+=","; objectName+=className+"="; } ObjectName oName=null; try { while(true) { Integer id=(Integer)__objectId.get(objectName); if (id==null) id=new Integer(0); oName=new ObjectName(objectName+id); id=new Integer(id.intValue()+1); __objectId.put(objectName,id); // If no server, this must be unique if (server==null) break; // Otherwise let's check it is unique // if not found then it is unique if (!server.isRegistered(oName)) break; } } catch(Exception e) { log.warn(LogSupport.EXCEPTION,e); } return oName; } /* ------------------------------------------------------------ */ /** Get Component MBeans. * Creates, registers and deregisters MBeans for an array of components. * On each call the passed map is used to determine components that have * already been registers and those that need to be deregistered. * @param components the components. * @param map A map of previously registered components to object * name. If null is passed, a default map for the mbean is used. * @return An array of ObjectNames for each component. */ protected ObjectName[] getComponentMBeans(Object[] components, Map map) { if (map==null) map=_components; ObjectName[] beans=null; if (components==null) beans = new ObjectName[0]; else { beans = new ObjectName[components==null?0:components.length]; // Add new beans for (int i=0;icomponents.length) { Object[] to_delete=new Object[map.size()-beans.length]; int d=0; Iterator iter = map.keySet().iterator(); keys: while(iter.hasNext()) { Object bean = iter.next(); if (components!=null) { for(int i=0;i0;) { try{getMBeanServer().unregisterMBean((ObjectName)map.remove(to_delete[d]));} catch (Exception e) {log.warn(LogSupport.EXCEPTION,e);} } } return beans; } /** Unregister mbeans for already registered components * @param map */ protected void destroyComponentMBeans (Map map) { //if no map of registered mbean names is passed, //use the default map if (null==map) map = _components; if (map==null) return; Iterator itor = map.values().iterator(); while (itor.hasNext()) { try { ObjectName o = (ObjectName)itor.next(); getMBeanServer().unregisterMBean(o); itor.remove(); } catch (Exception e) {log.warn(LogSupport.EXCEPTION,e);} } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy