/*
* Copyright (c) 2000, 2012, 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;
/* java imports */
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.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.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.misc.JavaSecurityAccess;
import sun.misc.SharedSecrets;
import sun.reflect.misc.MethodUtil;
import sun.reflect.misc.ReflectUtil;
/**
* 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.
*
* 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 in an XML formatted file or dynamically and
* programmatically at runtime.
*
* Every RequiredModelMBean which is instantiated in the MBeanServer
* becomes manageable:
* its attributes and operations become remotely accessible through the
* connectors/adaptors connected to that MBeanServer.
*
* 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;
private final static JavaSecurityAccess javaSecurityAccess = SharedSecrets.getJavaSecurityAccess();
final private AccessControlContext acc = AccessController.getContext();
/*************************************/
/* constructors */
/*************************************/
/**
* Constructs an RequiredModelMBean with an empty
* ModelMBeanInfo.
*
* 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.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"RequiredModelMBean()", "Entry");
}
modelMBeanInfo = createDefaultModelMBeanInfo();
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"RequiredModelMBean()", "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.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"RequiredModelMBean(MBeanInfo)", "Entry");
}
setModelMBeanInfo(mbi);
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"RequiredModelMBean(MBeanInfo)", "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.
*
* Once the ModelMBean's ModelMBeanInfo (with Descriptors) are
* customized and set on the ModelMBean, the ModelMBean be
* registered with the MBeanServer.
*
* If the ModelMBean is currently registered, this method throws
* a {@link javax.management.RuntimeOperationsException} wrapping an
* {@link IllegalStateException}
*
* If the given inModelMBeanInfo does not contain any
* {@link ModelMBeanNotificationInfo} for the GENERIC
* or ATTRIBUTE_CHANGE 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
*
Wraps an {@link IllegalArgumentException} if
* the MBeanInfo passed in parameter is null.
*
Wraps an {@link IllegalStateException} if the ModelMBean
* is currently registered in the MBeanServer.
*
*
**/
public void setModelMBeanInfo(ModelMBeanInfo mbi)
throws MBeanException, RuntimeOperationsException {
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setModelMBeanInfo(ModelMBeanInfo)","Entry");
}
if (mbi == null) {
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setModelMBeanInfo(ModelMBeanInfo)",
"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.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setModelMBeanInfo(ModelMBeanInfo)",
"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.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setModelMBeanInfo(ModelMBeanInfo)",
"Setting ModelMBeanInfo to " + printModelMBeanInfo(mbi));
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setModelMBeanInfo(ModelMBeanInfo)",
"ModelMBeanInfo notifications has " +
(mbi.getNotifications()).length + " elements");
}
modelMBeanInfo = (ModelMBeanInfo)mbi.clone();
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setModelMBeanInfo(ModelMBeanInfo)","set mbeanInfo to: "+
printModelMBeanInfo(modelMBeanInfo));
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setModelMBeanInfo(ModelMBeanInfo)","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.
* Can be: "ObjectReference", "Handle", "IOR", "EJBHandle",
* or "RMIReference".
* 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.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setManagedResource(Object,String)","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.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setManagedResource(Object,String)",
"Managed Resouce Type is not supported: " + mr_type);
}
throw new InvalidTargetObjectTypeException(mr_type);
}
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setManagedResource(Object,String)",
"Managed Resouce is valid");
}
managedResource = mr;
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"setManagedResource(Object, String)", "Exit");
}
}
/**
*
Instantiates this MBean instance with the data found for
* the MBean in the persistent store. The data loaded could include
* attribute and operation values.
*
*
This method should be called during construction or
* initialization of this instance, and before the MBean is
* registered with the MBeanServer.
*
*
If the implementation of this class does not support
* persistence, an {@link MBeanException} wrapping a {@link
* ServiceNotFoundException} is thrown.
*
* @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());
}
/**
*
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.
*
*
If the implementation of this class does not support
* persistence, an {@link MBeanException} wrapping a {@link
* ServiceNotFoundException} is thrown.
*
*
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:
*
*
!= "never"
* = "always"
* = "onTimer" and now > 'lastPersistTime' + 'persistPeriod'
* = "NoMoreOftenThan" and now > 'lastPersistTime' + 'persistPeriod'
* = "onUnregister"
*
*
*
Do not store the MBean if 'persistPolicy' field is:
*
* @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:
*
*
<0 Then the value is not cached and is never valid.
* Null is returned. The 'value' and 'lastUpdatedTimeStamp'
* fields are cleared.
*
=0 Then the value is always cached and always valid.
* The 'value' field is returned.
* The 'lastUpdatedTimeStamp' field is not checked.
*
>0 Represents the number of seconds that the
* 'value' field is valid.
* The 'value' field is no longer valid when
* 'lastUpdatedTimeStamp' + 'currencyTimeLimit' > Now.
*
*
When 'value' is valid, 'valid' is returned.
*
When 'value' is no longer valid then null is returned and
* 'value' and 'lastUpdatedTimeStamp' fields are cleared.
*
**/
private Object resolveForCacheValue(Descriptor descr)
throws MBeanException, RuntimeOperationsException {
final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.FINER);
final String mth = "resolveForCacheValue(Descriptor)";
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,"Entry");
}
Object response = null;
boolean resetValue = false, returnCachedValue = true;
long currencyPeriod = 0;
if (descr == null) {
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
"Input Descriptor is null");
}
return response;
}
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth, "descriptor is " + descr);
}
final Descriptor mmbDescr = modelMBeanInfo.getMBeanDescriptor();
if (mmbDescr == null) {
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth,"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.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth,"currencyTimeLimit: " + expTime);
}
// convert seconds to milliseconds for time comparison
currencyPeriod = ((new Long(expTime)).longValue()) * 1000;
if (currencyPeriod < 0) {
/* if currencyTimeLimit is -1 then value is never cached */
returnCachedValue = false;
resetValue = true;
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
currencyPeriod + ": never Cached");
}
} else if (currencyPeriod == 0) {
/* if currencyTimeLimit is 0 then value is always cached */
returnCachedValue = true;
resetValue = false;
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
"always valid Cache");
}
} else {
Object objtStamp =
descr.getFieldValue("lastUpdatedTimeStamp");
String tStamp;
if (objtStamp != null) tStamp = objtStamp.toString();
else tStamp = null;
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
"lastUpdatedTimeStamp: " + tStamp);
}
if (tStamp == null)
tStamp = "0";
long lastTime = (new Long(tStamp)).longValue();
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
"currencyPeriod:" + currencyPeriod +
" lastUpdatedTimeStamp:" + lastTime);
}
long now = (new Date()).getTime();
if (now < (lastTime + currencyPeriod)) {
returnCachedValue = true;
resetValue = false;
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
" timed valid Cache for " + now + " < " +
(lastTime + currencyPeriod));
}
} else { /* value is expired */
returnCachedValue = false;
resetValue = true;
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
"timed expired cache for " + now + " > " +
(lastTime + currencyPeriod));
}
}
}
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
"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.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,
"valid Cache value: " + currValue);
}
} else {
response = null;
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth,"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.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth,"reset cached value to null");
}
}
}
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),mth,"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.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"getMBeanInfo()","Entry");
}
if (modelMBeanInfo == null) {
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"getMBeanInfo()","modelMBeanInfo is null");
}
modelMBeanInfo = createDefaultModelMBeanInfo();
//return new ModelMBeanInfo(" ", "", null, null, null, null);
}
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"getMBeanInfo()","ModelMBeanInfo is " +
modelMBeanInfo.getClassName() + " for " +
modelMBeanInfo.getDescription());
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"getMBeanInfo()",printModelMBeanInfo(modelMBeanInfo));
}
return((MBeanInfo) modelMBeanInfo.clone());
}
private String printModelMBeanInfo(ModelMBeanInfo info) {
final StringBuilder retStr = new StringBuilder();
if (info == null) {
if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"printModelMBeanInfo(ModelMBeanInfo)",
"ModelMBeanInfo to print is null, " +
"printing local ModelMBeanInfo");
}
info = modelMBeanInfo;
}
retStr.append("\nMBeanInfo for ModelMBean is:");
retStr.append("\nCLASSNAME: \t"+ info.getClassName());
retStr.append("\nDESCRIPTION: \t"+ info.getDescription());
try {
retStr.append("\nMBEAN DESCRIPTOR: \t"+
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 0 )) {
for (int i=0; i0)) {
for (int i=0; i0)) {
for (int i=0; i
* 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.
*
* 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:
*
*
<0 Then the value is not cached and is never valid.
* The operation method is invoked.
* The 'value' and 'lastUpdatedTimeStamp' fields are cleared.
*
=0 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.
*
>0 Represents the number of seconds that the 'value'
* field is valid.
* The 'value' field is no longer valid when
* 'lastUpdatedTimeStamp' + 'currencyTimeLimit' > Now.
*
*
When 'value' is valid, 'value' is returned.
*
When 'value' is no longer valid then the operation
* method is invoked. The 'lastUpdatedTimeStamp' field
* and `value' fields are updated.
*
*
*
*
*
Note: because of inconsistencies in previous versions of
* this specification, it is recommended not to use negative or zero
* values for currencyTimeLimit. To indicate that a
* cached value is never valid, omit the
* currencyTimeLimit field. To indicate that it is
* always valid, use a very large number for this field.
*
* @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:
*
*
An Exception thrown by the managed object's invoked method.
*
{@link ServiceNotFoundException}: No ModelMBeanOperationInfo or
* no descriptor defined for the specified operation or the managed
* resource is null.
*
{@link InvalidTargetObjectTypeException}: The 'targetType'
* field value is not 'objectReference'.
*
* @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.FINER);
final String mth = "invoke(String, Object[], String[])";
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(), mth, "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.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth, "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.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth,
"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.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth, "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.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth, "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);
}
}
final Class> targetClass;
if (opClassName != null) {
try {
AccessControlContext stack = AccessController.getContext();
final Object obj = targetObject;
final String className = opClassName;
final ClassNotFoundException[] caughtException = new ClassNotFoundException[1];
targetClass = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction>() {
@Override
public Class> run() {
try {
ReflectUtil.checkPackageAccess(className);
final ClassLoader targetClassLoader =
obj.getClass().getClassLoader();
return Class.forName(className, false,
targetClassLoader);
} catch (ClassNotFoundException e) {
caughtException[0] = e;
}
return null;
}
}, stack, acc);
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.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth, "found " + opMethodName + ", now invoking");
}
final Object result =
invokeMethod(opName, method, targetObject, opArgs);
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
mth, "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.FINER);
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),"resolveMethod",
"resolving " + targetClass.getName() + "." + opMethodName);
}
final Class>[] argClasses;
if (sig == null)
argClasses = null;
else {
final AccessControlContext stack = AccessController.getContext();
final ReflectionException[] caughtException = new ReflectionException[1];
final ClassLoader targetClassLoader = targetClass.getClassLoader();
argClasses = new Class>[sig.length];
javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction() {
@Override
public Void run() {
for (int i = 0; i < sig.length; i++) {
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),"resolveMethod",
"resolve type " + sig[i]);
}
argClasses[i] = (Class>) primitiveClassMap.get(sig[i]);
if (argClasses[i] == null) {
try {
ReflectUtil.checkPackageAccess(sig[i]);
argClasses[i] =
Class.forName(sig[i], false, targetClassLoader);
} catch (ClassNotFoundException e) {
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"resolveMethod",
"class not found");
}
final String msg = "Parameter class not found";
caughtException[0] = new ReflectionException(e, msg);
}
}
}
return null;
}
}, stack, acc);
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> 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.FINER);
if (tracing) {
MODELMBEAN_LOGGER.logp(Level.FINER,
RequiredModelMBean.class.getName(),
"invoke(String, Object[], String[])",
"looking for method in RequiredModelMBean class");
}
if (!isRMMBMethodName(opMethodName))
return null;
if (targetObjectField != null)
return null;
final Class rmmbClass = RequiredModelMBean.class;
final Class> targetClass;
if (opClassName == null)
targetClass = rmmbClass;
else {
AccessControlContext stack = AccessController.getContext();
final String className = opClassName;
targetClass = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction>() {
@Override
public Class> run() {
try {
ReflectUtil.checkPackageAccess(className);
final ClassLoader targetClassLoader =
rmmbClass.getClassLoader();
Class clz = Class.forName(className, false,
targetClassLoader);
if (!rmmbClass.isAssignableFrom(clz))
return null;
return clz;
} catch (ClassNotFoundException e) {
return null;
}
}
}, stack, acc);
}
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];
AccessControlContext stack = AccessController.getContext();
Object rslt = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction