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

org.apache.tomcat.util.modeler.BaseModelMBean Maven / Gradle / Ivy

There is a newer version: 11.0.0-M26
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.tomcat.util.modeler;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;

import javax.management.Attribute;
import javax.management.AttributeChangeNotification;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InstanceNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.RuntimeErrorException;
import javax.management.RuntimeOperationsException;
import javax.management.modelmbean.InvalidTargetObjectTypeException;
import javax.management.modelmbean.ModelMBeanNotificationBroadcaster;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

/*
 * Changes from commons.modeler:
 *
 *  - use DynamicMBean
 *  - remove methods not used in tomcat and redundant/not very generic
 *  - must be created from the ManagedBean - I don't think there were any direct
 *    uses, but now it is required.
 *  - some of the gratuitous flexibility removed - instead this is more predictive and
 *    strict with the use cases.
 *  - all Method and metadata is stored in ManagedBean. BaseModelBMean and ManagedBean act
 *    like Object and Class.
 *  - setModelMBean is no longer called on resources ( not used in tomcat )
 *  - no caching of Methods for now - operations and setters are not called repeatedly in most
 *  management use cases. Getters should't be called very frequently either - and even if they
 *  are, the overhead of getting the method should be small compared with other JMX costs ( RMI, etc ).
 *  We can add getter cache if needed.
 *  - removed unused constructor, fields
 *
 *  TODO:
 *   - clean up catalina.mbeans, stop using weird inheritance
 */

