| /* | |
| * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* | |
| * @author IBM Corp. | |
| * | |
| * Copyright IBM Corp. 1999-2000. All rights reserved. | |
| */ | |
| package javax.management.modelmbean; | |
| import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER; | |
| import java.io.FileOutputStream; | |
| import java.io.PrintStream; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.lang.reflect.Method; | |
| import java.util.Date; | |
| import java.util.HashMap; | |
| import java.util.HashSet; | |
| import java.lang.System.Logger.Level; | |
| import java.util.Map; | |
| import java.util.Set; | |
| import java.util.Vector; | |
| import javax.management.Attribute; | |
| import javax.management.AttributeChangeNotification; | |
| import javax.management.AttributeChangeNotificationFilter; | |
| import javax.management.AttributeList; | |
| import javax.management.AttributeNotFoundException; | |
| import javax.management.Descriptor; | |
| import javax.management.InstanceNotFoundException; | |
| import javax.management.InvalidAttributeValueException; | |
| import javax.management.ListenerNotFoundException; | |
| 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.MBeanRegistration; | |
| import javax.management.MBeanServer; | |
| import javax.management.MBeanServerFactory; | |
| import javax.management.Notification; | |
| import javax.management.NotificationBroadcasterSupport; | |
| import javax.management.NotificationEmitter; | |
| 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.ServiceNotFoundException; | |
| import javax.management.loading.ClassLoaderRepository; | |
| import sun.reflect.misc.MethodUtil; | |
| /** | |
| * This class is the implementation of a ModelMBean. An appropriate | |
| * implementation of a ModelMBean must be shipped with every JMX Agent | |
| * and the class must be named RequiredModelMBean. | |
| * <P> | |
| * Java resources wishing to be manageable instantiate the | |
| * RequiredModelMBean using the MBeanServer's createMBean method. | |
| * The resource then sets the MBeanInfo and Descriptors for the | |
| * RequiredModelMBean instance. The attributes and operations exposed | |
| * via the ModelMBeanInfo for the ModelMBean are accessible | |
| * from MBeans, connectors/adaptors like other MBeans. Through the | |
| * Descriptors, values and methods in the managed application can be | |
| * defined and mapped to attributes and operations of the ModelMBean. | |
| * This mapping can be defined dynamically and programmatically at runtime. | |
| * <P> | |
| * Every RequiredModelMBean which is instantiated in the MBeanServer | |
| * becomes manageable:<br> | |
| * its attributes and operations become remotely accessible through the | |
| * connectors/adaptors connected to that MBeanServer. | |
| * <P> | |
| * A Java object cannot be registered in the MBeanServer unless it is a | |
| * JMX compliant MBean. By instantiating a RequiredModelMBean, resources | |
| * are guaranteed that the MBean is valid. | |
| * | |
| * MBeanException and RuntimeOperationsException must be thrown on every | |
| * public method. This allows for wrapping exceptions from distributed | |
| * communications (RMI, EJB, etc.) | |
| * | |
| * @since 1.5 | |
| */ | |
| public class RequiredModelMBean | |
| implements ModelMBean, MBeanRegistration, NotificationEmitter { | |
| /*************************************/ | |
| /* attributes */ | |
| /*************************************/ | |
| ModelMBeanInfo modelMBeanInfo; | |
| /* Notification broadcaster for any notification to be sent | |
| * from the application through the RequiredModelMBean. */ | |
| private NotificationBroadcasterSupport generalBroadcaster = null; | |
| /* Notification broadcaster for attribute change notifications */ | |
| private NotificationBroadcasterSupport attributeBroadcaster = null; | |
| /* handle, name, or reference for instance on which the actual invoke | |
| * and operations will be executed */ | |
| private Object managedResource = null; | |
| /* records the registering in MBeanServer */ | |
| private boolean registered = false; | |
| private transient MBeanServer server = null; | |
| /*************************************/ | |
| /* constructors */ | |
| /*************************************/ | |
| /** | |
| * Constructs an <CODE>RequiredModelMBean</CODE> with an empty | |
| * ModelMBeanInfo. | |
| * <P> | |
| * The RequiredModelMBean's MBeanInfo and Descriptors | |
| * can be customized using the {@link #setModelMBeanInfo} method. | |
| * After the RequiredModelMBean's MBeanInfo and Descriptors are | |
| * customized, the RequiredModelMBean can be registered with | |
| * the MBeanServer. | |
| * | |
| * @exception MBeanException Wraps a distributed communication Exception. | |
| * | |
| * @exception RuntimeOperationsException Wraps a {@link | |
| * RuntimeException} during the construction of the object. | |
| **/ | |
| public RequiredModelMBean() | |
| throws MBeanException, RuntimeOperationsException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| modelMBeanInfo = createDefaultModelMBeanInfo(); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /** | |
| * Constructs a RequiredModelMBean object using ModelMBeanInfo passed in. | |
| * As long as the RequiredModelMBean is not registered | |
| * with the MBeanServer yet, the RequiredModelMBean's MBeanInfo and | |
| * Descriptors can be customized using the {@link #setModelMBeanInfo} | |
| * method. | |
| * After the RequiredModelMBean's MBeanInfo and Descriptors are | |
| * customized, the RequiredModelMBean can be registered with the | |
| * MBeanServer. | |
| * | |
| * @param mbi The ModelMBeanInfo object to be used by the | |
| * RequiredModelMBean. The given ModelMBeanInfo is cloned | |
| * and modified as specified by {@link #setModelMBeanInfo} | |
| * | |
| * @exception MBeanException Wraps a distributed communication Exception. | |
| * @exception RuntimeOperationsException Wraps an | |
| * {@link java.lang.IllegalArgumentException}: | |
| * The MBeanInfo passed in parameter is null. | |
| * | |
| **/ | |
| public RequiredModelMBean(ModelMBeanInfo mbi) | |
| throws MBeanException, RuntimeOperationsException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| setModelMBeanInfo(mbi); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /*************************************/ | |
| /* initializers */ | |
| /*************************************/ | |
| /** | |
| * Initializes a ModelMBean object using ModelMBeanInfo passed in. | |
| * This method makes it possible to set a customized ModelMBeanInfo on | |
| * the ModelMBean as long as it is not registered with the MBeanServer. | |
| * <br> | |
| * Once the ModelMBean's ModelMBeanInfo (with Descriptors) are | |
| * customized and set on the ModelMBean, the ModelMBean be | |
| * registered with the MBeanServer. | |
| * <P> | |
| * If the ModelMBean is currently registered, this method throws | |
| * a {@link javax.management.RuntimeOperationsException} wrapping an | |
| * {@link IllegalStateException} | |
| * <P> | |
| * If the given <var>inModelMBeanInfo</var> does not contain any | |
| * {@link ModelMBeanNotificationInfo} for the <code>GENERIC</code> | |
| * or <code>ATTRIBUTE_CHANGE</code> notifications, then the | |
| * RequiredModelMBean will supply its own default | |
| * {@link ModelMBeanNotificationInfo ModelMBeanNotificationInfo}s for | |
| * those missing notifications. | |
| * | |
| * @param mbi The ModelMBeanInfo object to be used | |
| * by the ModelMBean. | |
| * | |
| * @exception MBeanException Wraps a distributed communication | |
| * Exception. | |
| * @exception RuntimeOperationsException | |
| * <ul><li>Wraps an {@link IllegalArgumentException} if | |
| * the MBeanInfo passed in parameter is null.</li> | |
| * <li>Wraps an {@link IllegalStateException} if the ModelMBean | |
| * is currently registered in the MBeanServer.</li> | |
| * </ul> | |
| * | |
| **/ | |
| public void setModelMBeanInfo(ModelMBeanInfo mbi) | |
| throws MBeanException, RuntimeOperationsException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (mbi == null) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "ModelMBeanInfo is null: Raising exception."); | |
| } | |
| final RuntimeException x = new | |
| IllegalArgumentException("ModelMBeanInfo must not be null"); | |
| final String exceptionText = | |
| "Exception occurred trying to initialize the " + | |
| "ModelMBeanInfo of the RequiredModelMBean"; | |
| throw new RuntimeOperationsException(x,exceptionText); | |
| } | |
| if (registered) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "RequiredMBean is registered: Raising exception."); | |
| } | |
| final String exceptionText = | |
| "Exception occurred trying to set the " + | |
| "ModelMBeanInfo of the RequiredModelMBean"; | |
| final RuntimeException x = new IllegalStateException( | |
| "cannot call setModelMBeanInfo while ModelMBean is registered"); | |
| throw new RuntimeOperationsException(x,exceptionText); | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Setting ModelMBeanInfo to " + printModelMBeanInfo(mbi)); | |
| int noOfNotifications = 0; | |
| if (mbi.getNotifications() != null) { | |
| noOfNotifications = mbi.getNotifications().length; | |
| } | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "ModelMBeanInfo notifications has " + | |
| noOfNotifications + " elements"); | |
| } | |
| modelMBeanInfo = (ModelMBeanInfo)mbi.clone(); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "set mbeanInfo to: "+ | |
| printModelMBeanInfo(modelMBeanInfo)); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /** | |
| * Sets the instance handle of the object against which to | |
| * execute all methods in this ModelMBean management interface | |
| * (MBeanInfo and Descriptors). | |
| * | |
| * @param mr Object that is the managed resource | |
| * @param mr_type The type of reference for the managed resource. | |
| * <br>Can be: "ObjectReference", "Handle", "IOR", "EJBHandle", | |
| * or "RMIReference". | |
| * <br>In this implementation only "ObjectReference" is supported. | |
| * | |
| * @exception MBeanException The initializer of the object has | |
| * thrown an exception. | |
| * @exception InstanceNotFoundException The managed resource | |
| * object could not be found | |
| * @exception InvalidTargetObjectTypeException The managed | |
| * resource type should be "ObjectReference". | |
| * @exception RuntimeOperationsException Wraps a {@link | |
| * RuntimeException} when setting the resource. | |
| **/ | |
| public void setManagedResource(Object mr, String mr_type) | |
| throws MBeanException, RuntimeOperationsException, | |
| InstanceNotFoundException, InvalidTargetObjectTypeException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| // check that the mr_type is supported by this JMXAgent | |
| // only "objectReference" is supported | |
| if ((mr_type == null) || | |
| (! mr_type.equalsIgnoreCase("objectReference"))) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Managed Resource Type is not supported: " + mr_type); | |
| } | |
| throw new InvalidTargetObjectTypeException(mr_type); | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Managed Resource is valid"); | |
| } | |
| managedResource = mr; | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /** | |
| * <p>Instantiates this MBean instance with the data found for | |
| * the MBean in the persistent store. The data loaded could include | |
| * attribute and operation values.</p> | |
| * | |
| * <p>This method should be called during construction or | |
| * initialization of this instance, and before the MBean is | |
| * registered with the MBeanServer.</p> | |
| * | |
| * <p>If the implementation of this class does not support | |
| * persistence, an {@link MBeanException} wrapping a {@link | |
| * ServiceNotFoundException} is thrown.</p> | |
| * | |
| * @exception MBeanException Wraps another exception, or | |
| * persistence is not supported | |
| * @exception RuntimeOperationsException Wraps exceptions from the | |
| * persistence mechanism | |
| * @exception InstanceNotFoundException Could not find or load | |
| * this MBean from persistent storage | |
| */ | |
| public void load() | |
| throws MBeanException, RuntimeOperationsException, | |
| InstanceNotFoundException { | |
| final ServiceNotFoundException x = new ServiceNotFoundException( | |
| "Persistence not supported for this MBean"); | |
| throw new MBeanException(x, x.getMessage()); | |
| } | |
| /** | |
| * <p>Captures the current state of this MBean instance and writes | |
| * it out to the persistent store. The state stored could include | |
| * attribute and operation values.</p> | |
| * | |
| * <p>If the implementation of this class does not support | |
| * persistence, an {@link MBeanException} wrapping a {@link | |
| * ServiceNotFoundException} is thrown.</p> | |
| * | |
| * <p>Persistence policy from the MBean and attribute descriptor | |
| * is used to guide execution of this method. The MBean should be | |
| * stored if 'persistPolicy' field is:</p> | |
| * | |
| * <PRE>{@literal != "never" | |
| * = "always" | |
| * = "onTimer" and now > 'lastPersistTime' + 'persistPeriod' | |
| * = "NoMoreOftenThan" and now > 'lastPersistTime' + 'persistPeriod' | |
| * = "onUnregister" | |
| * }</PRE> | |
| * | |
| * <p>Do not store the MBean if 'persistPolicy' field is:</p> | |
| * <PRE>{@literal | |
| * = "never" | |
| * = "onUpdate" | |
| * = "onTimer" && now < 'lastPersistTime' + 'persistPeriod' | |
| * }</PRE> | |
| * | |
| * @exception MBeanException Wraps another exception, or | |
| * persistence is not supported | |
| * @exception RuntimeOperationsException Wraps exceptions from the | |
| * persistence mechanism | |
| * @exception InstanceNotFoundException Could not find/access the | |
| * persistent store | |
| */ | |
| public void store() | |
| throws MBeanException, RuntimeOperationsException, | |
| InstanceNotFoundException { | |
| final ServiceNotFoundException x = new ServiceNotFoundException( | |
| "Persistence not supported for this MBean"); | |
| throw new MBeanException(x, x.getMessage()); | |
| } | |
| /*************************************/ | |
| /* DynamicMBean Interface */ | |
| /*************************************/ | |
| /** | |
| * The resolveForCacheValue method checks the descriptor passed in to | |
| * see if there is a valid cached value in the descriptor. | |
| * The valid value will be in the 'value' field if there is one. | |
| * If the 'currencyTimeLimit' field in the descriptor is: | |
| * <ul> | |
| * <li><b><0</b> Then the value is not cached and is never valid. | |
| * Null is returned. The 'value' and 'lastUpdatedTimeStamp' | |
| * fields are cleared.</li> | |
| * <li><b>=0</b> Then the value is always cached and always valid. | |
| * The 'value' field is returned. | |
| * The 'lastUpdatedTimeStamp' field is not checked.</li> | |
| * <li><b>>0</b> Represents the number of seconds that the | |
| * 'value' field is valid. | |
| * The 'value' field is no longer valid when | |
| * 'lastUpdatedTimeStamp' + 'currencyTimeLimit' > Now. | |
| * <ul> | |
| * <li>When 'value' is valid, 'valid' is returned.</li> | |
| * <li>When 'value' is no longer valid then null is returned and | |
| * 'value' and 'lastUpdatedTimeStamp' fields are cleared.</li> | |
| * </ul> | |
| * </li> | |
| * </ul> | |
| * | |
| **/ | |
| private Object resolveForCacheValue(Descriptor descr) | |
| throws MBeanException, RuntimeOperationsException { | |
| final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.TRACE); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| Object response = null; | |
| boolean resetValue = false, returnCachedValue = true; | |
| long currencyPeriod = 0; | |
| if (descr == null) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Input Descriptor is null"); | |
| } | |
| return response; | |
| } | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "descriptor is " + descr); | |
| } | |
| final Descriptor mmbDescr = modelMBeanInfo.getMBeanDescriptor(); | |
| if (mmbDescr == null) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "MBean Descriptor is null"); | |
| } | |
| //return response; | |
| } | |
| Object objExpTime = descr.getFieldValue("currencyTimeLimit"); | |
| String expTime; | |
| if (objExpTime != null) { | |
| expTime = objExpTime.toString(); | |
| } else { | |
| expTime = null; | |
| } | |
| if ((expTime == null) && (mmbDescr != null)) { | |
| objExpTime = mmbDescr.getFieldValue("currencyTimeLimit"); | |
| if (objExpTime != null) { | |
| expTime = objExpTime.toString(); | |
| } else { | |
| expTime = null; | |
| } | |
| } | |
| if (expTime != null) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "currencyTimeLimit: " + expTime); | |
| } | |
| // convert seconds to milliseconds for time comparison | |
| currencyPeriod = Long.parseLong(expTime) * 1000; | |
| if (currencyPeriod < 0) { | |
| /* if currencyTimeLimit is -1 then value is never cached */ | |
| returnCachedValue = false; | |
| resetValue = true; | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| currencyPeriod + ": never Cached"); | |
| } | |
| } else if (currencyPeriod == 0) { | |
| /* if currencyTimeLimit is 0 then value is always cached */ | |
| returnCachedValue = true; | |
| resetValue = false; | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "always valid Cache"); | |
| } | |
| } else { | |
| Object objtStamp = | |
| descr.getFieldValue("lastUpdatedTimeStamp"); | |
| String tStamp; | |
| if (objtStamp != null) tStamp = objtStamp.toString(); | |
| else tStamp = null; | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "lastUpdatedTimeStamp: " + tStamp); | |
| } | |
| if (tStamp == null) | |
| tStamp = "0"; | |
| long lastTime = Long.parseLong(tStamp); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "currencyPeriod:" + currencyPeriod + | |
| " lastUpdatedTimeStamp:" + lastTime); | |
| } | |
| long now = (new Date()).getTime(); | |
| if (now < (lastTime + currencyPeriod)) { | |
| returnCachedValue = true; | |
| resetValue = false; | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| " timed valid Cache for " + now + " < " + | |
| (lastTime + currencyPeriod)); | |
| } | |
| } else { /* value is expired */ | |
| returnCachedValue = false; | |
| resetValue = true; | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "timed expired cache for " + now + " > " + | |
| (lastTime + currencyPeriod)); | |
| } | |
| } | |
| } | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "returnCachedValue:" + returnCachedValue + | |
| " resetValue: " + resetValue); | |
| } | |
| if (returnCachedValue == true) { | |
| Object currValue = descr.getFieldValue("value"); | |
| if (currValue != null) { | |
| /* error/validity check return value here */ | |
| response = currValue; | |
| /* need to cast string cached value to type */ | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "valid Cache value: " + currValue); | |
| } | |
| } else { | |
| response = null; | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "no Cached value"); | |
| } | |
| } | |
| } | |
| if (resetValue == true) { | |
| /* value is not current, so remove it */ | |
| descr.removeField("lastUpdatedTimeStamp"); | |
| descr.removeField("value"); | |
| response = null; | |
| modelMBeanInfo.setDescriptor(descr,null); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "reset cached value to null"); | |
| } | |
| } | |
| } | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| return response; | |
| } | |
| /** | |
| * Returns the attributes, operations, constructors and notifications | |
| * that this RequiredModelMBean exposes for management. | |
| * | |
| * @return An instance of ModelMBeanInfo allowing retrieval all | |
| * attributes, operations, and Notifications of this MBean. | |
| * | |
| **/ | |
| public MBeanInfo getMBeanInfo() { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (modelMBeanInfo == null) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "modelMBeanInfo is null"); | |
| } | |
| modelMBeanInfo = createDefaultModelMBeanInfo(); | |
| //return new ModelMBeanInfo(" ", "", null, null, null, null); | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "ModelMBeanInfo is " + | |
| modelMBeanInfo.getClassName() + " for " + | |
| modelMBeanInfo.getDescription()); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| printModelMBeanInfo(modelMBeanInfo)); | |
| } | |
| return((MBeanInfo) modelMBeanInfo.clone()); | |
| } | |
| private String printModelMBeanInfo(ModelMBeanInfo info) { | |
| final StringBuilder retStr = new StringBuilder(); | |
| if (info == null) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "ModelMBeanInfo to print is null, " + | |
| "printing local ModelMBeanInfo"); | |
| } | |
| info = modelMBeanInfo; | |
| } | |
| retStr.append("\nMBeanInfo for ModelMBean is:"); | |
| retStr.append("\nCLASSNAME: \t").append(info.getClassName()); | |
| retStr.append("\nDESCRIPTION: \t").append(info.getDescription()); | |
| try { | |
| retStr.append("\nMBEAN DESCRIPTOR: \t").append(info.getMBeanDescriptor()); | |
| } catch (Exception e) { | |
| retStr.append("\nMBEAN DESCRIPTOR: \t is invalid"); | |
| } | |
| retStr.append("\nATTRIBUTES"); | |
| final MBeanAttributeInfo[] attrInfo = info.getAttributes(); | |
| if ((attrInfo != null) && (attrInfo.length>0)) { | |
| for (int i=0; i<attrInfo.length; i++) { | |
| final ModelMBeanAttributeInfo attInfo = | |
| (ModelMBeanAttributeInfo)attrInfo[i]; | |
| retStr.append(" ** NAME: \t").append(attInfo.getName()); | |
| retStr.append(" DESCR: \t").append(attInfo.getDescription()); | |
| retStr.append(" TYPE: \t").append(attInfo.getType()) | |
| .append(" READ: \t").append(attInfo.isReadable()) | |
| .append(" WRITE: \t").append(attInfo.isWritable()); | |
| retStr.append(" DESCRIPTOR: ").append(attInfo.getDescriptor()); | |
| } | |
| } else { | |
| retStr.append(" ** No attributes **"); | |
| } | |
| retStr.append("\nCONSTRUCTORS"); | |
| final MBeanConstructorInfo[] constrInfo = info.getConstructors(); | |
| if ((constrInfo != null) && (constrInfo.length > 0 )) { | |
| for (int i=0; i<constrInfo.length; i++) { | |
| final ModelMBeanConstructorInfo ctorInfo = | |
| (ModelMBeanConstructorInfo)constrInfo[i]; | |
| retStr.append(" ** NAME: \t").append(ctorInfo.getName()); | |
| retStr.append(" DESCR: \t").append(ctorInfo.getDescription()); | |
| retStr.append(" PARAM: \t") | |
| .append(ctorInfo.getSignature().length) | |
| .append(" parameter(s)"); | |
| retStr.append(" DESCRIPTOR: ").append( | |
| ctorInfo.getDescriptor()); | |
| } | |
| } else { | |
| retStr.append(" ** No Constructors **"); | |
| } | |
| retStr.append("\nOPERATIONS"); | |
| final MBeanOperationInfo[] opsInfo = info.getOperations(); | |
| if ((opsInfo != null) && (opsInfo.length>0)) { | |
| for (int i=0; i<opsInfo.length; i++) { | |
| final ModelMBeanOperationInfo operInfo = | |
| (ModelMBeanOperationInfo)opsInfo[i]; | |
| retStr.append(" ** NAME: \t").append(operInfo.getName()); | |
| retStr.append(" DESCR: \t").append(operInfo.getDescription()); | |
| retStr.append(" PARAM: \t") | |
| .append(operInfo.getSignature().length) | |
| .append(" parameter(s)"); | |
| retStr.append(" DESCRIPTOR: ").append(operInfo.getDescriptor()); | |
| } | |
| } else { | |
| retStr.append(" ** No operations ** "); | |
| } | |
| retStr.append("\nNOTIFICATIONS"); | |
| MBeanNotificationInfo[] notifInfo = info.getNotifications(); | |
| if ((notifInfo != null) && (notifInfo.length>0)) { | |
| for (int i=0; i<notifInfo.length; i++) { | |
| final ModelMBeanNotificationInfo nInfo = | |
| (ModelMBeanNotificationInfo)notifInfo[i]; | |
| retStr.append(" ** NAME: \t").append(nInfo.getName()); | |
| retStr.append(" DESCR: \t").append(nInfo.getDescription()); | |
| retStr.append(" DESCRIPTOR: ").append(nInfo.getDescriptor()); | |
| } | |
| } else { | |
| retStr.append(" ** No notifications **"); | |
| } | |
| retStr.append(" ** ModelMBean: End of MBeanInfo ** "); | |
| return retStr.toString(); | |
| } | |
| /** | |
| * Invokes a method on or through a RequiredModelMBean and returns | |
| * the result of the method execution. | |
| * <P> | |
| * If the given method to be invoked, together with the provided | |
| * signature, matches one of RequiredModelMbean | |
| * accessible methods, this one will be call. Otherwise the call to | |
| * the given method will be tried on the managed resource. | |
| * <P> | |
| * The last value returned by an operation may be cached in | |
| * the operation's descriptor which | |
| * is in the ModelMBeanOperationInfo's descriptor. | |
| * The valid value will be in the 'value' field if there is one. | |
| * If the 'currencyTimeLimit' field in the descriptor is: | |
| * <UL> | |
| * <LI><b><0</b> Then the value is not cached and is never valid. | |
| * The operation method is invoked. | |
| * The 'value' and 'lastUpdatedTimeStamp' fields are cleared.</LI> | |
| * <LI><b>=0</b> Then the value is always cached and always valid. | |
| * The 'value' field is returned. If there is no 'value' field | |
| * then the operation method is invoked for the attribute. | |
| * The 'lastUpdatedTimeStamp' field and `value' fields are set to | |
| * the operation's return value and the current time stamp.</LI> | |
| * <LI><b>>0</b> Represents the number of seconds that the 'value' | |
| * field is valid. | |
| * The 'value' field is no longer valid when | |
| * 'lastUpdatedTimeStamp' + 'currencyTimeLimit' > Now. | |
| * <UL> | |
| * <LI>When 'value' is valid, 'value' is returned.</LI> | |
| * <LI>When 'value' is no longer valid then the operation | |
| * method is invoked. The 'lastUpdatedTimeStamp' field | |
| * and `value' fields are updated.</lI> | |
| * </UL> | |
| * </LI> | |
| * </UL> | |
| * | |
| * <p><b>Note:</b> because of inconsistencies in previous versions of | |
| * this specification, it is recommended not to use negative or zero | |
| * values for <code>currencyTimeLimit</code>. To indicate that a | |
| * cached value is never valid, omit the | |
| * <code>currencyTimeLimit</code> field. To indicate that it is | |
| * always valid, use a very large number for this field.</p> | |
| * | |
| * @param opName The name of the method to be invoked. The | |
| * name can be the fully qualified method name including the | |
| * classname, or just the method name if the classname is | |
| * defined in the 'class' field of the operation descriptor. | |
| * @param opArgs An array containing the parameters to be set | |
| * when the operation is invoked | |
| * @param sig An array containing the signature of the | |
| * operation. The class objects will be loaded using the same | |
| * class loader as the one used for loading the MBean on which | |
| * the operation was invoked. | |
| * | |
| * @return The object returned by the method, which represents the | |
| * result of invoking the method on the specified managed resource. | |
| * | |
| * @exception MBeanException Wraps one of the following Exceptions: | |
| * <UL> | |
| * <LI> An Exception thrown by the managed object's invoked method.</LI> | |
| * <LI> {@link ServiceNotFoundException}: No ModelMBeanOperationInfo or | |
| * no descriptor defined for the specified operation or the managed | |
| * resource is null.</LI> | |
| * <LI> {@link InvalidTargetObjectTypeException}: The 'targetType' | |
| * field value is not 'objectReference'.</LI> | |
| * </UL> | |
| * @exception ReflectionException Wraps an {@link java.lang.Exception} | |
| * thrown while trying to invoke the method. | |
| * @exception RuntimeOperationsException Wraps an | |
| * {@link IllegalArgumentException} Method name is null. | |
| * | |
| **/ | |
| /* | |
| The requirement to be able to invoke methods on the | |
| RequiredModelMBean class itself makes this method considerably | |
| more complicated than it might otherwise be. Note that, unlike | |
| earlier versions, we do not allow you to invoke such methods if | |
| they are not explicitly mentioned in the ModelMBeanInfo. Doing | |
| so was potentially a security problem, and certainly very | |
| surprising. | |
| We do not look for the method in the RequiredModelMBean class | |
| itself if: | |
| (a) there is a "targetObject" field in the Descriptor for the | |
| operation; or | |
| (b) there is a "class" field in the Descriptor for the operation | |
| and the named class is not RequiredModelMBean or one of its | |
| superinterfaces; or | |
| (c) the name of the operation is not the name of a method in | |
| RequiredModelMBean (this is just an optimization). | |
| In cases (a) and (b), if you have gone to the trouble of adding | |
| those fields specifically for this operation then presumably you | |
| do not want RequiredModelMBean's methods to be called. | |
| We have to pay attention to class loading issues. If the | |
| "class" field is present, the named class has to be resolved | |
| relative to RequiredModelMBean's class loader to test the | |
| condition (b) above, and relative to the managed resource's | |
| class loader to ensure that the managed resource is in fact of | |
| the named class (or a subclass). The class names in the sig | |
| array likewise have to be resolved, first against | |
| RequiredModelMBean's class loader, then against the managed | |
| resource's class loader. There is no point in using any other | |
| loader because when we call Method.invoke we must call it on | |
| a Method that is implemented by the target object. | |
| */ | |
| public Object invoke(String opName, Object[] opArgs, String[] sig) | |
| throws MBeanException, ReflectionException { | |
| final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.TRACE); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (opName == null) { | |
| final RuntimeException x = | |
| new IllegalArgumentException("Method name must not be null"); | |
| throw new RuntimeOperationsException(x, | |
| "An exception occurred while trying to " + | |
| "invoke a method on a RequiredModelMBean"); | |
| } | |
| String opClassName = null; | |
| String opMethodName; | |
| // Parse for class name and method | |
| int opSplitter = opName.lastIndexOf('.'); | |
| if (opSplitter > 0) { | |
| opClassName = opName.substring(0,opSplitter); | |
| opMethodName = opName.substring(opSplitter+1); | |
| } else | |
| opMethodName = opName; | |
| /* Ignore anything after a left paren. We keep this for | |
| compatibility but it isn't specified. */ | |
| opSplitter = opMethodName.indexOf('('); | |
| if (opSplitter > 0) | |
| opMethodName = opMethodName.substring(0,opSplitter); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Finding operation " + opName + " as " + opMethodName); | |
| } | |
| ModelMBeanOperationInfo opInfo = | |
| modelMBeanInfo.getOperation(opMethodName); | |
| if (opInfo == null) { | |
| final String msg = | |
| "Operation " + opName + " not in ModelMBeanInfo"; | |
| throw new MBeanException(new ServiceNotFoundException(msg), msg); | |
| } | |
| final Descriptor opDescr = opInfo.getDescriptor(); | |
| if (opDescr == null) { | |
| final String msg = "Operation descriptor null"; | |
| throw new MBeanException(new ServiceNotFoundException(msg), msg); | |
| } | |
| final Object cached = resolveForCacheValue(opDescr); | |
| if (cached != null) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Returning cached value"); | |
| } | |
| return cached; | |
| } | |
| if (opClassName == null) | |
| opClassName = (String) opDescr.getFieldValue("class"); | |
| // may still be null now | |
| opMethodName = (String) opDescr.getFieldValue("name"); | |
| if (opMethodName == null) { | |
| final String msg = | |
| "Method descriptor must include `name' field"; | |
| throw new MBeanException(new ServiceNotFoundException(msg), msg); | |
| } | |
| final String targetTypeField = (String) | |
| opDescr.getFieldValue("targetType"); | |
| if (targetTypeField != null | |
| && !targetTypeField.equalsIgnoreCase("objectReference")) { | |
| final String msg = | |
| "Target type must be objectReference: " + targetTypeField; | |
| throw new MBeanException(new InvalidTargetObjectTypeException(msg), | |
| msg); | |
| } | |
| final Object targetObjectField = opDescr.getFieldValue("targetObject"); | |
| if (tracing && targetObjectField != null) | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Found target object in descriptor"); | |
| /* Now look for the method, either in RequiredModelMBean itself | |
| or in the target object. Set "method" and "targetObject" | |
| appropriately. */ | |
| Method method; | |
| Object targetObject; | |
| method = findRMMBMethod(opMethodName, targetObjectField, | |
| opClassName, sig); | |
| if (method != null) | |
| targetObject = this; | |
| else { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "looking for method in managedResource class"); | |
| } | |
| if (targetObjectField != null) | |
| targetObject = targetObjectField; | |
| else { | |
| targetObject = managedResource; | |
| if (targetObject == null) { | |
| final String msg = | |
| "managedResource for invoke " + opName + | |
| " is null"; | |
| Exception snfe = new ServiceNotFoundException(msg); | |
| throw new MBeanException(snfe); | |
| } | |
| } | |
| Class<?> targetClass = null; | |
| if (opClassName != null) { | |
| try { | |
| final ClassNotFoundException[] caughtException = new ClassNotFoundException[1]; | |
| final ClassLoader targetClassLoader = targetObject.getClass().getClassLoader(); | |
| try { | |
| targetClass = Class.forName(opClassName, false, targetClassLoader); | |
| } catch (ClassNotFoundException e) { | |
| caughtException[0] = e; | |
| } | |
| if (caughtException[0] != null) { | |
| throw caughtException[0]; | |
| } | |
| } catch (ClassNotFoundException e) { | |
| final String msg = | |
| "class for invoke " + opName + " not found"; | |
| throw new ReflectionException(e, msg); | |
| } | |
| } else | |
| targetClass = targetObject.getClass(); | |
| method = resolveMethod(targetClass, opMethodName, sig); | |
| } | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "found " + opMethodName + ", now invoking"); | |
| } | |
| final Object result = | |
| invokeMethod(opName, method, targetObject, opArgs); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "successfully invoked method"); | |
| } | |
| if (result != null) | |
| cacheResult(opInfo, opDescr, result); | |
| return result; | |
| } | |
| private Method resolveMethod(Class<?> targetClass, | |
| String opMethodName, | |
| final String[] sig) | |
| throws ReflectionException { | |
| final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.TRACE); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "resolving " + targetClass.getName() + "." + opMethodName); | |
| } | |
| final Class<?>[] argClasses; | |
| if (sig == null) | |
| argClasses = null; | |
| else { | |
| final ReflectionException[] caughtException = new ReflectionException[1]; | |
| final ClassLoader targetClassLoader = targetClass.getClassLoader(); | |
| argClasses = new Class<?>[sig.length]; | |
| for (int i = 0; i < sig.length; i++) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "resolve type " + sig[i]); | |
| } | |
| argClasses[i] = (Class<?>) primitiveClassMap.get(sig[i]); | |
| if (argClasses[i] == null) { | |
| try { | |
| argClasses[i] = | |
| Class.forName(sig[i], false, targetClassLoader); | |
| } catch (ClassNotFoundException e) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "class not found"); | |
| } | |
| final String msg = "Parameter class not found"; | |
| caughtException[0] = new ReflectionException(e, msg); | |
| } | |
| } | |
| } | |
| if (caughtException[0] != null) { | |
| throw caughtException[0]; | |
| } | |
| } | |
| try { | |
| return targetClass.getMethod(opMethodName, argClasses); | |
| } catch (NoSuchMethodException e) { | |
| final String msg = | |
| "Target method not found: " + targetClass.getName() + "." + | |
| opMethodName; | |
| throw new ReflectionException(e, msg); | |
| } | |
| } | |
| /* Map e.g. "int" to int.class. Goodness knows how many time this | |
| particular wheel has been reinvented. */ | |
| private static final Class<?>[] primitiveClasses = { | |
| int.class, long.class, boolean.class, double.class, | |
| float.class, short.class, byte.class, char.class, | |
| }; | |
| private static final Map<String,Class<?>> primitiveClassMap = | |
| new HashMap<>(); | |
| static { | |
| for (int i = 0; i < primitiveClasses.length; i++) { | |
| final Class<?> c = primitiveClasses[i]; | |
| primitiveClassMap.put(c.getName(), c); | |
| } | |
| } | |
| /* Find a method in RequiredModelMBean as determined by the given | |
| parameters. Return null if there is none, or if the parameters | |
| exclude using it. Called from invoke. */ | |
| private Method findRMMBMethod(String opMethodName, | |
| Object targetObjectField, | |
| String opClassName, | |
| String[] sig) { | |
| final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.TRACE); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "looking for method in RequiredModelMBean class"); | |
| } | |
| if (!isRMMBMethodName(opMethodName)) | |
| return null; | |
| if (targetObjectField != null) | |
| return null; | |
| final Class<RequiredModelMBean> rmmbClass = RequiredModelMBean.class; | |
| Class<?> targetClass = null; | |
| if (opClassName == null) | |
| targetClass = rmmbClass; | |
| else { | |
| final ClassLoader targetClassLoader = rmmbClass.getClassLoader(); | |
| try { | |
| Class<?> clz = Class.forName(opClassName, false, targetClassLoader); | |
| if (!rmmbClass.isAssignableFrom(clz)) { | |
| targetClass = null; | |
| } else { | |
| targetClass = clz; | |
| } | |
| } catch (ClassNotFoundException e) { | |
| } | |
| } | |
| try { | |
| return targetClass != null ? resolveMethod(targetClass, opMethodName, sig) : null; | |
| } catch (ReflectionException e) { | |
| return null; | |
| } | |
| } | |
| /* | |
| * Invoke the given method, and throw the somewhat unpredictable | |
| * appropriate exception if the method itself gets an exception. | |
| */ | |
| private Object invokeMethod(String opName, final Method method, | |
| final Object targetObject, final Object[] opArgs) | |
| throws MBeanException, ReflectionException { | |
| try { | |
| final Throwable[] caughtException = new Throwable[1]; | |
| Object rslt = null; | |
| try { | |
| rslt = MethodUtil.invoke(method, targetObject, opArgs); | |
| } catch (InvocationTargetException | IllegalAccessException e) { | |
| caughtException[0] = e; | |
| } | |
| if (caughtException[0] != null) { | |
| if (caughtException[0] instanceof Exception) { | |
| throw (Exception)caughtException[0]; | |
| } else if(caughtException[0] instanceof Error) { | |
| throw (Error)caughtException[0]; | |
| } | |
| } | |
| return rslt; | |
| } catch (RuntimeException re) { | |
| throw new RuntimeOperationsException(re, | |
| "RuntimeException occurred in RequiredModelMBean "+ | |
| "while trying to invoke operation " + opName); | |
| } catch (IllegalAccessException iae) { | |
| throw new ReflectionException(iae, | |
| "IllegalAccessException occurred in " + | |
| "RequiredModelMBean while trying to " + | |
| "invoke operation " + opName); | |
| } catch (InvocationTargetException ite) { | |
| Throwable mmbTargEx = ite.getCause(); | |
| if (mmbTargEx instanceof RuntimeException) { | |
| throw new MBeanException ((RuntimeException)mmbTargEx, | |
| "RuntimeException thrown in RequiredModelMBean "+ | |
| "while trying to invoke operation " + opName); | |
| } else if (mmbTargEx instanceof Error) { | |
| throw new RuntimeErrorException((Error)mmbTargEx, | |
| "Error occurred in RequiredModelMBean while trying "+ | |
| "to invoke operation " + opName); | |
| } else if (mmbTargEx instanceof ReflectionException) { | |
| throw (ReflectionException) mmbTargEx; | |
| } else { | |
| throw new MBeanException ((Exception)mmbTargEx, | |
| "Exception thrown in RequiredModelMBean "+ | |
| "while trying to invoke operation " + opName); | |
| } | |
| } catch (Error err) { | |
| throw new RuntimeErrorException(err, | |
| "Error occurred in RequiredModelMBean while trying "+ | |
| "to invoke operation " + opName); | |
| } catch (Exception e) { | |
| throw new ReflectionException(e, | |
| "Exception occurred in RequiredModelMBean while " + | |
| "trying to invoke operation " + opName); | |
| } | |
| } | |
| /* | |
| * Cache the result of an operation in the descriptor, if that is | |
| * called for by the descriptor's configuration. Note that we | |
| * don't remember operation parameters when caching the result, so | |
| * this is unlikely to be useful if there are any. | |
| */ | |
| private void cacheResult(ModelMBeanOperationInfo opInfo, | |
| Descriptor opDescr, Object result) | |
| throws MBeanException { | |
| Descriptor mmbDesc = | |
| modelMBeanInfo.getMBeanDescriptor(); | |
| Object objctl = | |
| opDescr.getFieldValue("currencyTimeLimit"); | |
| String ctl; | |
| if (objctl != null) { | |
| ctl = objctl.toString(); | |
| } else { | |
| ctl = null; | |
| } | |
| if ((ctl == null) && (mmbDesc != null)) { | |
| objctl = | |
| mmbDesc.getFieldValue("currencyTimeLimit"); | |
| if (objctl != null) { | |
| ctl = objctl.toString(); | |
| } else { | |
| ctl = null; | |
| } | |
| } | |
| if ((ctl != null) && !(ctl.equals("-1"))) { | |
| opDescr.setField("value", result); | |
| opDescr.setField("lastUpdatedTimeStamp", | |
| String.valueOf((new Date()).getTime())); | |
| modelMBeanInfo.setDescriptor(opDescr, | |
| "operation"); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "new descriptor is " + opDescr); | |
| } | |
| } | |
| } | |
| /* | |
| * Determine whether the given name is the name of a public method | |
| * in this class. This is only an optimization: it prevents us | |
| * from trying to do argument type lookups and reflection on a | |
| * method that will obviously fail because it has the wrong name. | |
| * | |
| * The first time this method is called we do the reflection, and | |
| * every other time we reuse the remembered values. | |
| * | |
| * It's conceivable that the (possibly malicious) first caller | |
| * doesn't have the required permissions to do reflection, in | |
| * which case we don't touch anything so as not to interfere | |
| * with a later permissionful caller. | |
| */ | |
| private static Set<String> rmmbMethodNames; | |
| private static synchronized boolean isRMMBMethodName(String name) { | |
| if (rmmbMethodNames == null) { | |
| try { | |
| Set<String> names = new HashSet<>(); | |
| Method[] methods = RequiredModelMBean.class.getMethods(); | |
| for (int i = 0; i < methods.length; i++) | |
| names.add(methods[i].getName()); | |
| rmmbMethodNames = names; | |
| } catch (Exception e) { | |
| return true; | |
| // This is only an optimization so we'll go on to discover | |
| // whether the name really is an RMMB method. | |
| } | |
| } | |
| return rmmbMethodNames.contains(name); | |
| } | |
| /** | |
| * Returns the value of a specific attribute defined for this | |
| * ModelMBean. | |
| * The last value returned by an attribute may be cached in the | |
| * attribute's descriptor. | |
| * The valid value will be in the 'value' field if there is one. | |
| * If the 'currencyTimeLimit' field in the descriptor is: | |
| * <UL> | |
| * <LI> <b><0</b> Then the value is not cached and is never valid. | |
| * The getter method is invoked for the attribute. | |
| * The 'value' and 'lastUpdatedTimeStamp' fields are cleared.</LI> | |
| * <LI> <b>=0</b> Then the value is always cached and always valid. | |
| * The 'value' field is returned. If there is no'value' field | |
| * then the getter method is invoked for the attribute. | |
| * The 'lastUpdatedTimeStamp' field and `value' fields are set | |
| * to the attribute's value and the current time stamp.</LI> | |
| * <LI> <b>>0</b> Represents the number of seconds that the 'value' | |
| * field is valid. | |
| * The 'value' field is no longer valid when | |
| * 'lastUpdatedTimeStamp' + 'currencyTimeLimit' > Now. | |
| * <UL> | |
| * <LI>When 'value' is valid, 'value' is returned.</LI> | |
| * <LI>When 'value' is no longer valid then the getter | |
| * method is invoked for the attribute. | |
| * The 'lastUpdatedTimeStamp' field and `value' fields | |
| * are updated.</LI> | |
| * </UL></LI> | |
| * </UL> | |
| * | |
| * <p><b>Note:</b> because of inconsistencies in previous versions of | |
| * this specification, it is recommended not to use negative or zero | |
| * values for <code>currencyTimeLimit</code>. To indicate that a | |
| * cached value is never valid, omit the | |
| * <code>currencyTimeLimit</code> field. To indicate that it is | |
| * always valid, use a very large number for this field.</p> | |
| * | |
| * <p>If the 'getMethod' field contains the name of a valid | |
| * operation descriptor, then the method described by the | |
| * operation descriptor is executed. The response from the | |
| * method is returned as the value of the attribute. If the | |
| * operation fails or the returned value is not compatible with | |
| * the declared type of the attribute, an exception will be thrown.</p> | |
| * | |
| * <p>If no 'getMethod' field is defined then the default value of the | |
| * attribute is returned. If the returned value is not compatible with | |
| * the declared type of the attribute, an exception will be thrown.</p> | |
| * | |
| * <p>The declared type of the attribute is the String returned by | |
| * {@link ModelMBeanAttributeInfo#getType()}. A value is compatible | |
| * with this type if one of the following is true: | |
| * <ul> | |
| * <li>the value is null;</li> | |
| * <li>the declared name is a primitive type name (such as "int") | |
| * and the value is an instance of the corresponding wrapper | |
| * type (such as java.lang.Integer);</li> | |
| * <li>the name of the value's class is identical to the declared name;</li> | |
| * <li>the declared name can be loaded by the value's class loader and | |
| * produces a class to which the value can be assigned.</li> | |
| * </ul> | |
| * | |
| * <p>In this implementation, in every case where the getMethod needs to | |
| * be called, because the method is invoked through the standard "invoke" | |
| * method and thus needs operationInfo, an operation must be specified | |
| * for that getMethod so that the invocation works correctly.</p> | |
| * | |
| * @param attrName A String specifying the name of the | |
| * attribute to be retrieved. It must match the name of a | |
| * ModelMBeanAttributeInfo. | |
| * | |
| * @return The value of the retrieved attribute from the | |
| * descriptor 'value' field or from the invocation of the | |
| * operation in the 'getMethod' field of the descriptor. | |
| * | |
| * @exception AttributeNotFoundException The specified attribute is | |
| * not accessible in the MBean. | |
| * The following cases may result in an AttributeNotFoundException: | |
| * <UL> | |
| * <LI> No ModelMBeanInfo was found for the Model MBean.</LI> | |
| * <LI> No ModelMBeanAttributeInfo was found for the specified | |
| * attribute name.</LI> | |
| * <LI> The ModelMBeanAttributeInfo isReadable method returns | |
| * 'false'.</LI> | |
| * </UL> | |
| * @exception MBeanException Wraps one of the following Exceptions: | |
| * <UL> | |
| * <LI> {@link InvalidAttributeValueException}: A wrong value type | |
| * was received from the attribute's getter method or | |
| * no 'getMethod' field defined in the descriptor for | |
| * the attribute and no default value exists.</LI> | |
| * <LI> {@link ServiceNotFoundException}: No | |
| * ModelMBeanOperationInfo defined for the attribute's | |
| * getter method or no descriptor associated with the | |
| * ModelMBeanOperationInfo or the managed resource is | |
| * null.</LI> | |
| * <LI> {@link InvalidTargetObjectTypeException} The 'targetType' | |
| * field value is not 'objectReference'.</LI> | |
| * <LI> An Exception thrown by the managed object's getter.</LI> | |
| * </UL> | |
| * @exception ReflectionException Wraps an {@link java.lang.Exception} | |
| * thrown while trying to invoke the getter. | |
| * @exception RuntimeOperationsException Wraps an | |
| * {@link IllegalArgumentException}: The attribute name in | |
| * parameter is null. | |
| * | |
| * @see #setAttribute(javax.management.Attribute) | |
| **/ | |
| public Object getAttribute(String attrName) | |
| throws AttributeNotFoundException, MBeanException, | |
| ReflectionException { | |
| if (attrName == null) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("attributeName must not be null"), | |
| "Exception occurred trying to get attribute of a " + | |
| "RequiredModelMBean"); | |
| final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.TRACE); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry with " + attrName); | |
| } | |
| /* Check attributeDescriptor for getMethod */ | |
| Object response; | |
| try { | |
| if (modelMBeanInfo == null) | |
| throw new AttributeNotFoundException( | |
| "getAttribute failed: ModelMBeanInfo not found for "+ | |
| attrName); | |
| ModelMBeanAttributeInfo attrInfo = modelMBeanInfo.getAttribute(attrName); | |
| Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor(); | |
| if (attrInfo == null) | |
| throw new AttributeNotFoundException("getAttribute failed:"+ | |
| " ModelMBeanAttributeInfo not found for " + attrName); | |
| Descriptor attrDescr = attrInfo.getDescriptor(); | |
| if (attrDescr != null) { | |
| if (!attrInfo.isReadable()) | |
| throw new AttributeNotFoundException( | |
| "getAttribute failed: " + attrName + | |
| " is not readable "); | |
| response = resolveForCacheValue(attrDescr); | |
| /* return current cached value */ | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "*** cached value is " + response); | |
| } | |
| if (response == null) { | |
| /* no cached value, run getMethod */ | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "**** cached value is null - getting getMethod"); | |
| } | |
| String attrGetMethod = | |
| (String)(attrDescr.getFieldValue("getMethod")); | |
| if (attrGetMethod != null) { | |
| /* run method from operations descriptor */ | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "invoking a getMethod for " + attrName); | |
| } | |
| Object getResponse = | |
| invoke(attrGetMethod, new Object[] {}, | |
| new String[] {}); | |
| if (getResponse != null) { | |
| // error/validity check return value here | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "got a non-null response " + | |
| "from getMethod\n"); | |
| } | |
| response = getResponse; | |
| // change cached value in attribute descriptor | |
| Object objctl = | |
| attrDescr.getFieldValue("currencyTimeLimit"); | |
| String ctl; | |
| if (objctl != null) ctl = objctl.toString(); | |
| else ctl = null; | |
| if ((ctl == null) && (mmbDesc != null)) { | |
| objctl = mmbDesc. | |
| getFieldValue("currencyTimeLimit"); | |
| if (objctl != null) ctl = objctl.toString(); | |
| else ctl = null; | |
| } | |
| if ((ctl != null) && !(ctl.equals("-1"))) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "setting cached value and " + | |
| "lastUpdatedTime in descriptor"); | |
| } | |
| attrDescr.setField("value", response); | |
| final String stamp = String.valueOf( | |
| (new Date()).getTime()); | |
| attrDescr.setField("lastUpdatedTimeStamp", | |
| stamp); | |
| attrInfo.setDescriptor(attrDescr); | |
| modelMBeanInfo.setDescriptor(attrDescr, | |
| "attribute"); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "new descriptor is " +attrDescr); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "AttributeInfo descriptor is " + | |
| attrInfo.getDescriptor()); | |
| final String attStr = modelMBeanInfo. | |
| getDescriptor(attrName,"attribute"). | |
| toString(); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "modelMBeanInfo: AttributeInfo " + | |
| "descriptor is " + attStr); | |
| } | |
| } | |
| } else { | |
| // response was invalid or really returned null | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "got a null response from getMethod\n"); | |
| } | |
| response = null; | |
| } | |
| } else { | |
| // not getMethod so return descriptor (default) value | |
| String qualifier=""; | |
| response = attrDescr.getFieldValue("value"); | |
| if (response == null) { | |
| qualifier="default "; | |
| response = attrDescr.getFieldValue("default"); | |
| } | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "could not find getMethod for " +attrName + | |
| ", returning descriptor " +qualifier + "value"); | |
| } | |
| // !! cast response to right class | |
| } | |
| } | |
| // make sure response class matches type field | |
| final String respType = attrInfo.getType(); | |
| if (response != null) { | |
| String responseClass = response.getClass().getName(); | |
| if (!respType.equals(responseClass)) { | |
| boolean wrongType = false; | |
| boolean primitiveType = false; | |
| boolean correspondingTypes = false; | |
| for (int i = 0; i < primitiveTypes.length; i++) { | |
| if (respType.equals(primitiveTypes[i])) { | |
| primitiveType = true; | |
| if (responseClass.equals(primitiveWrappers[i])) | |
| correspondingTypes = true; | |
| break; | |
| } | |
| } | |
| if (primitiveType) { | |
| // inequality may come from primitive/wrapper class | |
| if (!correspondingTypes) | |
| wrongType = true; | |
| } else { | |
| // inequality may come from type subclassing | |
| boolean subtype; | |
| try { | |
| final Class<?> respClass = response.getClass(); | |
| final Exception[] caughException = new Exception[1]; | |
| ClassLoader cl = respClass.getClassLoader(); | |
| Class<?> c = null; | |
| try { | |
| c = Class.forName(respType, true, cl); | |
| } catch (Exception e) { | |
| caughException[0] = e; | |
| } | |
| if (caughException[0] != null) { | |
| throw caughException[0]; | |
| } | |
| subtype = c.isInstance(response); | |
| } catch (Exception e) { | |
| subtype = false; | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Exception: ", e); | |
| } | |
| } | |
| if (!subtype) | |
| wrongType = true; | |
| } | |
| if (wrongType) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Wrong response type '" + respType + "'"); | |
| } | |
| // throw exception, didn't get | |
| // back right attribute type | |
| throw new MBeanException( | |
| new InvalidAttributeValueException( | |
| "Wrong value type received for get attribute"), | |
| "An exception occurred while trying to get an " + | |
| "attribute value through a RequiredModelMBean"); | |
| } | |
| } | |
| } | |
| } else { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "getMethod failed " + attrName + | |
| " not in attributeDescriptor\n"); | |
| } | |
| throw new MBeanException(new | |
| InvalidAttributeValueException( | |
| "Unable to resolve attribute value, " + | |
| "no getMethod defined in descriptor for attribute"), | |
| "An exception occurred while trying to get an "+ | |
| "attribute value through a RequiredModelMBean"); | |
| } | |
| } catch (MBeanException | AttributeNotFoundException e) { | |
| throw e; | |
| } catch (Exception e) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "getMethod failed with " + e.getMessage() + | |
| " exception type " + (e.getClass()).toString()); | |
| } | |
| throw new MBeanException(e,"An exception occurred while trying "+ | |
| "to get an attribute value: " + e.getMessage()); | |
| } | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| return response; | |
| } | |
| /** | |
| * Returns the values of several attributes in the ModelMBean. | |
| * Executes a getAttribute for each attribute name in the | |
| * attrNames array passed in. | |
| * | |
| * @param attrNames A String array of names of the attributes | |
| * to be retrieved. | |
| * | |
| * @return The array of the retrieved attributes. | |
| * | |
| * @exception RuntimeOperationsException Wraps an | |
| * {@link IllegalArgumentException}: The object name in parameter is | |
| * null or attributes in parameter is null. | |
| * | |
| * @see #setAttributes(javax.management.AttributeList) | |
| */ | |
| public AttributeList getAttributes(String[] attrNames) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| RequiredModelMBean.class.getName(), "Entry"); | |
| } | |
| if (attrNames == null) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("attributeNames must not be null"), | |
| "Exception occurred trying to get attributes of a "+ | |
| "RequiredModelMBean"); | |
| AttributeList responseList = new AttributeList(); | |
| for (int i = 0; i < attrNames.length; i++) { | |
| try { | |
| responseList.add(new Attribute(attrNames[i], | |
| getAttribute(attrNames[i]))); | |
| } catch (Exception e) { | |
| // eat exceptions because interface doesn't have an | |
| // exception on it | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Failed to get \"" + attrNames[i] + "\": ", e); | |
| } | |
| } | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| return responseList; | |
| } | |
| /** | |
| * Sets the value of a specific attribute of a named ModelMBean. | |
| * | |
| * If the 'setMethod' field of the attribute's descriptor | |
| * contains the name of a valid operation descriptor, then the | |
| * method described by the operation descriptor is executed. | |
| * In this implementation, the operation descriptor must be specified | |
| * correctly and assigned to the modelMBeanInfo so that the 'setMethod' | |
| * works correctly. | |
| * The response from the method is set as the value of the attribute | |
| * in the descriptor. | |
| * | |
| * <p>If currencyTimeLimit is > 0, then the new value for the | |
| * attribute is cached in the attribute descriptor's | |
| * 'value' field and the 'lastUpdatedTimeStamp' field is set to | |
| * the current time stamp. | |
| * | |
| * <p>If the persist field of the attribute's descriptor is not null | |
| * then Persistence policy from the attribute descriptor is used to | |
| * guide storing the attribute in a persistent store. | |
| * <br>Store the MBean if 'persistPolicy' field is: | |
| * <UL> | |
| * <Li> != "never"</Li> | |
| * <Li> = "always"</Li> | |
| * <Li> = "onUpdate"</Li> | |
| * <Li> {@literal = "onTimer" and now > 'lastPersistTime' + 'persistPeriod'}</Li> | |
| * <Li> {@literal = "NoMoreOftenThan" and now > 'lastPersistTime' + | |
| * 'persistPeriod'}</Li> | |
| * </UL> | |
| * Do not store the MBean if 'persistPolicy' field is: | |
| * <UL> | |
| * <Li> = "never"</Li> | |
| * <Li> = {@literal = "onTimer" && now < 'lastPersistTime' + 'persistPeriod'}</Li> | |
| * <Li> = "onUnregister"</Li> | |
| * <Li> = {@literal = "NoMoreOftenThan" and now < 'lastPersistTime' + | |
| * 'persistPeriod'}</Li> | |
| * </UL> | |
| * | |
| * <p>The ModelMBeanInfo of the Model MBean is stored in a file. | |
| * | |
| * @param attribute The Attribute instance containing the name of | |
| * the attribute to be set and the value it is to be set to. | |
| * | |
| * | |
| * @exception AttributeNotFoundException The specified attribute is | |
| * not accessible in the MBean. | |
| * <br>The following cases may result in an AttributeNotFoundException: | |
| * <UL> | |
| * <LI> No ModelMBeanAttributeInfo is found for the specified | |
| * attribute.</LI> | |
| * <LI> The ModelMBeanAttributeInfo's isWritable method returns | |
| * 'false'.</LI> | |
| * </UL> | |
| * @exception InvalidAttributeValueException No descriptor is defined | |
| * for the specified attribute. | |
| * @exception MBeanException Wraps one of the following Exceptions: | |
| * <UL> | |
| * <LI> An Exception thrown by the managed object's setter.</LI> | |
| * <LI> A {@link ServiceNotFoundException} if a setMethod field is | |
| * defined in the descriptor for the attribute and the managed | |
| * resource is null; or if no setMethod field is defined and | |
| * caching is not enabled for the attribute. | |
| * Note that if there is no getMethod field either, then caching | |
| * is automatically enabled.</LI> | |
| * <LI> {@link InvalidTargetObjectTypeException} The 'targetType' | |
| * field value is not 'objectReference'.</LI> | |
| * <LI> An Exception thrown by the managed object's getter.</LI> | |
| * </UL> | |
| * @exception ReflectionException Wraps an {@link java.lang.Exception} | |
| * thrown while trying to invoke the setter. | |
| * @exception RuntimeOperationsException Wraps an | |
| * {@link IllegalArgumentException}: The attribute in parameter is | |
| * null. | |
| * | |
| * @see #getAttribute(java.lang.String) | |
| **/ | |
| public void setAttribute(Attribute attribute) | |
| throws AttributeNotFoundException, InvalidAttributeValueException, | |
| MBeanException, ReflectionException { | |
| final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.TRACE); | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (attribute == null) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("attribute must not be null"), | |
| "Exception occurred trying to set an attribute of a "+ | |
| "RequiredModelMBean"); | |
| /* run setMethod if there is one */ | |
| /* return cached value if its current */ | |
| /* set cached value in descriptor and set date/time */ | |
| /* send attribute change Notification */ | |
| /* check persistence policy and persist if need be */ | |
| String attrName = attribute.getName(); | |
| Object attrValue = attribute.getValue(); | |
| boolean updateDescriptor = false; | |
| ModelMBeanAttributeInfo attrInfo = | |
| modelMBeanInfo.getAttribute(attrName); | |
| if (attrInfo == null) | |
| throw new AttributeNotFoundException("setAttribute failed: " + | |
| attrName + " is not found "); | |
| Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor(); | |
| Descriptor attrDescr = attrInfo.getDescriptor(); | |
| if (attrDescr != null) { | |
| if (!attrInfo.isWritable()) | |
| throw new AttributeNotFoundException("setAttribute failed: " | |
| + attrName + " is not writable "); | |
| String attrSetMethod = (String) | |
| (attrDescr.getFieldValue("setMethod")); | |
| String attrGetMethod = (String) | |
| (attrDescr.getFieldValue("getMethod")); | |
| String attrType = attrInfo.getType(); | |
| Object currValue = "Unknown"; | |
| try { | |
| currValue = this.getAttribute(attrName); | |
| } catch (Throwable t) { | |
| // OK: Default "Unknown" value used for unknown attribute | |
| } | |
| Attribute oldAttr = new Attribute(attrName, currValue); | |
| /* run method from operations descriptor */ | |
| if (attrSetMethod == null) { | |
| if (attrValue != null) { | |
| try { | |
| final Class<?> clazz = loadClass(attrType); | |
| if (! clazz.isInstance(attrValue)) throw new | |
| InvalidAttributeValueException(clazz.getName() + | |
| " expected, " + | |
| attrValue.getClass().getName() + | |
| " received."); | |
| } catch (ClassNotFoundException x) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Class " + attrType + " for attribute " | |
| + attrName + " not found: ", x); | |
| } | |
| } | |
| } | |
| updateDescriptor = true; | |
| } else { | |
| invoke(attrSetMethod, | |
| (new Object[] {attrValue}), | |
| (new String[] {attrType}) ); | |
| } | |
| /* change cached value */ | |
| Object objctl = attrDescr.getFieldValue("currencyTimeLimit"); | |
| String ctl; | |
| if (objctl != null) ctl = objctl.toString(); | |
| else ctl = null; | |
| if ((ctl == null) && (mmbDesc != null)) { | |
| objctl = mmbDesc.getFieldValue("currencyTimeLimit"); | |
| if (objctl != null) ctl = objctl.toString(); | |
| else ctl = null; | |
| } | |
| final boolean updateCache = ((ctl != null) && !(ctl.equals("-1"))); | |
| if(attrSetMethod == null && !updateCache && attrGetMethod != null) | |
| throw new MBeanException(new ServiceNotFoundException("No " + | |
| "setMethod field is defined in the descriptor for " + | |
| attrName + " attribute and caching is not enabled " + | |
| "for it")); | |
| if (updateCache || updateDescriptor) { | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "setting cached value of " + | |
| attrName + " to " + attrValue); | |
| } | |
| attrDescr.setField("value", attrValue); | |
| if (updateCache) { | |
| final String currtime = String.valueOf( | |
| (new Date()).getTime()); | |
| attrDescr.setField("lastUpdatedTimeStamp", currtime); | |
| } | |
| attrInfo.setDescriptor(attrDescr); | |
| modelMBeanInfo.setDescriptor(attrDescr,"attribute"); | |
| if (tracing) { | |
| final StringBuilder strb = new StringBuilder() | |
| .append("new descriptor is ").append(attrDescr) | |
| .append(". AttributeInfo descriptor is ") | |
| .append(attrInfo.getDescriptor()) | |
| .append(". AttributeInfo descriptor is ") | |
| .append(modelMBeanInfo.getDescriptor(attrName,"attribute")); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, strb::toString); | |
| } | |
| } | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "sending sendAttributeNotification"); | |
| } | |
| sendAttributeChangeNotification(oldAttr,attribute); | |
| } else { // if descriptor ... else no descriptor | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "setMethod failed " + attrName + | |
| " not in attributeDescriptor\n"); | |
| } | |
| throw new InvalidAttributeValueException( | |
| "Unable to resolve attribute value, "+ | |
| "no defined in descriptor for attribute"); | |
| } // else no descriptor | |
| if (tracing) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /** | |
| * Sets the values of an array of attributes of this ModelMBean. | |
| * Executes the setAttribute() method for each attribute in the list. | |
| * | |
| * @param attributes A list of attributes: The identification of the | |
| * attributes to be set and the values they are to be set to. | |
| * | |
| * @return The array of attributes that were set, with their new | |
| * values in Attribute instances. | |
| * | |
| * @exception RuntimeOperationsException Wraps an | |
| * {@link IllegalArgumentException}: The object name in parameter | |
| * is null or attributes in parameter is null. | |
| * | |
| * @see #getAttributes | |
| **/ | |
| public AttributeList setAttributes(AttributeList attributes) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (attributes == null) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("attributes must not be null"), | |
| "Exception occurred trying to set attributes of a "+ | |
| "RequiredModelMBean"); | |
| final AttributeList responseList = new AttributeList(); | |
| // Go through the list of attributes | |
| for (Attribute attr : attributes.asList()) { | |
| try { | |
| setAttribute(attr); | |
| responseList.add(attr); | |
| } catch (Exception excep) { | |
| responseList.remove(attr); | |
| } | |
| } | |
| return responseList; | |
| } | |
| private ModelMBeanInfo createDefaultModelMBeanInfo() { | |
| return(new ModelMBeanInfoSupport((this.getClass().getName()), | |
| "Default ModelMBean", null, null, null, null)); | |
| } | |
| /*************************************/ | |
| /* NotificationBroadcaster Interface */ | |
| /*************************************/ | |
| private synchronized void writeToLog(String logFileName, | |
| String logEntry) throws Exception { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Notification Logging to " + logFileName + ": " + logEntry); | |
| } | |
| if ((logFileName == null) || (logEntry == null)) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Bad input parameters, will not log this entry."); | |
| } | |
| return; | |
| } | |
| FileOutputStream fos = new FileOutputStream(logFileName, true); | |
| try { | |
| PrintStream logOut = new PrintStream(fos); | |
| logOut.println(logEntry); | |
| logOut.close(); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Successfully opened log " + logFileName); | |
| } | |
| } catch (Exception e) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Exception " + e.toString() + | |
| " trying to write to the Notification log file " + | |
| logFileName); | |
| } | |
| throw e; | |
| } finally { | |
| fos.close(); | |
| } | |
| } | |
| /** | |
| * Registers an object which implements the NotificationListener | |
| * interface as a listener. This | |
| * object's 'handleNotification()' method will be invoked when any | |
| * notification is issued through or by the ModelMBean. This does | |
| * not include attributeChangeNotifications. They must be registered | |
| * for independently. | |
| * | |
| * @param listener The listener object which will handles | |
| * notifications emitted by the registered MBean. | |
| * @param filter The filter object. If null, no filtering will be | |
| * performed before handling notifications. | |
| * @param handback The context to be sent to the listener with | |
| * the notification when a notification is emitted. | |
| * | |
| * @exception IllegalArgumentException The listener cannot be null. | |
| * | |
| * @see #removeNotificationListener | |
| */ | |
| public void addNotificationListener(NotificationListener listener, | |
| NotificationFilter filter, | |
| Object handback) | |
| throws java.lang.IllegalArgumentException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (listener == null) | |
| throw new IllegalArgumentException( | |
| "notification listener must not be null"); | |
| if (generalBroadcaster == null) | |
| generalBroadcaster = new NotificationBroadcasterSupport(); | |
| generalBroadcaster.addNotificationListener(listener, filter, | |
| handback); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "NotificationListener added"); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /** | |
| * Removes a listener for Notifications from the RequiredModelMBean. | |
| * | |
| * @param listener The listener name which was handling notifications | |
| * emitted by the registered MBean. | |
| * This method will remove all information related to this listener. | |
| * | |
| * @exception ListenerNotFoundException The listener is not registered | |
| * in the MBean or is null. | |
| * | |
| * @see #addNotificationListener | |
| **/ | |
| public void removeNotificationListener(NotificationListener listener) | |
| throws ListenerNotFoundException { | |
| if (listener == null) | |
| throw new ListenerNotFoundException( | |
| "Notification listener is null"); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (generalBroadcaster == null) | |
| throw new ListenerNotFoundException( | |
| "No notification listeners registered"); | |
| generalBroadcaster.removeNotificationListener(listener); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| public void removeNotificationListener(NotificationListener listener, | |
| NotificationFilter filter, | |
| Object handback) | |
| throws ListenerNotFoundException { | |
| if (listener == null) | |
| throw new ListenerNotFoundException( | |
| "Notification listener is null"); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (generalBroadcaster == null) | |
| throw new ListenerNotFoundException( | |
| "No notification listeners registered"); | |
| generalBroadcaster.removeNotificationListener(listener,filter, | |
| handback); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| public void sendNotification(Notification ntfyObj) | |
| throws MBeanException, RuntimeOperationsException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (ntfyObj == null) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("notification object must not be "+ | |
| "null"), | |
| "Exception occurred trying to send a notification from a "+ | |
| "RequiredModelMBean"); | |
| // log notification if specified in descriptor | |
| Descriptor ntfyDesc = | |
| modelMBeanInfo.getDescriptor(ntfyObj.getType(),"notification"); | |
| Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor(); | |
| if (ntfyDesc != null) { | |
| String logging = (String) ntfyDesc.getFieldValue("log"); | |
| if (logging == null) { | |
| if (mmbDesc != null) | |
| logging = (String) mmbDesc.getFieldValue("log"); | |
| } | |
| if ((logging != null) && | |
| (logging.equalsIgnoreCase("t") || | |
| logging.equalsIgnoreCase("true"))) { | |
| String logfile = (String) ntfyDesc.getFieldValue("logfile"); | |
| if (logfile == null) { | |
| if (mmbDesc != null) | |
| logfile = (String)mmbDesc.getFieldValue("logfile"); | |
| } | |
| if (logfile != null) { | |
| try { | |
| writeToLog(logfile,"LogMsg: " + | |
| ((new Date(ntfyObj.getTimeStamp())).toString())+ | |
| " " + ntfyObj.getType() + " " + | |
| ntfyObj.getMessage() + " Severity = " + | |
| (String)ntfyDesc.getFieldValue("severity")); | |
| } catch (Exception e) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.DEBUG)) { | |
| MODELMBEAN_LOGGER.log(Level.DEBUG, | |
| "Failed to log " + | |
| ntfyObj.getType() + " notification: ", e); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (generalBroadcaster != null) { | |
| generalBroadcaster.sendNotification(ntfyObj); | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "sendNotification sent provided notification object"); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| public void sendNotification(String ntfyText) | |
| throws MBeanException, RuntimeOperationsException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (ntfyText == null) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("notification message must not "+ | |
| "be null"), | |
| "Exception occurred trying to send a text notification "+ | |
| "from a ModelMBean"); | |
| Notification myNtfyObj = new Notification("jmx.modelmbean.generic", | |
| this, 1, ntfyText); | |
| sendNotification(myNtfyObj); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Notification sent"); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /** | |
| * Returns `true' if the notification `notifName' is found | |
| * in `info'. (bug 4744667) | |
| **/ | |
| private static final | |
| boolean hasNotification(final ModelMBeanInfo info, | |
| final String notifName) { | |
| try { | |
| if (info == null) return false; | |
| else return (info.getNotification(notifName)!=null); | |
| } catch (MBeanException | RuntimeOperationsException x) { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Creates a default ModelMBeanNotificationInfo for GENERIC | |
| * notification. (bug 4744667) | |
| **/ | |
| private static final ModelMBeanNotificationInfo makeGenericInfo() { | |
| final Descriptor genericDescriptor = new DescriptorSupport( new | |
| String[] { | |
| "name=GENERIC", | |
| "descriptorType=notification", | |
| "log=T", | |
| "severity=6", | |
| "displayName=jmx.modelmbean.generic"} ); | |
| return new ModelMBeanNotificationInfo(new | |
| String[] {"jmx.modelmbean.generic"}, | |
| "GENERIC", | |
| "A text notification has been issued by the managed resource", | |
| genericDescriptor); | |
| } | |
| /** | |
| * Creates a default ModelMBeanNotificationInfo for ATTRIBUTE_CHANGE | |
| * notification. (bug 4744667) | |
| **/ | |
| private static final | |
| ModelMBeanNotificationInfo makeAttributeChangeInfo() { | |
| final Descriptor attributeDescriptor = new DescriptorSupport(new | |
| String[] { | |
| "name=ATTRIBUTE_CHANGE", | |
| "descriptorType=notification", | |
| "log=T", | |
| "severity=6", | |
| "displayName=jmx.attribute.change"}); | |
| return new ModelMBeanNotificationInfo(new | |
| String[] {"jmx.attribute.change"}, | |
| "ATTRIBUTE_CHANGE", | |
| "Signifies that an observed MBean attribute value has changed", | |
| attributeDescriptor ); | |
| } | |
| /** | |
| * Returns the array of Notifications always generated by the | |
| * RequiredModelMBean. | |
| * <P> | |
| * | |
| * RequiredModelMBean may always send also two additional notifications: | |
| * <UL> | |
| * <LI> One with descriptor <code>"name=GENERIC,descriptorType=notification,log=T,severity=6,displayName=jmx.modelmbean.generic"</code></LI> | |
| * <LI> Second is a standard attribute change notification | |
| * with descriptor <code>"name=ATTRIBUTE_CHANGE,descriptorType=notification,log=T,severity=6,displayName=jmx.attribute.change"</code></LI> | |
| * </UL> | |
| * Thus these two notifications are always added to those specified | |
| * by the application. | |
| * | |
| * @return MBeanNotificationInfo[] | |
| * | |
| **/ | |
| public MBeanNotificationInfo[] getNotificationInfo() { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| // Using hasNotification() is not optimal, but shouldn't really | |
| // matter in this context... | |
| // hasGeneric==true if GENERIC notification is present. | |
| // (bug 4744667) | |
| final boolean hasGeneric = hasNotification(modelMBeanInfo,"GENERIC"); | |
| // hasAttributeChange==true if ATTRIBUTE_CHANGE notification is | |
| // present. | |
| // (bug 4744667) | |
| final boolean hasAttributeChange = | |
| hasNotification(modelMBeanInfo,"ATTRIBUTE_CHANGE"); | |
| // User supplied list of notification infos. | |
| // | |
| final ModelMBeanNotificationInfo[] currInfo = | |
| (ModelMBeanNotificationInfo[])modelMBeanInfo.getNotifications(); | |
| // Length of the returned list of notification infos: | |
| // length of user supplied list + possibly 1 for GENERIC, + | |
| // possibly 1 for ATTRIBUTE_CHANGE | |
| // (bug 4744667) | |
| final int len = ((currInfo==null?0:currInfo.length) + | |
| (hasGeneric?0:1) + (hasAttributeChange?0:1)); | |
| // Returned list of notification infos: | |
| // | |
| final ModelMBeanNotificationInfo[] respInfo = | |
| new ModelMBeanNotificationInfo[len]; | |
| // Preserve previous ordering (JMX 1.1) | |
| // | |
| // Counter of "standard" notification inserted before user | |
| // supplied notifications. | |
| // | |
| int inserted=0; | |
| if (!hasGeneric) | |
| // We need to add description for GENERIC notification | |
| // (bug 4744667) | |
| respInfo[inserted++] = makeGenericInfo(); | |
| if (!hasAttributeChange) | |
| // We need to add description for ATTRIBUTE_CHANGE notification | |
| // (bug 4744667) | |
| respInfo[inserted++] = makeAttributeChangeInfo(); | |
| // Now copy user supplied list in returned list. | |
| // | |
| final int count = currInfo.length; | |
| final int offset = inserted; | |
| for (int j=0; j < count; j++) { | |
| respInfo[offset+j] = currInfo[j]; | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| return respInfo; | |
| } | |
| public void addAttributeChangeNotificationListener(NotificationListener | |
| inlistener, | |
| String | |
| inAttributeName, | |
| Object inhandback) | |
| throws MBeanException, RuntimeOperationsException, | |
| IllegalArgumentException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (inlistener == null) | |
| throw new IllegalArgumentException( | |
| "Listener to be registered must not be null"); | |
| if (attributeBroadcaster == null) | |
| attributeBroadcaster = new NotificationBroadcasterSupport(); | |
| AttributeChangeNotificationFilter currFilter = | |
| new AttributeChangeNotificationFilter(); | |
| MBeanAttributeInfo[] attrInfo = modelMBeanInfo.getAttributes(); | |
| boolean found = false; | |
| if (inAttributeName == null) { | |
| if ((attrInfo != null) && (attrInfo.length>0)) { | |
| for (int i=0; i<attrInfo.length; i++) { | |
| currFilter.enableAttribute(attrInfo[i].getName()); | |
| } | |
| } | |
| } else { | |
| if ((attrInfo != null) && (attrInfo.length>0)) { | |
| for (int i=0; i<attrInfo.length; i++) { | |
| if (inAttributeName.equals(attrInfo[i].getName())) { | |
| found = true; | |
| currFilter.enableAttribute(inAttributeName); | |
| break; | |
| } | |
| } | |
| } | |
| if (!found) { | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException( | |
| "The attribute name does not exist"), | |
| "Exception occurred trying to add an "+ | |
| "AttributeChangeNotification listener"); | |
| } | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| Vector<String> enabledAttrs = currFilter.getEnabledAttributes(); | |
| String s = (enabledAttrs.size() > 1) ? | |
| "[" + enabledAttrs.firstElement() + ", ...]" : | |
| enabledAttrs.toString(); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Set attribute change filter to " + s); | |
| } | |
| attributeBroadcaster.addNotificationListener(inlistener,currFilter, | |
| inhandback); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Notification listener added for " + inAttributeName); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| public void removeAttributeChangeNotificationListener( | |
| NotificationListener inlistener, String inAttributeName) | |
| throws MBeanException, RuntimeOperationsException, | |
| ListenerNotFoundException { | |
| if (inlistener == null) throw new | |
| ListenerNotFoundException("Notification listener is null"); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| RequiredModelMBean.class.getName(), "Entry"); | |
| } | |
| if (attributeBroadcaster == null) | |
| throw new ListenerNotFoundException( | |
| "No attribute change notification listeners registered"); | |
| MBeanAttributeInfo[] attrInfo = modelMBeanInfo.getAttributes(); | |
| boolean found = false; | |
| if ((attrInfo != null) && (attrInfo.length>0)) { | |
| for (int i=0; i<attrInfo.length; i++) { | |
| if (attrInfo[i].getName().equals(inAttributeName)) { | |
| found = true; | |
| break; | |
| } | |
| } | |
| } | |
| if ((!found) && (inAttributeName != null)) { | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("Invalid attribute name"), | |
| "Exception occurred trying to remove "+ | |
| "attribute change notification listener"); | |
| } | |
| /* note: */ | |
| /* this may be a problem if the same listener is registered for | |
| multiple attributes with multiple filters and/or handback | |
| objects. It may remove all of them */ | |
| attributeBroadcaster.removeNotificationListener(inlistener); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| public void sendAttributeChangeNotification(AttributeChangeNotification | |
| ntfyObj) | |
| throws MBeanException, RuntimeOperationsException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| if (ntfyObj == null) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException( | |
| "attribute change notification object must not be null"), | |
| "Exception occurred trying to send "+ | |
| "attribute change notification of a ModelMBean"); | |
| Object oldv = ntfyObj.getOldValue(); | |
| Object newv = ntfyObj.getNewValue(); | |
| if (oldv == null) oldv = "null"; | |
| if (newv == null) newv = "null"; | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, | |
| "Sending AttributeChangeNotification with " + | |
| ntfyObj.getAttributeName() + ntfyObj.getAttributeType() + | |
| ntfyObj.getNewValue() + ntfyObj.getOldValue()); | |
| } | |
| // log notification if specified in descriptor | |
| Descriptor ntfyDesc = | |
| modelMBeanInfo.getDescriptor(ntfyObj.getType(),"notification"); | |
| Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor(); | |
| String logging, logfile; | |
| if (ntfyDesc != null) { | |
| logging =(String) ntfyDesc.getFieldValue("log"); | |
| if (logging == null) { | |
| if (mmbDesc != null) | |
| logging = (String) mmbDesc.getFieldValue("log"); | |
| } | |
| if ((logging != null) && | |
| ( logging.equalsIgnoreCase("t") || | |
| logging.equalsIgnoreCase("true"))) { | |
| logfile = (String) ntfyDesc.getFieldValue("logfile"); | |
| if (logfile == null) { | |
| if (mmbDesc != null) | |
| logfile = (String)mmbDesc.getFieldValue("logfile"); | |
| } | |
| if (logfile != null) { | |
| try { | |
| writeToLog(logfile,"LogMsg: " + | |
| ((new Date(ntfyObj.getTimeStamp())).toString())+ | |
| " " + ntfyObj.getType() + " " + | |
| ntfyObj.getMessage() + | |
| " Name = " + ntfyObj.getAttributeName() + | |
| " Old value = " + oldv + | |
| " New value = " + newv); | |
| } catch (Exception e) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.DEBUG)) { | |
| MODELMBEAN_LOGGER.log(Level.DEBUG, | |
| "Failed to log " + ntfyObj.getType() + | |
| " notification: ", e); | |
| } | |
| } | |
| } | |
| } | |
| } else if (mmbDesc != null) { | |
| logging = (String) mmbDesc.getFieldValue("log"); | |
| if ((logging != null) && | |
| ( logging.equalsIgnoreCase("t") || | |
| logging.equalsIgnoreCase("true") )) { | |
| logfile = (String) mmbDesc.getFieldValue("logfile"); | |
| if (logfile != null) { | |
| try { | |
| writeToLog(logfile,"LogMsg: " + | |
| ((new Date(ntfyObj.getTimeStamp())).toString())+ | |
| " " + ntfyObj.getType() + " " + | |
| ntfyObj.getMessage() + | |
| " Name = " + ntfyObj.getAttributeName() + | |
| " Old value = " + oldv + | |
| " New value = " + newv); | |
| } catch (Exception e) { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.DEBUG)) { | |
| MODELMBEAN_LOGGER.log(Level.DEBUG, | |
| "Failed to log " + ntfyObj.getType() + | |
| " notification: ", e); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (attributeBroadcaster != null) { | |
| attributeBroadcaster.sendNotification(ntfyObj); | |
| } | |
| // XXX Revisit: This is a quickfix: it would be better to have a | |
| // single broadcaster. However, it is not so simple because | |
| // removeAttributeChangeNotificationListener() should | |
| // remove only listeners whose filter is an instanceof | |
| // AttributeChangeNotificationFilter. | |
| // | |
| if (generalBroadcaster != null) { | |
| generalBroadcaster.sendNotification(ntfyObj); | |
| } | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "sent notification"); | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| public void sendAttributeChangeNotification(Attribute inOldVal, | |
| Attribute inNewVal) | |
| throws MBeanException, RuntimeOperationsException { | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Entry"); | |
| } | |
| // do we really want to do this? | |
| if ((inOldVal == null) || (inNewVal == null)) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("Attribute object must not be null"), | |
| "Exception occurred trying to send " + | |
| "attribute change notification of a ModelMBean"); | |
| if (!(inOldVal.getName().equals(inNewVal.getName()))) | |
| throw new RuntimeOperationsException(new | |
| IllegalArgumentException("Attribute names are not the same"), | |
| "Exception occurred trying to send " + | |
| "attribute change notification of a ModelMBean"); | |
| Object newVal = inNewVal.getValue(); | |
| Object oldVal = inOldVal.getValue(); | |
| String className = "unknown"; | |
| if (newVal != null) | |
| className = newVal.getClass().getName(); | |
| if (oldVal != null) | |
| className = oldVal.getClass().getName(); | |
| AttributeChangeNotification myNtfyObj = new | |
| AttributeChangeNotification(this, | |
| 1, | |
| ((new Date()).getTime()), | |
| "AttributeChangeDetected", | |
| inOldVal.getName(), | |
| className, | |
| inOldVal.getValue(), | |
| inNewVal.getValue()); | |
| sendAttributeChangeNotification(myNtfyObj); | |
| if (MODELMBEAN_LOGGER.isLoggable(Level.TRACE)) { | |
| MODELMBEAN_LOGGER.log(Level.TRACE, "Exit"); | |
| } | |
| } | |
| /** | |
| * Return the Class Loader Repository used to perform class loading. | |
| * Subclasses may wish to redefine this method in order to return | |
| * the appropriate {@link javax.management.loading.ClassLoaderRepository} | |
| * that should be used in this object. | |
| * | |
| * @return the Class Loader Repository. | |
| * | |
| */ | |
| protected ClassLoaderRepository getClassLoaderRepository() { | |
| return MBeanServerFactory.getClassLoaderRepository(server); | |
| } | |
| private Class<?> loadClass(final String className) | |
| throws ClassNotFoundException { | |
| final ClassNotFoundException[] caughtException = new ClassNotFoundException[1]; | |
| Class<?> c = null; | |
| try { | |
| c = Class.forName(className); | |
| } catch (ClassNotFoundException e) { | |
| final ClassLoaderRepository clr = getClassLoaderRepository(); | |
| try { | |
| if (clr == null) throw new ClassNotFoundException(className); | |
| return clr.loadClass(className); | |
| } catch (ClassNotFoundException ex) { | |
| caughtException[0] = ex; | |
| } | |
| } | |
| if (caughtException[0] != null) { | |
| throw caughtException[0]; | |
| } | |
| return c; | |
| } | |
| /*************************************/ | |
| /* MBeanRegistration Interface */ | |
| /*************************************/ | |
| /** | |
| * Allows the MBean to perform any operations it needs before | |
| * being registered in the MBean server. If the name of the MBean | |
| * is not specified, the MBean can provide a name for its | |
| * registration. If any exception is raised, the MBean will not be | |
| * registered in the MBean server. | |
| * <P> | |
| * In order to ensure proper run-time semantics of RequireModelMBean, | |
| * Any subclass of RequiredModelMBean overloading or overriding this | |
| * method should call <code>super.preRegister(server, name)</code> | |
| * in its own <code>preRegister</code> implementation. | |
| * | |
| * @param server The MBean server in which the MBean will be registered. | |
| * | |
| * @param name The object name of the MBean. This name is null if | |
| * the name parameter to one of the <code>createMBean</code> or | |
| * <code>registerMBean</code> methods in the {@link MBeanServer} | |
| * interface is null. In that case, this method must return a | |
| * non-null ObjectName for the new MBean. | |
| * | |
| * @return The name under which the MBean is to be registered. | |
| * This value must not be null. If the <code>name</code> | |
| * parameter is not null, it will usually but not necessarily be | |
| * the returned value. | |
| * | |
| * @exception java.lang.Exception This exception will be caught by | |
| * the MBean server and re-thrown as an | |
| * {@link javax.management.MBeanRegistrationException | |
| * MBeanRegistrationException}. | |
| */ | |
| public ObjectName preRegister(MBeanServer server, | |
| ObjectName name) | |
| throws java.lang.Exception { | |
| // Since ModelMbeanInfo cannot be null (otherwise exception | |
| // thrown at creation) | |
| // no exception thrown on ModelMBeanInfo not set. | |
| if (name == null) throw new NullPointerException( | |
| "name of RequiredModelMBean to registered is null"); | |
| this.server = server; | |
| return name; | |
| } | |
| /** | |
| * Allows the MBean to perform any operations needed after having been | |
| * registered in the MBean server or after the registration has failed. | |
| * <P> | |
| * In order to ensure proper run-time semantics of RequireModelMBean, | |
| * Any subclass of RequiredModelMBean overloading or overriding this | |
| * method should call <code>super.postRegister(registrationDone)</code> | |
| * in its own <code>postRegister</code> implementation. | |
| * | |
| * @param registrationDone Indicates whether or not the MBean has | |
| * been successfully registered in the MBean server. The value | |
| * false means that the registration phase has failed. | |
| */ | |
| public void postRegister(Boolean registrationDone) { | |
| registered = registrationDone.booleanValue(); | |
| } | |
| /** | |
| * Allows the MBean to perform any operations it needs before | |
| * being unregistered by the MBean server. | |
| * <P> | |
| * In order to ensure proper run-time semantics of RequireModelMBean, | |
| * Any subclass of RequiredModelMBean overloading or overriding this | |
| * method should call <code>super.preDeregister()</code> in its own | |
| * <code>preDeregister</code> implementation. | |
| * | |
| * @exception java.lang.Exception This exception will be caught by | |
| * the MBean server and re-thrown as an | |
| * {@link javax.management.MBeanRegistrationException | |
| * MBeanRegistrationException}. | |
| */ | |
| public void preDeregister() throws java.lang.Exception { | |
| } | |
| /** | |
| * Allows the MBean to perform any operations needed after having been | |
| * unregistered in the MBean server. | |
| * <P> | |
| * In order to ensure proper run-time semantics of RequireModelMBean, | |
| * Any subclass of RequiredModelMBean overloading or overriding this | |
| * method should call <code>super.postDeregister()</code> in its own | |
| * <code>postDeregister</code> implementation. | |
| */ | |
| public void postDeregister() { | |
| registered = false; | |
| this.server=null; | |
| } | |
| private static final String[] primitiveTypes; | |
| private static final String[] primitiveWrappers; | |
| static { | |
| primitiveTypes = new String[] { | |
| Boolean.TYPE.getName(), | |
| Byte.TYPE.getName(), | |
| Character.TYPE.getName(), | |
| Short.TYPE.getName(), | |
| Integer.TYPE.getName(), | |
| Long.TYPE.getName(), | |
| Float.TYPE.getName(), | |
| Double.TYPE.getName(), | |
| Void.TYPE.getName() | |
| }; | |
| primitiveWrappers = new String[] { | |
| Boolean.class.getName(), | |
| Byte.class.getName(), | |
| Character.class.getName(), | |
| Short.class.getName(), | |
| Integer.class.getName(), | |
| Long.class.getName(), | |
| Float.class.getName(), | |
| Double.class.getName(), | |
| Void.class.getName() | |
| }; | |
| } | |
| } |