/**
 * 

Basic implementation of the DynamicMBean interface, which * supports the minimal requirements of the interface contract.

* *

This can be used directly to wrap an existing java bean, or inside * an mlet or anywhere an MBean would be used. * * Limitations: *

    *
  • Only managed resources of type objectReference are * supported.
  • *
  • Caching of attribute values and operation results is not supported. * All calls to invoke() are immediately executed.
  • *
  • Persistence of MBean attributes and operations is not supported.
  • *
  • All classes referenced as attribute types, operation parameters, or * operation return values must be one of the following: *
      *
    • One of the Java primitive types (boolean, byte, char, double, * float, integer, long, short). Corresponding value will be wrapped * in the appropriate wrapper class automatically.
    • *
    • Operations that return no value should declare a return type of * void.
    • *
    *
  • Attribute caching is not supported
  • *
* * @author Craig R. McClanahan * @author Costin Manolache */ public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBeanNotificationBroadcaster { private static final Log log = LogFactory.getLog(BaseModelMBean.class); // ----------------------------------------------------------- Constructors /** * Construct a ModelMBean with default * ModelMBeanInfo information. * * @exception MBeanException if the initializer of an object * throws an exception * @exception RuntimeOperationsException if an IllegalArgumentException * occurs */ protected BaseModelMBean() throws MBeanException, RuntimeOperationsException { super(); } // ----------------------------------------------------- Instance Variables protected ObjectName oname=null; /** * Notification broadcaster for attribute changes. */ protected BaseNotificationBroadcaster attributeBroadcaster = null; /** * Notification broadcaster for general notifications. */ protected BaseNotificationBroadcaster generalBroadcaster = null; /** Metadata for the mbean instance. */ protected ManagedBean managedBean = null; /** * The managed resource this MBean is associated with (if any). */ protected Object resource = null; // --------------------------------------------------- DynamicMBean Methods // TODO: move to ManagedBean static final Object[] NO_ARGS_PARAM = new Object[0]; protected String resourceType = null; // key: operation val: invoke method //private Hashtable invokeAttMap=new Hashtable(); /** * Obtain and return the value of a specific attribute of this MBean. * * @param name Name of the requested attribute * * @exception AttributeNotFoundException if this attribute is not * supported by this MBean * @exception MBeanException if the initializer of an object * throws an exception * @exception ReflectionException if a Java reflection exception * occurs when invoking the getter */ @Override public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException { // Validate the input parameters if (name == null) throw new RuntimeOperationsException (new IllegalArgumentException("Attribute name is null"), "Attribute name is null"); if( (resource instanceof DynamicMBean) && ! ( resource instanceof BaseModelMBean )) { return ((DynamicMBean)resource).getAttribute(name); } Method m=managedBean.getGetter(name, this, resource); Object result = null; try { Class declaring = m.getDeclaringClass(); // workaround for catalina weird mbeans - the declaring class is BaseModelMBean. // but this is the catalina class. if( declaring.isAssignableFrom(this.getClass()) ) { result = m.invoke(this, NO_ARGS_PARAM ); } else { result = m.invoke(resource, NO_ARGS_PARAM ); } } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t == null) t = e; if (t instanceof RuntimeException) throw new RuntimeOperationsException ((RuntimeException) t, "Exception invoking method " + name); else if (t instanceof Error) throw new RuntimeErrorException ((Error) t, "Error invoking method " + name); else throw new MBeanException (e, "Exception invoking method " + name); } catch (Exception e) { throw new MBeanException (e, "Exception invoking method " + name); } // Return the results of this method invocation // FIXME - should we validate the return type? return (result); } /** * Obtain and return the values of several attributes of this MBean. * * @param names Names of the requested attributes */ @Override public AttributeList getAttributes(String names[]) { // Validate the input parameters if (names == null) throw new RuntimeOperationsException (new IllegalArgumentException("Attribute names list is null"), "Attribute names list is null"); // Prepare our response, eating all exceptions AttributeList response = new AttributeList(); for (int i = 0; i < names.length; i++) { try { response.add(new Attribute(names[i],getAttribute(names[i]))); } catch (Exception e) { // Not having a particular attribute in the response // is the indication of a getter problem } } return (response); } public void setManagedBean(ManagedBean managedBean) { this.managedBean = managedBean; } /** * Return the MBeanInfo object for this MBean. */ @Override public MBeanInfo getMBeanInfo() { return managedBean.getMBeanInfo(); } /** * Invoke a particular method on this MBean, and return any returned * value. * *

IMPLEMENTATION NOTE - This implementation will * attempt to invoke this method on the MBean itself, or (if not * available) on the managed resource object associated with this * MBean.

* * @param name Name of the operation to be invoked * @param params Array containing the method parameters of this operation * @param signature Array containing the class names representing * the signature of this operation * * @exception MBeanException if the initializer of an object * throws an exception * @exception ReflectionException if a Java reflection exception * occurs when invoking a method */ @Override public Object invoke(String name, Object params[], String signature[]) throws MBeanException, ReflectionException { if( (resource instanceof DynamicMBean) && ! ( resource instanceof BaseModelMBean )) { return ((DynamicMBean)resource).invoke(name, params, signature); } // Validate the input parameters if (name == null) throw new RuntimeOperationsException (new IllegalArgumentException("Method name is null"), "Method name is null"); if( log.isDebugEnabled()) log.debug("Invoke " + name); Method method= managedBean.getInvoke(name, params, signature, this, resource); // Invoke the selected method on the appropriate object Object result = null; try { if( method.getDeclaringClass().isAssignableFrom( this.getClass()) ) { result = method.invoke(this, params ); } else { result = method.invoke(resource, params); } } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); log.error("Exception invoking method " + name , t ); if (t == null) t = e; if (t instanceof RuntimeException) throw new RuntimeOperationsException ((RuntimeException) t, "Exception invoking method " + name); else if (t instanceof Error) throw new RuntimeErrorException ((Error) t, "Error invoking method " + name); else throw new MBeanException ((Exception)t, "Exception invoking method " + name); } catch (Exception e) { log.error("Exception invoking method " + name , e ); throw new MBeanException (e, "Exception invoking method " + name); } // Return the results of this method invocation // FIXME - should we validate the return type? return (result); } static Class getAttributeClass(String signature) throws ReflectionException { if (signature.equals(Boolean.TYPE.getName())) return Boolean.TYPE; else if (signature.equals(Byte.TYPE.getName())) return Byte.TYPE; else if (signature.equals(Character.TYPE.getName())) return Character.TYPE; else if (signature.equals(Double.TYPE.getName())) return Double.TYPE; else if (signature.equals(Float.TYPE.getName())) return Float.TYPE; else if (signature.equals(Integer.TYPE.getName())) return Integer.TYPE; else if (signature.equals(Long.TYPE.getName())) return Long.TYPE; else if (signature.equals(Short.TYPE.getName())) return Short.TYPE; else { try { ClassLoader cl=Thread.currentThread().getContextClassLoader(); if( cl!=null ) return cl.loadClass(signature); } catch( ClassNotFoundException e ) { } try { return Class.forName(signature); } catch (ClassNotFoundException e) { throw new ReflectionException (e, "Cannot find Class for " + signature); } } } /** * Set the value of a specific attribute of this MBean. * * @param attribute The identification of the attribute to be set * and the new value * * @exception AttributeNotFoundException if this attribute is not * supported by this MBean * @exception MBeanException if the initializer of an object * throws an exception * @exception ReflectionException if a Java reflection exception * occurs when invoking the getter */ @Override public void setAttribute(Attribute attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { if( log.isDebugEnabled() ) log.debug("Setting attribute " + this + " " + attribute ); if( (resource instanceof DynamicMBean) && ! ( resource instanceof BaseModelMBean )) { try { ((DynamicMBean)resource).setAttribute(attribute); } catch (InvalidAttributeValueException e) { throw new MBeanException(e); } return; } // Validate the input parameters if (attribute == null) throw new RuntimeOperationsException (new IllegalArgumentException("Attribute is null"), "Attribute is null"); String name = attribute.getName(); Object value = attribute.getValue(); if (name == null) throw new RuntimeOperationsException (new IllegalArgumentException("Attribute name is null"), "Attribute name is null"); Object oldValue=null; //if( getAttMap.get(name) != null ) // oldValue=getAttribute( name ); Method m=managedBean.getSetter(name,this,resource); try { if( m.getDeclaringClass().isAssignableFrom( this.getClass()) ) { m.invoke(this, new Object[] { value }); } else { m.invoke(resource, new Object[] { value }); } } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t == null) t = e; if (t instanceof RuntimeException) throw new RuntimeOperationsException ((RuntimeException) t, "Exception invoking method " + name); else if (t instanceof Error) throw new RuntimeErrorException ((Error) t, "Error invoking method " + name); else throw new MBeanException (e, "Exception invoking method " + name); } catch (Exception e) { log.error("Exception invoking method " + name , e ); throw new MBeanException (e, "Exception invoking method " + name); } try { sendAttributeChangeNotification(new Attribute( name, oldValue), attribute); } catch(Exception ex) { log.error("Error sending notification " + name, ex); } //attributes.put( name, value ); // if( source != null ) { // // this mbean is associated with a source - maybe we want to persist // source.updateField(oname, name, value); // } } @Override public String toString() { if( resource==null ) return "BaseModelMbean[" + resourceType + "]"; return resource.toString(); } /** * Set the values of several attributes of this MBean. * * @param attributes THe names and values to be set * * @return The list of attributes that were set and their new values */ @Override public AttributeList setAttributes(AttributeList attributes) { AttributeList response = new AttributeList(); // Validate the input parameters if (attributes == null) return response; // Prepare and return our response, eating all exceptions String names[] = new String[attributes.size()]; int n = 0; Iterator items = attributes.iterator(); while (items.hasNext()) { Attribute item = (Attribute) items.next(); names[n++] = item.getName(); try { setAttribute(item); } catch (Exception e) { // Ignore all exceptions } } return (getAttributes(names)); } // ----------------------------------------------------- ModelMBean Methods /** * Get the instance handle of the object against which we execute * all methods in this ModelMBean management interface. * * @exception InstanceNotFoundException if the managed resource object * cannot be found * @exception InvalidTargetObjectTypeException if the managed resource * object is of the wrong type * @exception MBeanException if the initializer of the object throws * an exception * @exception RuntimeOperationsException if the managed resource or the * resource type is null or invalid */ public Object getManagedResource() throws InstanceNotFoundException, InvalidTargetObjectTypeException, MBeanException, RuntimeOperationsException { if (resource == null) throw new RuntimeOperationsException (new IllegalArgumentException("Managed resource is null"), "Managed resource is null"); return resource; } /** * Set the instance handle of the object against which we will execute * all methods in this ModelMBean management interface. * * This method will detect and call "setModelMbean" method. A resource * can implement this method to get a reference to the model mbean. * The reference can be used to send notification and access the * registry. * The caller can provide the mbean instance or the object name to * the resource, if needed. * * @param resource The resource object to be managed * @param type The type of reference for the managed resource * ("ObjectReference", "Handle", "IOR", "EJBHandle", or * "RMIReference") * * @exception InstanceNotFoundException if the managed resource object * cannot be found * @exception MBeanException if the initializer of the object throws * an exception * @exception RuntimeOperationsException if the managed resource or the * resource type is null or invalid */ public void setManagedResource(Object resource, String type) throws InstanceNotFoundException, MBeanException, RuntimeOperationsException { if (resource == null) throw new RuntimeOperationsException (new IllegalArgumentException("Managed resource is null"), "Managed resource is null"); // if (!"objectreference".equalsIgnoreCase(type)) // throw new InvalidTargetObjectTypeException(type); this.resource = resource; this.resourceType = resource.getClass().getName(); // // Make the resource aware of the model mbean. // try { // Method m=resource.getClass().getMethod("setModelMBean", // new Class[] {ModelMBean.class}); // if( m!= null ) { // m.invoke(resource, new Object[] {this}); // } // } catch( NoSuchMethodException t ) { // // ignore // } catch( Throwable t ) { // log.error( "Can't set model mbean ", t ); // } } // ------------------------------ ModelMBeanNotificationBroadcaster Methods /** * Add an attribute change notification event listener to this MBean. * * @param listener Listener that will receive event notifications * @param name Name of the attribute of interest, or null * to indicate interest in all attributes * @param handback Handback object to be sent along with event * notifications * * @exception IllegalArgumentException if the listener parameter is null */ @Override public void addAttributeChangeNotificationListener (NotificationListener listener, String name, Object handback) throws IllegalArgumentException { if (listener == null) throw new IllegalArgumentException("Listener is null"); if (attributeBroadcaster == null) attributeBroadcaster = new BaseNotificationBroadcaster(); if( log.isDebugEnabled() ) log.debug("addAttributeNotificationListener " + listener); BaseAttributeFilter filter = new BaseAttributeFilter(name); attributeBroadcaster.addNotificationListener (listener, filter, handback); } /** * Remove an attribute change notification event listener from * this MBean. * * @param listener The listener to be removed * @param name The attribute name for which no more events are required * * * @exception ListenerNotFoundException if this listener is not * registered in the MBean */ @Override public void removeAttributeChangeNotificationListener (NotificationListener listener, String name) throws ListenerNotFoundException { if (listener == null) throw new IllegalArgumentException("Listener is null"); if (attributeBroadcaster == null) attributeBroadcaster = new BaseNotificationBroadcaster(); // FIXME - currently this removes *all* notifications for this listener attributeBroadcaster.removeNotificationListener(listener); } /** * Send an AttributeChangeNotification to all registered * listeners. * * @param notification The AttributeChangeNotification * that will be passed * * @exception MBeanException if an object initializer throws an * exception * @exception RuntimeOperationsException wraps IllegalArgumentException * when the specified notification is null or invalid */ @Override public void sendAttributeChangeNotification (AttributeChangeNotification notification) throws MBeanException, RuntimeOperationsException { if (notification == null) throw new RuntimeOperationsException (new IllegalArgumentException("Notification is null"), "Notification is null"); if (attributeBroadcaster == null) return; // This means there are no registered listeners if( log.isDebugEnabled() ) log.debug( "AttributeChangeNotification " + notification ); attributeBroadcaster.sendNotification(notification); } /** * Send an AttributeChangeNotification to all registered * listeners. * * @param oldValue The original value of the Attribute * @param newValue The new value of the Attribute * * @exception MBeanException if an object initializer throws an * exception * @exception RuntimeOperationsException wraps IllegalArgumentException * when the specified notification is null or invalid */ @Override public void sendAttributeChangeNotification (Attribute oldValue, Attribute newValue) throws MBeanException, RuntimeOperationsException { // Calculate the class name for the change notification String type = null; if (newValue.getValue() != null) type = newValue.getValue().getClass().getName(); else if (oldValue.getValue() != null) type = oldValue.getValue().getClass().getName(); else return; // Old and new are both null == no change AttributeChangeNotification notification = new AttributeChangeNotification (this, 1, System.currentTimeMillis(), "Attribute value has changed", oldValue.getName(), type, oldValue.getValue(), newValue.getValue()); sendAttributeChangeNotification(notification); } /** * Send a Notification to all registered listeners as a * jmx.modelmbean.general notification. * * @param notification The Notification that will be passed * * @exception MBeanException if an object initializer throws an * exception * @exception RuntimeOperationsException wraps IllegalArgumentException * when the specified notification is null or invalid */ @Override public void sendNotification(Notification notification) throws MBeanException, RuntimeOperationsException { if (notification == null) throw new RuntimeOperationsException (new IllegalArgumentException("Notification is null"), "Notification is null"); if (generalBroadcaster == null) return; // This means there are no registered listeners generalBroadcaster.sendNotification(notification); } /** * Send a Notification which contains the specified string * as a jmx.modelmbean.generic notification. * * @param message The message string to be passed * * @exception MBeanException if an object initializer throws an * exception * @exception RuntimeOperationsException wraps IllegalArgumentException * when the specified notification is null or invalid */ @Override public void sendNotification(String message) throws MBeanException, RuntimeOperationsException { if (message == null) throw new RuntimeOperationsException (new IllegalArgumentException("Message is null"), "Message is null"); Notification notification = new Notification ("jmx.modelmbean.generic", this, 1, message); sendNotification(notification); } // ---------------------------------------- NotificationBroadcaster Methods /** * Add a notification event listener to this MBean. * * @param listener Listener that will receive event notifications * @param filter Filter object used to filter event notifications * actually delivered, or null for no filtering * @param handback Handback object to be sent along with event * notifications * * @exception IllegalArgumentException if the listener parameter is null */ @Override public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { if (listener == null) throw new IllegalArgumentException("Listener is null"); if( log.isDebugEnabled() ) log.debug("addNotificationListener " + listener); if (generalBroadcaster == null) generalBroadcaster = new BaseNotificationBroadcaster(); generalBroadcaster.addNotificationListener (listener, filter, handback); // We'll send the attribute change notifications to all listeners ( who care ) // The normal filtering can be used. // The problem is that there is no other way to add attribute change listeners // to a model mbean ( AFAIK ). I suppose the spec should be fixed. if (attributeBroadcaster == null) attributeBroadcaster = new BaseNotificationBroadcaster(); if( log.isDebugEnabled() ) log.debug("addAttributeNotificationListener " + listener); attributeBroadcaster.addNotificationListener (listener, filter, handback); } /** * Return an MBeanNotificationInfo object describing the * notifications sent by this MBean. */ @Override public MBeanNotificationInfo[] getNotificationInfo() { // Acquire the set of application notifications MBeanNotificationInfo current[] = getMBeanInfo().getNotifications(); if (current == null) current = new MBeanNotificationInfo[0]; MBeanNotificationInfo response[] = new MBeanNotificationInfo[current.length + 2]; // Descriptor descriptor = null; // Fill in entry for general notifications // descriptor = new DescriptorSupport // (new String[] { "name=GENERIC", // "descriptorType=notification", // "log=T", // "severity=5", // "displayName=jmx.modelmbean.generic" }); response[0] = new MBeanNotificationInfo (new String[] { "jmx.modelmbean.generic" }, "GENERIC", "Text message notification from the managed resource"); //descriptor); // Fill in entry for attribute change notifications // descriptor = new DescriptorSupport // (new String[] { "name=ATTRIBUTE_CHANGE", // "descriptorType=notification", // "log=T", // "severity=5", // "displayName=jmx.attribute.change" }); response[1] = new MBeanNotificationInfo (new String[] { "jmx.attribute.change" }, "ATTRIBUTE_CHANGE", "Observed MBean attribute value has changed"); //descriptor); // Copy remaining notifications as reported by the application System.arraycopy(current, 0, response, 2, current.length); return (response); } /** * Remove a notification event listener from this MBean. * * @param listener The listener to be removed (any and all registrations * for this listener will be eliminated) * * @exception ListenerNotFoundException if this listener is not * registered in the MBean */ @Override public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { if (listener == null) throw new IllegalArgumentException("Listener is null"); if (generalBroadcaster == null) generalBroadcaster = new BaseNotificationBroadcaster(); generalBroadcaster.removeNotificationListener(listener); } public String getModelerType() { return resourceType; } public String getClassName() { return getModelerType(); } public ObjectName getJmxName() { return oname; } public String getObjectName() { if (oname != null) { return oname.toString(); } else { return null; } } // -------------------- Registration -------------------- // XXX We can add some method patterns here- like setName() and // setDomain() for code that doesn't implement the Registration @Override public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { if( log.isDebugEnabled()) log.debug("preRegister " + resource + " " + name ); oname=name; if( resource instanceof MBeanRegistration ) { oname = ((MBeanRegistration)resource).preRegister(server, name ); } return oname; } @Override public void postRegister(Boolean registrationDone) { if( resource instanceof MBeanRegistration ) { ((MBeanRegistration)resource).postRegister(registrationDone); } } @Override public void preDeregister() throws Exception { if( resource instanceof MBeanRegistration ) { ((MBeanRegistration)resource).preDeregister(); } } @Override public void postDeregister() { if( resource instanceof MBeanRegistration ) { ((MBeanRegistration)resource).postDeregister(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy