1 /*
   2  * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 /*
  26  * @author    IBM Corp.
  27  *
  28  * Copyright IBM Corp. 1999-2000.  All rights reserved.
  29  */
  30 
  31 
  32 package javax.management.modelmbean;
  33 
  34 /* java imports */
  35 
  36 import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER;
  37 import java.io.FileOutputStream;
  38 import java.io.PrintStream;
  39 import java.lang.reflect.InvocationTargetException;
  40 
  41 import java.lang.reflect.Method;
  42 import java.security.AccessControlContext;
  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 
  46 import java.util.Date;
  47 import java.util.HashMap;
  48 import java.util.HashSet;
  49 import java.util.logging.Level;
  50 import java.util.Map;
  51 import java.util.Set;
  52 
  53 import java.util.Vector;
  54 import javax.management.Attribute;
  55 import javax.management.AttributeChangeNotification;
  56 import javax.management.AttributeChangeNotificationFilter;
  57 import javax.management.AttributeList;
  58 import javax.management.AttributeNotFoundException;
  59 import javax.management.Descriptor;
  60 import javax.management.InstanceNotFoundException;
  61 import javax.management.InvalidAttributeValueException;
  62 import javax.management.ListenerNotFoundException;
  63 import javax.management.MBeanAttributeInfo;
  64 import javax.management.MBeanConstructorInfo;
  65 import javax.management.MBeanException;
  66 import javax.management.MBeanInfo;
  67 import javax.management.MBeanNotificationInfo;
  68 import javax.management.MBeanOperationInfo;
  69 import javax.management.MBeanRegistration;
  70 import javax.management.MBeanServer;
  71 import javax.management.MBeanServerFactory;
  72 import javax.management.Notification;
  73 import javax.management.NotificationBroadcasterSupport;
  74 import javax.management.NotificationEmitter;
  75 import javax.management.NotificationFilter;
  76 import javax.management.NotificationListener;
  77 import javax.management.ObjectName;
  78 import javax.management.ReflectionException;
  79 import javax.management.RuntimeErrorException;
  80 import javax.management.RuntimeOperationsException;
  81 import javax.management.ServiceNotFoundException;
  82 import javax.management.loading.ClassLoaderRepository;
  83 import sun.misc.JavaSecurityAccess;
  84 import sun.misc.SharedSecrets;
  85 
  86 import sun.reflect.misc.MethodUtil;
  87 import sun.reflect.misc.ReflectUtil;
  88 
  89 /**
  90  * This class is the implementation of a ModelMBean. An appropriate
  91  * implementation of a ModelMBean must be shipped with every JMX Agent
  92  * and the class must be named RequiredModelMBean.
  93  * <P>
  94  * Java resources wishing to be manageable instantiate the
  95  * RequiredModelMBean using the MBeanServer's createMBean method.
  96  * The resource then sets the MBeanInfo and Descriptors for the
  97  * RequiredModelMBean instance. The attributes and operations exposed
  98  * via the ModelMBeanInfo for the ModelMBean are accessible
  99  * from MBeans, connectors/adaptors like other MBeans. Through the
 100  * Descriptors, values and methods in the managed application can be
 101  * defined and mapped to attributes and operations of the ModelMBean.
 102  * This mapping can be defined in an XML formatted file or dynamically and
 103  * programmatically at runtime.
 104  * <P>
 105  * Every RequiredModelMBean which is instantiated in the MBeanServer
 106  * becomes manageable:<br>
 107  * its attributes and operations become remotely accessible through the
 108  * connectors/adaptors connected to that MBeanServer.
 109  * <P>
 110  * A Java object cannot be registered in the MBeanServer unless it is a
 111  * JMX compliant MBean. By instantiating a RequiredModelMBean, resources
 112  * are guaranteed that the MBean is valid.
 113  *
 114  * MBeanException and RuntimeOperationsException must be thrown on every
 115  * public method.  This allows for wrapping exceptions from distributed
 116  * communications (RMI, EJB, etc.)
 117  *
 118  * @since 1.5
 119  */
 120 
 121 public class RequiredModelMBean
 122     implements ModelMBean, MBeanRegistration, NotificationEmitter {
 123 
 124     /*************************************/
 125     /* attributes                        */
 126     /*************************************/
 127     ModelMBeanInfo modelMBeanInfo;
 128 
 129     /* Notification broadcaster for any notification to be sent
 130      * from the application through the RequiredModelMBean.  */
 131     private NotificationBroadcasterSupport generalBroadcaster = null;
 132 
 133     /* Notification broadcaster for attribute change notifications */
 134     private NotificationBroadcasterSupport attributeBroadcaster = null;
 135 
 136     /* handle, name, or reference for instance on which the actual invoke
 137      * and operations will be executed */
 138     private Object managedResource = null;
 139 
 140 
 141     /* records the registering in MBeanServer */
 142     private boolean registered = false;
 143     private transient MBeanServer server = null;
 144 
 145     private final static JavaSecurityAccess javaSecurityAccess = SharedSecrets.getJavaSecurityAccess();
 146     final private AccessControlContext acc = AccessController.getContext();
 147 
 148     /*************************************/
 149     /* constructors                      */
 150     /*************************************/
 151 
 152     /**
 153      * Constructs an <CODE>RequiredModelMBean</CODE> with an empty
 154      * ModelMBeanInfo.
 155      * <P>
 156      * The RequiredModelMBean's MBeanInfo and Descriptors
 157      * can be customized using the {@link #setModelMBeanInfo} method.
 158      * After the RequiredModelMBean's MBeanInfo and Descriptors are
 159      * customized, the RequiredModelMBean can be registered with
 160      * the MBeanServer.
 161      *
 162      * @exception MBeanException Wraps a distributed communication Exception.
 163      *
 164      * @exception RuntimeOperationsException Wraps a {@link
 165      * RuntimeException} during the construction of the object.
 166      **/
 167     public RequiredModelMBean()
 168         throws MBeanException, RuntimeOperationsException {
 169         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 170             MODELMBEAN_LOGGER.logp(Level.FINER,
 171                     RequiredModelMBean.class.getName(),
 172                     "RequiredModelMBean()", "Entry");
 173         }
 174         modelMBeanInfo = createDefaultModelMBeanInfo();
 175         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 176             MODELMBEAN_LOGGER.logp(Level.FINER,
 177                     RequiredModelMBean.class.getName(),
 178                     "RequiredModelMBean()", "Exit");
 179         }
 180     }
 181 
 182     /**
 183      * Constructs a RequiredModelMBean object using ModelMBeanInfo passed in.
 184      * As long as the RequiredModelMBean is not registered
 185      * with the MBeanServer yet, the RequiredModelMBean's MBeanInfo and
 186      * Descriptors can be customized using the {@link #setModelMBeanInfo}
 187      * method.
 188      * After the RequiredModelMBean's MBeanInfo and Descriptors are
 189      * customized, the RequiredModelMBean can be registered with the
 190      * MBeanServer.
 191      *
 192      * @param mbi The ModelMBeanInfo object to be used by the
 193      *            RequiredModelMBean. The given ModelMBeanInfo is cloned
 194      *            and modified as specified by {@link #setModelMBeanInfo}
 195      *
 196      * @exception MBeanException Wraps a distributed communication Exception.
 197      * @exception RuntimeOperationsException Wraps an
 198      *    {link java.lang.IllegalArgumentException}:
 199      *          The MBeanInfo passed in parameter is null.
 200      *
 201      **/
 202     public RequiredModelMBean(ModelMBeanInfo mbi)
 203         throws MBeanException, RuntimeOperationsException {
 204 
 205         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 206             MODELMBEAN_LOGGER.logp(Level.FINER,
 207                     RequiredModelMBean.class.getName(),
 208                     "RequiredModelMBean(MBeanInfo)", "Entry");
 209         }
 210         setModelMBeanInfo(mbi);
 211 
 212         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 213             MODELMBEAN_LOGGER.logp(Level.FINER,
 214                     RequiredModelMBean.class.getName(),
 215                     "RequiredModelMBean(MBeanInfo)", "Exit");
 216         }
 217     }
 218 
 219 
 220     /*************************************/
 221     /* initializers                      */
 222     /*************************************/
 223 
 224     /**
 225      * Initializes a ModelMBean object using ModelMBeanInfo passed in.
 226      * This method makes it possible to set a customized ModelMBeanInfo on
 227      * the ModelMBean as long as it is not registered with the MBeanServer.
 228      * <br>
 229      * Once the ModelMBean's ModelMBeanInfo (with Descriptors) are
 230      * customized and set on the ModelMBean, the  ModelMBean be
 231      * registered with the MBeanServer.
 232      * <P>
 233      * If the ModelMBean is currently registered, this method throws
 234      * a {@link javax.management.RuntimeOperationsException} wrapping an
 235      * {@link IllegalStateException}
 236      * <P>
 237      * If the given <var>inModelMBeanInfo</var> does not contain any
 238      * {@link ModelMBeanNotificationInfo} for the <code>GENERIC</code>
 239      * or <code>ATTRIBUTE_CHANGE</code> notifications, then the
 240      * RequiredModelMBean will supply its own default
 241      * {@link ModelMBeanNotificationInfo ModelMBeanNotificationInfo}s for
 242      * those missing notifications.
 243      *
 244      * @param mbi The ModelMBeanInfo object to be used
 245      *        by the ModelMBean.
 246      *
 247      * @exception MBeanException Wraps a distributed communication
 248      *        Exception.
 249      * @exception RuntimeOperationsException
 250      * <ul><li>Wraps an {@link IllegalArgumentException} if
 251      *         the MBeanInfo passed in parameter is null.</li>
 252      *     <li>Wraps an {@link IllegalStateException} if the ModelMBean
 253      *         is currently registered in the MBeanServer.</li>
 254      * </ul>
 255      *
 256      **/
 257     public void setModelMBeanInfo(ModelMBeanInfo mbi)
 258         throws MBeanException, RuntimeOperationsException {
 259 
 260         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 261             MODELMBEAN_LOGGER.logp(Level.FINER,
 262                     RequiredModelMBean.class.getName(),
 263                 "setModelMBeanInfo(ModelMBeanInfo)","Entry");
 264         }
 265 
 266         if (mbi == null) {
 267             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 268                 MODELMBEAN_LOGGER.logp(Level.FINER,
 269                         RequiredModelMBean.class.getName(),
 270                     "setModelMBeanInfo(ModelMBeanInfo)",
 271                     "ModelMBeanInfo is null: Raising exception.");
 272             }
 273             final RuntimeException x = new
 274                 IllegalArgumentException("ModelMBeanInfo must not be null");
 275             final String exceptionText =
 276                 "Exception occurred trying to initialize the " +
 277                 "ModelMBeanInfo of the RequiredModelMBean";
 278             throw new RuntimeOperationsException(x,exceptionText);
 279         }
 280 
 281         if (registered) {
 282             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 283                 MODELMBEAN_LOGGER.logp(Level.FINER,
 284                         RequiredModelMBean.class.getName(),
 285                     "setModelMBeanInfo(ModelMBeanInfo)",
 286                     "RequiredMBean is registered: Raising exception.");
 287             }
 288             final String exceptionText =
 289                 "Exception occurred trying to set the " +
 290                 "ModelMBeanInfo of the RequiredModelMBean";
 291             final RuntimeException x = new IllegalStateException(
 292              "cannot call setModelMBeanInfo while ModelMBean is registered");
 293             throw new RuntimeOperationsException(x,exceptionText);
 294         }
 295 
 296         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 297             MODELMBEAN_LOGGER.logp(Level.FINER,
 298                     RequiredModelMBean.class.getName(),
 299                 "setModelMBeanInfo(ModelMBeanInfo)",
 300                 "Setting ModelMBeanInfo to " + printModelMBeanInfo(mbi));
 301             int noOfNotifications = 0;
 302             if (mbi.getNotifications() != null) {
 303                 noOfNotifications = mbi.getNotifications().length;
 304             }
 305             MODELMBEAN_LOGGER.logp(Level.FINER,
 306                     RequiredModelMBean.class.getName(),
 307                 "setModelMBeanInfo(ModelMBeanInfo)",
 308                 "ModelMBeanInfo notifications has " +
 309                 noOfNotifications + " elements");
 310         }
 311 
 312         modelMBeanInfo = (ModelMBeanInfo)mbi.clone();
 313 
 314         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 315             MODELMBEAN_LOGGER.logp(Level.FINER,
 316                     RequiredModelMBean.class.getName(),
 317                 "setModelMBeanInfo(ModelMBeanInfo)","set mbeanInfo to: "+
 318                  printModelMBeanInfo(modelMBeanInfo));
 319             MODELMBEAN_LOGGER.logp(Level.FINER,
 320                     RequiredModelMBean.class.getName(),
 321                 "setModelMBeanInfo(ModelMBeanInfo)","Exit");
 322         }
 323     }
 324 
 325 
 326     /**
 327      * Sets the instance handle of the object against which to
 328      * execute all methods in this ModelMBean management interface
 329      * (MBeanInfo and Descriptors).
 330      *
 331      * @param mr Object that is the managed resource
 332      * @param mr_type The type of reference for the managed resource.
 333      *     <br>Can be: "ObjectReference", "Handle", "IOR", "EJBHandle",
 334      *         or "RMIReference".
 335      *     <br>In this implementation only "ObjectReference" is supported.
 336      *
 337      * @exception MBeanException The initializer of the object has
 338      *            thrown an exception.
 339      * @exception InstanceNotFoundException The managed resource
 340      *            object could not be found
 341      * @exception InvalidTargetObjectTypeException The managed
 342      *            resource type should be "ObjectReference".
 343      * @exception RuntimeOperationsException Wraps a {@link
 344      *            RuntimeException} when setting the resource.
 345      **/
 346     public void setManagedResource(Object mr, String mr_type)
 347         throws MBeanException, RuntimeOperationsException,
 348                InstanceNotFoundException, InvalidTargetObjectTypeException {
 349         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 350             MODELMBEAN_LOGGER.logp(Level.FINER,
 351                     RequiredModelMBean.class.getName(),
 352                 "setManagedResource(Object,String)","Entry");
 353         }
 354 
 355         // check that the mr_type is supported by this JMXAgent
 356         // only "objectReference" is supported
 357         if ((mr_type == null) ||
 358             (! mr_type.equalsIgnoreCase("objectReference"))) {
 359             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 360                 MODELMBEAN_LOGGER.logp(Level.FINER,
 361                         RequiredModelMBean.class.getName(),
 362                     "setManagedResource(Object,String)",
 363                     "Managed Resource Type is not supported: " + mr_type);
 364             }
 365             throw new InvalidTargetObjectTypeException(mr_type);
 366         }
 367 
 368         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 369             MODELMBEAN_LOGGER.logp(Level.FINER,
 370                     RequiredModelMBean.class.getName(),
 371                 "setManagedResource(Object,String)",
 372                 "Managed Resource is valid");
 373         }
 374         managedResource = mr;
 375 
 376         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 377             MODELMBEAN_LOGGER.logp(Level.FINER,
 378                     RequiredModelMBean.class.getName(),
 379                 "setManagedResource(Object, String)", "Exit");
 380         }
 381     }
 382 
 383     /**
 384      * <p>Instantiates this MBean instance with the data found for
 385      * the MBean in the persistent store.  The data loaded could include
 386      * attribute and operation values.</p>
 387      *
 388      * <p>This method should be called during construction or
 389      * initialization of this instance, and before the MBean is
 390      * registered with the MBeanServer.</p>
 391      *
 392      * <p>If the implementation of this class does not support
 393      * persistence, an {@link MBeanException} wrapping a {@link
 394      * ServiceNotFoundException} is thrown.</p>
 395      *
 396      * @exception MBeanException Wraps another exception, or
 397      * persistence is not supported
 398      * @exception RuntimeOperationsException Wraps exceptions from the
 399      * persistence mechanism
 400      * @exception InstanceNotFoundException Could not find or load
 401      * this MBean from persistent storage
 402      */
 403     public void load()
 404         throws MBeanException, RuntimeOperationsException,
 405                InstanceNotFoundException {
 406         final ServiceNotFoundException x = new ServiceNotFoundException(
 407                                 "Persistence not supported for this MBean");
 408         throw new MBeanException(x, x.getMessage());
 409     }
 410 
 411         /**
 412      * <p>Captures the current state of this MBean instance and writes
 413      * it out to the persistent store.  The state stored could include
 414      * attribute and operation values.</p>
 415      *
 416      * <p>If the implementation of this class does not support
 417      * persistence, an {@link MBeanException} wrapping a {@link
 418      * ServiceNotFoundException} is thrown.</p>
 419      *
 420      * <p>Persistence policy from the MBean and attribute descriptor
 421      * is used to guide execution of this method. The MBean should be
 422      * stored if 'persistPolicy' field is:</p>
 423      *
 424      * <PRE>{@literal  != "never"
 425      *   = "always"
 426      *   = "onTimer" and now > 'lastPersistTime' + 'persistPeriod'
 427      *   = "NoMoreOftenThan" and now > 'lastPersistTime' + 'persistPeriod'
 428      *   = "onUnregister"
 429      * }</PRE>
 430      *
 431      * <p>Do not store the MBean if 'persistPolicy' field is:</p>
 432      * <PRE>{@literal
 433      *    = "never"
 434      *    = "onUpdate"
 435      *    = "onTimer" && now < 'lastPersistTime' + 'persistPeriod'
 436      * }</PRE>
 437      *
 438      * @exception MBeanException Wraps another exception, or
 439      * persistence is not supported
 440      * @exception RuntimeOperationsException Wraps exceptions from the
 441      * persistence mechanism
 442      * @exception InstanceNotFoundException Could not find/access the
 443      * persistent store
 444      */
 445     public void store()
 446         throws MBeanException, RuntimeOperationsException,
 447                InstanceNotFoundException {
 448         final ServiceNotFoundException x = new ServiceNotFoundException(
 449                                 "Persistence not supported for this MBean");
 450         throw new MBeanException(x, x.getMessage());
 451     }
 452 
 453     /*************************************/
 454     /* DynamicMBean Interface            */
 455     /*************************************/
 456 
 457     /**
 458      * The resolveForCacheValue method checks the descriptor passed in to
 459      * see if there is a valid cached value in the descriptor.
 460      * The valid value will be in the 'value' field if there is one.
 461      * If the 'currencyTimeLimit' field in the descriptor is:
 462      * <ul>
 463      *   <li><b>&lt;0</b> Then the value is not cached and is never valid.
 464      *         Null is returned. The 'value' and 'lastUpdatedTimeStamp'
 465      *         fields are cleared.</li>
 466      *   <li><b>=0</b> Then the value is always cached and always valid.
 467      *         The 'value' field is returned.
 468      *         The 'lastUpdatedTimeStamp' field is not checked.</li>
 469      *   <li><b>&gt;0</b> Represents the number of seconds that the
 470      *         'value' field is valid.
 471      *         The 'value' field is no longer valid when
 472      *         'lastUpdatedTimeStamp' + 'currencyTimeLimit' &gt; Now.
 473      *       <ul>
 474      *       <li>When 'value' is valid, 'valid' is returned.</li>
 475      *       <li>When 'value' is no longer valid then null is returned and
 476      *           'value' and 'lastUpdatedTimeStamp' fields are cleared.</li>
 477      *       </ul>
 478      *   </li>
 479      * </ul>
 480      *
 481      **/
 482     private Object resolveForCacheValue(Descriptor descr)
 483         throws MBeanException, RuntimeOperationsException {
 484 
 485         final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.FINER);
 486         final String mth = "resolveForCacheValue(Descriptor)";
 487         if (tracing) {
 488             MODELMBEAN_LOGGER.logp(Level.FINER,
 489                     RequiredModelMBean.class.getName(),mth,"Entry");
 490         }
 491 
 492         Object response = null;
 493         boolean resetValue = false, returnCachedValue = true;
 494         long currencyPeriod = 0;
 495 
 496         if (descr == null) {
 497             if (tracing) {
 498                 MODELMBEAN_LOGGER.logp(Level.FINER,
 499                         RequiredModelMBean.class.getName(),mth,
 500                     "Input Descriptor is null");
 501             }
 502             return response;
 503         }
 504 
 505         if (tracing) {
 506             MODELMBEAN_LOGGER.logp(Level.FINER,
 507                     RequiredModelMBean.class.getName(),
 508                     mth, "descriptor is " + descr);
 509         }
 510 
 511         final Descriptor mmbDescr = modelMBeanInfo.getMBeanDescriptor();
 512         if (mmbDescr == null) {
 513             if (tracing) {
 514                 MODELMBEAN_LOGGER.logp(Level.FINER,
 515                     RequiredModelMBean.class.getName(),
 516                         mth,"MBean Descriptor is null");
 517             }
 518             //return response;
 519         }
 520 
 521         Object objExpTime = descr.getFieldValue("currencyTimeLimit");
 522 
 523         String expTime;
 524         if (objExpTime != null) {
 525             expTime = objExpTime.toString();
 526         } else {
 527             expTime = null;
 528         }
 529 
 530         if ((expTime == null) && (mmbDescr != null)) {
 531             objExpTime = mmbDescr.getFieldValue("currencyTimeLimit");
 532             if (objExpTime != null) {
 533                 expTime = objExpTime.toString();
 534             } else {
 535                 expTime = null;
 536             }
 537         }
 538 
 539         if (expTime != null) {
 540             if (tracing) {
 541                 MODELMBEAN_LOGGER.logp(Level.FINER,
 542                     RequiredModelMBean.class.getName(),
 543                         mth,"currencyTimeLimit: " + expTime);
 544             }
 545 
 546             // convert seconds to milliseconds for time comparison
 547             currencyPeriod = Long.parseLong(expTime) * 1000;
 548             if (currencyPeriod < 0) {
 549                 /* if currencyTimeLimit is -1 then value is never cached */
 550                 returnCachedValue = false;
 551                 resetValue = true;
 552                 if (tracing) {
 553                     MODELMBEAN_LOGGER.logp(Level.FINER,
 554                             RequiredModelMBean.class.getName(),mth,
 555                         currencyPeriod + ": never Cached");
 556                 }
 557             } else if (currencyPeriod == 0) {
 558                 /* if currencyTimeLimit is 0 then value is always cached */
 559                 returnCachedValue = true;
 560                 resetValue = false;
 561                 if (tracing) {
 562                     MODELMBEAN_LOGGER.logp(Level.FINER,
 563                             RequiredModelMBean.class.getName(),mth,
 564                         "always valid Cache");
 565                 }
 566             } else {
 567                 Object objtStamp =
 568                     descr.getFieldValue("lastUpdatedTimeStamp");
 569 
 570                 String tStamp;
 571                 if (objtStamp != null) tStamp = objtStamp.toString();
 572                 else tStamp = null;
 573 
 574                 if (tracing) {
 575                     MODELMBEAN_LOGGER.logp(Level.FINER,
 576                             RequiredModelMBean.class.getName(),mth,
 577                         "lastUpdatedTimeStamp: " + tStamp);
 578                 }
 579 
 580                 if (tStamp == null)
 581                     tStamp = "0";
 582 
 583                 long lastTime = Long.parseLong(tStamp);
 584 
 585                 if (tracing) {
 586                     MODELMBEAN_LOGGER.logp(Level.FINER,
 587                             RequiredModelMBean.class.getName(),mth,
 588                         "currencyPeriod:" + currencyPeriod +
 589                         " lastUpdatedTimeStamp:" + lastTime);
 590                 }
 591 
 592                 long now = (new Date()).getTime();
 593 
 594                 if (now < (lastTime + currencyPeriod)) {
 595                     returnCachedValue = true;
 596                     resetValue = false;
 597                     if (tracing) {
 598                         MODELMBEAN_LOGGER.logp(Level.FINER,
 599                                 RequiredModelMBean.class.getName(),mth,
 600                             " timed valid Cache for " + now + " < " +
 601                             (lastTime + currencyPeriod));
 602                     }
 603                 } else { /* value is expired */
 604                     returnCachedValue = false;
 605                     resetValue = true;
 606                     if (tracing) {
 607                         MODELMBEAN_LOGGER.logp(Level.FINER,
 608                                 RequiredModelMBean.class.getName(),mth,
 609                             "timed expired cache for " + now + " > " +
 610                             (lastTime + currencyPeriod));
 611                     }
 612                 }
 613             }
 614             if (tracing) {
 615                 MODELMBEAN_LOGGER.logp(Level.FINER,
 616                         RequiredModelMBean.class.getName(),mth,
 617                     "returnCachedValue:" + returnCachedValue +
 618                     " resetValue: " + resetValue);
 619             }
 620 
 621             if (returnCachedValue == true) {
 622                 Object currValue = descr.getFieldValue("value");
 623                 if (currValue != null) {
 624                     /* error/validity check return value here */
 625                     response = currValue;
 626                     /* need to cast string cached value to type */
 627                     if (tracing) {
 628                         MODELMBEAN_LOGGER.logp(Level.FINER,
 629                                 RequiredModelMBean.class.getName(),mth,
 630                             "valid Cache value: " + currValue);
 631                     }
 632 
 633                 } else {
 634                     response = null;
 635                     if (tracing) {
 636                         MODELMBEAN_LOGGER.logp(Level.FINER,
 637                             RequiredModelMBean.class.getName(),
 638                                 mth,"no Cached value");
 639                     }
 640                 }
 641             }
 642 
 643             if (resetValue == true) {
 644                 /* value is not current, so remove it */
 645                 descr.removeField("lastUpdatedTimeStamp");
 646                 descr.removeField("value");
 647                 response = null;
 648                 modelMBeanInfo.setDescriptor(descr,null);
 649                 if (tracing) {
 650                     MODELMBEAN_LOGGER.logp(Level.FINER,
 651                         RequiredModelMBean.class.getName(),
 652                             mth,"reset cached value to null");
 653                 }
 654             }
 655         }
 656 
 657         if (tracing) {
 658             MODELMBEAN_LOGGER.logp(Level.FINER,
 659                     RequiredModelMBean.class.getName(),mth,"Exit");
 660         }
 661 
 662         return response;
 663     }
 664 
 665     /**
 666      * Returns the attributes, operations, constructors and notifications
 667      * that this RequiredModelMBean exposes for management.
 668      *
 669      * @return  An instance of ModelMBeanInfo allowing retrieval all
 670      *          attributes, operations, and Notifications of this MBean.
 671      *
 672      **/
 673     public MBeanInfo getMBeanInfo() {
 674 
 675         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 676             MODELMBEAN_LOGGER.logp(Level.FINER,
 677                     RequiredModelMBean.class.getName(),
 678                     "getMBeanInfo()","Entry");
 679         }
 680 
 681         if (modelMBeanInfo == null) {
 682             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 683                 MODELMBEAN_LOGGER.logp(Level.FINER,
 684                         RequiredModelMBean.class.getName(),
 685                     "getMBeanInfo()","modelMBeanInfo is null");
 686             }
 687             modelMBeanInfo = createDefaultModelMBeanInfo();
 688             //return new ModelMBeanInfo(" ", "", null, null, null, null);
 689         }
 690 
 691         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 692             MODELMBEAN_LOGGER.logp(Level.FINER,
 693                     RequiredModelMBean.class.getName(),
 694                 "getMBeanInfo()","ModelMBeanInfo is " +
 695                   modelMBeanInfo.getClassName() + " for " +
 696                   modelMBeanInfo.getDescription());
 697             MODELMBEAN_LOGGER.logp(Level.FINER,
 698                     RequiredModelMBean.class.getName(),
 699                 "getMBeanInfo()",printModelMBeanInfo(modelMBeanInfo));
 700         }
 701 
 702         return((MBeanInfo) modelMBeanInfo.clone());
 703     }
 704 
 705     private String printModelMBeanInfo(ModelMBeanInfo info) {
 706         final StringBuilder retStr = new StringBuilder();
 707         if (info == null) {
 708             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 709                 MODELMBEAN_LOGGER.logp(Level.FINER,
 710                         RequiredModelMBean.class.getName(),
 711                         "printModelMBeanInfo(ModelMBeanInfo)",
 712                         "ModelMBeanInfo to print is null, " +
 713                         "printing local ModelMBeanInfo");
 714             }
 715             info = modelMBeanInfo;
 716         }
 717 
 718         retStr.append("\nMBeanInfo for ModelMBean is:");
 719         retStr.append("\nCLASSNAME: \t"+ info.getClassName());
 720         retStr.append("\nDESCRIPTION: \t"+ info.getDescription());
 721 
 722 
 723         try {
 724             retStr.append("\nMBEAN DESCRIPTOR: \t"+
 725                           info.getMBeanDescriptor());
 726         } catch (Exception e) {
 727             retStr.append("\nMBEAN DESCRIPTOR: \t" + " is invalid");
 728         }
 729 
 730         retStr.append("\nATTRIBUTES");
 731 
 732         final MBeanAttributeInfo[] attrInfo = info.getAttributes();
 733         if ((attrInfo != null) && (attrInfo.length>0)) {
 734             for (int i=0; i<attrInfo.length; i++) {
 735                 final ModelMBeanAttributeInfo attInfo =
 736                     (ModelMBeanAttributeInfo)attrInfo[i];
 737                 retStr.append(" ** NAME: \t"+ attInfo.getName());
 738                 retStr.append("    DESCR: \t"+ attInfo.getDescription());
 739                 retStr.append("    TYPE: \t"+ attInfo.getType() +
 740                               "    READ: \t"+ attInfo.isReadable() +
 741                               "    WRITE: \t"+ attInfo.isWritable());
 742                 retStr.append("    DESCRIPTOR: " +
 743                               attInfo.getDescriptor().toString());
 744             }
 745         } else {
 746             retStr.append(" ** No attributes **");
 747         }
 748 
 749         retStr.append("\nCONSTRUCTORS");
 750         final MBeanConstructorInfo[] constrInfo = info.getConstructors();
 751         if ((constrInfo != null) && (constrInfo.length > 0 )) {
 752             for (int i=0; i<constrInfo.length; i++) {
 753                 final ModelMBeanConstructorInfo ctorInfo =
 754                     (ModelMBeanConstructorInfo)constrInfo[i];
 755                 retStr.append(" ** NAME: \t"+ ctorInfo.getName());
 756                 retStr.append("    DESCR: \t"+
 757                               ctorInfo.getDescription());
 758                 retStr.append("    PARAM: \t"+
 759                               ctorInfo.getSignature().length +
 760                               " parameter(s)");
 761                 retStr.append("    DESCRIPTOR: " +
 762                               ctorInfo.getDescriptor().toString());
 763             }
 764         } else {
 765             retStr.append(" ** No Constructors **");
 766         }
 767 
 768         retStr.append("\nOPERATIONS");
 769         final MBeanOperationInfo[] opsInfo = info.getOperations();
 770         if ((opsInfo != null) && (opsInfo.length>0)) {
 771             for (int i=0; i<opsInfo.length; i++) {
 772                 final ModelMBeanOperationInfo operInfo =
 773                     (ModelMBeanOperationInfo)opsInfo[i];
 774                 retStr.append(" ** NAME: \t"+ operInfo.getName());
 775                 retStr.append("    DESCR: \t"+ operInfo.getDescription());
 776                 retStr.append("    PARAM: \t"+
 777                               operInfo.getSignature().length +
 778                               " parameter(s)");
 779                 retStr.append("    DESCRIPTOR: " +
 780                               operInfo.getDescriptor().toString());
 781             }
 782         } else {
 783             retStr.append(" ** No operations ** ");
 784         }
 785 
 786         retStr.append("\nNOTIFICATIONS");
 787 
 788         MBeanNotificationInfo[] notifInfo = info.getNotifications();
 789         if ((notifInfo != null) && (notifInfo.length>0)) {
 790             for (int i=0; i<notifInfo.length; i++) {
 791                 final ModelMBeanNotificationInfo nInfo =
 792                     (ModelMBeanNotificationInfo)notifInfo[i];
 793                 retStr.append(" ** NAME: \t"+ nInfo.getName());
 794                 retStr.append("    DESCR: \t"+ nInfo.getDescription());
 795                 retStr.append("    DESCRIPTOR: " +
 796                               nInfo.getDescriptor().toString());
 797             }
 798         } else {
 799             retStr.append(" ** No notifications **");
 800         }
 801 
 802         retStr.append(" ** ModelMBean: End of MBeanInfo ** ");
 803 
 804         return retStr.toString();
 805     }
 806 
 807     /**
 808      * Invokes a method on or through a RequiredModelMBean and returns
 809      * the result of the method execution.
 810      * <P>
 811      * If the given method to be invoked, together with the provided
 812      * signature, matches one of RequiredModelMbean
 813      * accessible methods, this one will be call. Otherwise the call to
 814      * the given method will be tried on the managed resource.
 815      * <P>
 816      * The last value returned by an operation may be cached in
 817      * the operation's descriptor which
 818      * is in the ModelMBeanOperationInfo's descriptor.
 819      * The valid value will be in the 'value' field if there is one.
 820      * If the 'currencyTimeLimit' field in the descriptor is:
 821      * <UL>
 822      * <LI><b>&lt;0</b> Then the value is not cached and is never valid.
 823      *      The operation method is invoked.
 824      *      The 'value' and 'lastUpdatedTimeStamp' fields are cleared.</LI>
 825      * <LI><b>=0</b> Then the value is always cached and always valid.
 826      *      The 'value' field is returned. If there is no 'value' field
 827      *      then the operation method is invoked for the attribute.
 828      *      The 'lastUpdatedTimeStamp' field and `value' fields are set to
 829      *      the operation's return value and the current time stamp.</LI>
 830      * <LI><b>&gt;0</b> Represents the number of seconds that the 'value'
 831      *      field is valid.
 832      *      The 'value' field is no longer valid when
 833      *      'lastUpdatedTimeStamp' + 'currencyTimeLimit' &gt; Now.
 834      *      <UL>
 835      *         <LI>When 'value' is valid, 'value' is returned.</LI>
 836      *         <LI>When 'value' is no longer valid then the operation
 837      *             method is invoked. The 'lastUpdatedTimeStamp' field
 838      *             and `value' fields are updated.</lI>
 839      *      </UL>
 840      * </LI>
 841      * </UL>
 842      *
 843      * <p><b>Note:</b> because of inconsistencies in previous versions of
 844      * this specification, it is recommended not to use negative or zero
 845      * values for <code>currencyTimeLimit</code>.  To indicate that a
 846      * cached value is never valid, omit the
 847      * <code>currencyTimeLimit</code> field.  To indicate that it is
 848      * always valid, use a very large number for this field.</p>
 849      *
 850      * @param opName The name of the method to be invoked. The
 851      *     name can be the fully qualified method name including the
 852      *     classname, or just the method name if the classname is
 853      *     defined in the 'class' field of the operation descriptor.
 854      * @param opArgs An array containing the parameters to be set
 855      *     when the operation is invoked
 856      * @param sig An array containing the signature of the
 857      *     operation. The class objects will be loaded using the same
 858      *     class loader as the one used for loading the MBean on which
 859      *     the operation was invoked.
 860      *
 861      * @return  The object returned by the method, which represents the
 862      *     result of invoking the method on the specified managed resource.
 863      *
 864      * @exception MBeanException  Wraps one of the following Exceptions:
 865      * <UL>
 866      * <LI> An Exception thrown by the managed object's invoked method.</LI>
 867      * <LI> {@link ServiceNotFoundException}: No ModelMBeanOperationInfo or
 868      *      no descriptor defined for the specified operation or the managed
 869      *      resource is null.</LI>
 870      * <LI> {@link InvalidTargetObjectTypeException}: The 'targetType'
 871      *      field value is not 'objectReference'.</LI>
 872      * </UL>
 873      * @exception ReflectionException  Wraps an {@link java.lang.Exception}
 874      *      thrown while trying to invoke the method.
 875      * @exception RuntimeOperationsException Wraps an
 876      *      {@link IllegalArgumentException} Method name is null.
 877      *
 878      **/
 879     /*
 880       The requirement to be able to invoke methods on the
 881       RequiredModelMBean class itself makes this method considerably
 882       more complicated than it might otherwise be.  Note that, unlike
 883       earlier versions, we do not allow you to invoke such methods if
 884       they are not explicitly mentioned in the ModelMBeanInfo.  Doing
 885       so was potentially a security problem, and certainly very
 886       surprising.
 887 
 888       We do not look for the method in the RequiredModelMBean class
 889       itself if:
 890       (a) there is a "targetObject" field in the Descriptor for the
 891       operation; or
 892       (b) there is a "class" field in the Descriptor for the operation
 893       and the named class is not RequiredModelMBean or one of its
 894       superinterfaces; or
 895       (c) the name of the operation is not the name of a method in
 896       RequiredModelMBean (this is just an optimization).
 897 
 898       In cases (a) and (b), if you have gone to the trouble of adding
 899       those fields specifically for this operation then presumably you
 900       do not want RequiredModelMBean's methods to be called.
 901 
 902       We have to pay attention to class loading issues.  If the
 903       "class" field is present, the named class has to be resolved
 904       relative to RequiredModelMBean's class loader to test the
 905       condition (b) above, and relative to the managed resource's
 906       class loader to ensure that the managed resource is in fact of
 907       the named class (or a subclass).  The class names in the sig
 908       array likewise have to be resolved, first against
 909       RequiredModelMBean's class loader, then against the managed
 910       resource's class loader.  There is no point in using any other
 911       loader because when we call Method.invoke we must call it on
 912       a Method that is implemented by the target object.
 913      */
 914     public Object invoke(String opName, Object[] opArgs, String[] sig)
 915             throws MBeanException, ReflectionException {
 916 
 917         final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.FINER);
 918         final String mth = "invoke(String, Object[], String[])";
 919 
 920         if (tracing) {
 921             MODELMBEAN_LOGGER.logp(Level.FINER,
 922                     RequiredModelMBean.class.getName(), mth, "Entry");
 923         }
 924 
 925         if (opName == null) {
 926             final RuntimeException x =
 927                 new IllegalArgumentException("Method name must not be null");
 928             throw new RuntimeOperationsException(x,
 929                       "An exception occurred while trying to " +
 930                       "invoke a method on a RequiredModelMBean");
 931         }
 932 
 933         String opClassName = null;
 934         String opMethodName;
 935 
 936         // Parse for class name and method
 937         int opSplitter = opName.lastIndexOf('.');
 938         if (opSplitter > 0) {
 939             opClassName = opName.substring(0,opSplitter);
 940             opMethodName = opName.substring(opSplitter+1);
 941         } else
 942             opMethodName = opName;
 943 
 944         /* Ignore anything after a left paren.  We keep this for
 945            compatibility but it isn't specified.  */
 946         opSplitter = opMethodName.indexOf('(');
 947         if (opSplitter > 0)
 948             opMethodName = opMethodName.substring(0,opSplitter);
 949 
 950         if (tracing) {
 951             MODELMBEAN_LOGGER.logp(Level.FINER,
 952                     RequiredModelMBean.class.getName(),
 953                     mth, "Finding operation " + opName + " as " + opMethodName);
 954         }
 955 
 956         ModelMBeanOperationInfo opInfo =
 957             modelMBeanInfo.getOperation(opMethodName);
 958         if (opInfo == null) {
 959             final String msg =
 960                 "Operation " + opName + " not in ModelMBeanInfo";
 961             throw new MBeanException(new ServiceNotFoundException(msg), msg);
 962         }
 963 
 964         final Descriptor opDescr = opInfo.getDescriptor();
 965         if (opDescr == null) {
 966             final String msg = "Operation descriptor null";
 967             throw new MBeanException(new ServiceNotFoundException(msg), msg);
 968         }
 969 
 970         final Object cached = resolveForCacheValue(opDescr);
 971         if (cached != null) {
 972             if (tracing) {
 973                 MODELMBEAN_LOGGER.logp(Level.FINER,
 974                         RequiredModelMBean.class.getName(),
 975                         mth,
 976                         "Returning cached value");
 977             }
 978             return cached;
 979         }
 980 
 981         if (opClassName == null)
 982             opClassName = (String) opDescr.getFieldValue("class");
 983         // may still be null now
 984 
 985         opMethodName = (String) opDescr.getFieldValue("name");
 986         if (opMethodName == null) {
 987             final String msg =
 988                 "Method descriptor must include `name' field";
 989             throw new MBeanException(new ServiceNotFoundException(msg), msg);
 990         }
 991 
 992         final String targetTypeField = (String)
 993             opDescr.getFieldValue("targetType");
 994         if (targetTypeField != null
 995             && !targetTypeField.equalsIgnoreCase("objectReference")) {
 996             final String msg =
 997                 "Target type must be objectReference: " + targetTypeField;
 998             throw new MBeanException(new InvalidTargetObjectTypeException(msg),
 999                                      msg);
1000         }
1001 
1002         final Object targetObjectField = opDescr.getFieldValue("targetObject");
1003         if (tracing && targetObjectField != null)
1004                 MODELMBEAN_LOGGER.logp(Level.FINER,
1005                     RequiredModelMBean.class.getName(),
1006                         mth, "Found target object in descriptor");
1007 
1008         /* Now look for the method, either in RequiredModelMBean itself
1009            or in the target object.  Set "method" and "targetObject"
1010            appropriately.  */
1011         Method method;
1012         Object targetObject;
1013 
1014         method = findRMMBMethod(opMethodName, targetObjectField,
1015                                 opClassName, sig);
1016 
1017         if (method != null)
1018             targetObject = this;
1019         else {
1020             if (tracing) {
1021                 MODELMBEAN_LOGGER.logp(Level.FINER,
1022                     RequiredModelMBean.class.getName(),
1023                         mth, "looking for method in managedResource class");
1024             }
1025             if (targetObjectField != null)
1026                 targetObject = targetObjectField;
1027             else {
1028                 targetObject = managedResource;
1029                 if (targetObject == null) {
1030                     final String msg =
1031                         "managedResource for invoke " + opName +
1032                         " is null";
1033                     Exception snfe = new ServiceNotFoundException(msg);
1034                     throw new MBeanException(snfe);
1035                 }
1036             }
1037 
1038             final Class<?> targetClass;
1039 
1040             if (opClassName != null) {
1041                 try {
1042                     AccessControlContext stack = AccessController.getContext();
1043                     final Object obj = targetObject;
1044                     final String className = opClassName;
1045                     final ClassNotFoundException[] caughtException = new ClassNotFoundException[1];
1046 
1047                     targetClass = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Class<?>>() {
1048 
1049                         @Override
1050                         public Class<?> run() {
1051                             try {
1052                                 ReflectUtil.checkPackageAccess(className);
1053                                 final ClassLoader targetClassLoader =
1054                                     obj.getClass().getClassLoader();
1055                                 return Class.forName(className, false,
1056                                                             targetClassLoader);
1057                             } catch (ClassNotFoundException e) {
1058                                 caughtException[0] = e;
1059                             }
1060                             return null;
1061                         }
1062                     }, stack, acc);
1063 
1064                     if (caughtException[0] != null) {
1065                         throw caughtException[0];
1066                     }
1067                 } catch (ClassNotFoundException e) {
1068                     final String msg =
1069                         "class for invoke " + opName + " not found";
1070                     throw new ReflectionException(e, msg);
1071                 }
1072             } else
1073                 targetClass = targetObject.getClass();
1074 
1075             method = resolveMethod(targetClass, opMethodName, sig);
1076         }
1077 
1078         if (tracing) {
1079             MODELMBEAN_LOGGER.logp(Level.FINER,
1080                 RequiredModelMBean.class.getName(),
1081                     mth, "found " + opMethodName + ", now invoking");
1082         }
1083 
1084         final Object result =
1085             invokeMethod(opName, method, targetObject, opArgs);
1086 
1087         if (tracing) {
1088             MODELMBEAN_LOGGER.logp(Level.FINER,
1089                 RequiredModelMBean.class.getName(),
1090                     mth, "successfully invoked method");
1091         }
1092 
1093         if (result != null)
1094             cacheResult(opInfo, opDescr, result);
1095 
1096         return result;
1097     }
1098 
1099     private Method resolveMethod(Class<?> targetClass,
1100                                         String opMethodName,
1101                                         final String[] sig)
1102             throws ReflectionException {
1103         final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.FINER);
1104 
1105         if (tracing) {
1106             MODELMBEAN_LOGGER.logp(Level.FINER,
1107                 RequiredModelMBean.class.getName(),"resolveMethod",
1108                   "resolving " + targetClass.getName() + "." + opMethodName);
1109         }
1110 
1111         final Class<?>[] argClasses;
1112 
1113         if (sig == null)
1114             argClasses = null;
1115         else {
1116             final AccessControlContext stack = AccessController.getContext();
1117             final ReflectionException[] caughtException = new ReflectionException[1];
1118             final ClassLoader targetClassLoader = targetClass.getClassLoader();
1119             argClasses = new Class<?>[sig.length];
1120 
1121             javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Void>() {
1122 
1123                 @Override
1124                 public Void run() {
1125                     for (int i = 0; i < sig.length; i++) {
1126                         if (tracing) {
1127                             MODELMBEAN_LOGGER.logp(Level.FINER,
1128                                 RequiredModelMBean.class.getName(),"resolveMethod",
1129                                     "resolve type " + sig[i]);
1130                         }
1131                         argClasses[i] = (Class<?>) primitiveClassMap.get(sig[i]);
1132                         if (argClasses[i] == null) {
1133                             try {
1134                                 ReflectUtil.checkPackageAccess(sig[i]);
1135                                 argClasses[i] =
1136                                     Class.forName(sig[i], false, targetClassLoader);
1137                             } catch (ClassNotFoundException e) {
1138                                 if (tracing) {
1139                                     MODELMBEAN_LOGGER.logp(Level.FINER,
1140                                             RequiredModelMBean.class.getName(),
1141                                             "resolveMethod",
1142                                             "class not found");
1143                                 }
1144                                 final String msg = "Parameter class not found";
1145                                 caughtException[0] = new ReflectionException(e, msg);
1146                             }
1147                         }
1148                     }
1149                     return null;
1150                 }
1151             }, stack, acc);
1152 
1153             if (caughtException[0] != null) {
1154                 throw caughtException[0];
1155             }
1156         }
1157 
1158         try {
1159             return targetClass.getMethod(opMethodName, argClasses);
1160         } catch (NoSuchMethodException e) {
1161             final String msg =
1162                 "Target method not found: " + targetClass.getName() + "." +
1163                 opMethodName;
1164             throw new ReflectionException(e, msg);
1165         }
1166     }
1167 
1168     /* Map e.g. "int" to int.class.  Goodness knows how many time this
1169        particular wheel has been reinvented.  */
1170     private static final Class<?>[] primitiveClasses = {
1171         int.class, long.class, boolean.class, double.class,
1172         float.class, short.class, byte.class, char.class,
1173     };
1174     private static final Map<String,Class<?>> primitiveClassMap =
1175         new HashMap<String,Class<?>>();
1176     static {
1177         for (int i = 0; i < primitiveClasses.length; i++) {
1178             final Class<?> c = primitiveClasses[i];
1179             primitiveClassMap.put(c.getName(), c);
1180         }
1181     }
1182 
1183     /* Find a method in RequiredModelMBean as determined by the given
1184        parameters.  Return null if there is none, or if the parameters
1185        exclude using it.  Called from invoke. */
1186     private Method findRMMBMethod(String opMethodName,
1187                                          Object targetObjectField,
1188                                          String opClassName,
1189                                          String[] sig) {
1190         final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.FINER);
1191 
1192         if (tracing) {
1193             MODELMBEAN_LOGGER.logp(Level.FINER,
1194                 RequiredModelMBean.class.getName(),
1195                     "invoke(String, Object[], String[])",
1196                   "looking for method in RequiredModelMBean class");
1197         }
1198 
1199         if (!isRMMBMethodName(opMethodName))
1200             return null;
1201         if (targetObjectField != null)
1202             return null;
1203         final Class<RequiredModelMBean> rmmbClass = RequiredModelMBean.class;
1204         final Class<?> targetClass;
1205         if (opClassName == null)
1206             targetClass = rmmbClass;
1207         else {
1208             AccessControlContext stack = AccessController.getContext();
1209             final String className = opClassName;
1210             targetClass = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Class<?>>() {
1211 
1212                 @Override
1213                 public Class<?> run() {
1214                     try {
1215                         ReflectUtil.checkPackageAccess(className);
1216                         final ClassLoader targetClassLoader =
1217                             rmmbClass.getClassLoader();
1218                         Class<?> clz = Class.forName(className, false,
1219                                                     targetClassLoader);
1220                         if (!rmmbClass.isAssignableFrom(clz))
1221                             return null;
1222                         return clz;
1223                     } catch (ClassNotFoundException e) {
1224                         return null;
1225                     }
1226                 }
1227             }, stack, acc);
1228         }
1229         try {
1230             return targetClass != null ? resolveMethod(targetClass, opMethodName, sig) : null;
1231         } catch (ReflectionException e) {
1232             return null;
1233         }
1234     }
1235 
1236     /*
1237      * Invoke the given method, and throw the somewhat unpredictable
1238      * appropriate exception if the method itself gets an exception.
1239      */
1240     private Object invokeMethod(String opName, final Method method,
1241                                 final Object targetObject, final Object[] opArgs)
1242             throws MBeanException, ReflectionException {
1243         try {
1244             final Throwable[] caughtException = new Throwable[1];
1245             AccessControlContext stack = AccessController.getContext();
1246             Object rslt = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Object>() {
1247 
1248                 @Override
1249                 public Object run() {
1250                     try {
1251                         ReflectUtil.checkPackageAccess(method.getDeclaringClass());
1252                         return MethodUtil.invoke(method, targetObject, opArgs);
1253                     } catch (InvocationTargetException e) {
1254                         caughtException[0] = e;
1255                     } catch (IllegalAccessException e) {
1256                         caughtException[0] = e;
1257                     }
1258                     return null;
1259                 }
1260             }, stack, acc);
1261             if (caughtException[0] != null) {
1262                 if (caughtException[0] instanceof Exception) {
1263                     throw (Exception)caughtException[0];
1264                 } else if(caughtException[0] instanceof Error) {
1265                     throw (Error)caughtException[0];
1266                 }
1267             }
1268             return rslt;
1269         } catch (RuntimeErrorException ree) {
1270             throw new RuntimeOperationsException(ree,
1271                       "RuntimeException occurred in RequiredModelMBean "+
1272                       "while trying to invoke operation " + opName);
1273         } catch (RuntimeException re) {
1274             throw new RuntimeOperationsException(re,
1275                       "RuntimeException occurred in RequiredModelMBean "+
1276                       "while trying to invoke operation " + opName);
1277         } catch (IllegalAccessException iae) {
1278             throw new ReflectionException(iae,
1279                       "IllegalAccessException occurred in " +
1280                       "RequiredModelMBean while trying to " +
1281                       "invoke operation " + opName);
1282         } catch (InvocationTargetException ite) {
1283             Throwable mmbTargEx = ite.getTargetException();
1284             if (mmbTargEx instanceof RuntimeException) {
1285                 throw new MBeanException ((RuntimeException)mmbTargEx,
1286                       "RuntimeException thrown in RequiredModelMBean "+
1287                       "while trying to invoke operation " + opName);
1288             } else if (mmbTargEx instanceof Error) {
1289                 throw new RuntimeErrorException((Error)mmbTargEx,
1290                       "Error occurred in RequiredModelMBean while trying "+
1291                       "to invoke operation " + opName);
1292             } else if (mmbTargEx instanceof ReflectionException) {
1293                 throw (ReflectionException) mmbTargEx;
1294             } else {
1295                 throw new MBeanException ((Exception)mmbTargEx,
1296                       "Exception thrown in RequiredModelMBean "+
1297                       "while trying to invoke operation " + opName);
1298             }
1299         } catch (Error err) {
1300             throw new RuntimeErrorException(err,
1301                   "Error occurred in RequiredModelMBean while trying "+
1302                   "to invoke operation " + opName);
1303         } catch (Exception e) {
1304             throw new ReflectionException(e,
1305                   "Exception occurred in RequiredModelMBean while " +
1306                   "trying to invoke operation " + opName);
1307         }
1308     }
1309 
1310     /*
1311      * Cache the result of an operation in the descriptor, if that is
1312      * called for by the descriptor's configuration.  Note that we
1313      * don't remember operation parameters when caching the result, so
1314      * this is unlikely to be useful if there are any.
1315      */
1316     private void cacheResult(ModelMBeanOperationInfo opInfo,
1317                              Descriptor opDescr, Object result)
1318             throws MBeanException {
1319 
1320         Descriptor mmbDesc =
1321             modelMBeanInfo.getMBeanDescriptor();
1322 
1323         Object objctl =
1324             opDescr.getFieldValue("currencyTimeLimit");
1325         String ctl;
1326         if (objctl != null) {
1327             ctl = objctl.toString();
1328         } else {
1329             ctl = null;
1330         }
1331         if ((ctl == null) && (mmbDesc != null)) {
1332             objctl =
1333                 mmbDesc.getFieldValue("currencyTimeLimit");
1334             if (objctl != null) {
1335                 ctl = objctl.toString();
1336             } else {
1337                 ctl = null;
1338             }
1339         }
1340         if ((ctl != null) && !(ctl.equals("-1"))) {
1341             opDescr.setField("value", result);
1342             opDescr.setField("lastUpdatedTimeStamp",
1343                     String.valueOf((new Date()).getTime()));
1344 
1345 
1346             modelMBeanInfo.setDescriptor(opDescr,
1347                                          "operation");
1348             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
1349                 MODELMBEAN_LOGGER.logp(Level.FINER,
1350                         RequiredModelMBean.class.getName(),
1351                         "invoke(String,Object[],Object[])",
1352                         "new descriptor is " + opDescr);
1353             }
1354         }
1355     }
1356 
1357     /*
1358      * Determine whether the given name is the name of a public method
1359      * in this class.  This is only an optimization: it prevents us
1360      * from trying to do argument type lookups and reflection on a
1361      * method that will obviously fail because it has the wrong name.
1362      *
1363      * The first time this method is called we do the reflection, and
1364      * every other time we reuse the remembered values.
1365      *
1366      * It's conceivable that the (possibly malicious) first caller
1367      * doesn't have the required permissions to do reflection, in
1368      * which case we don't touch anything so as not to interfere
1369      * with a later permissionful caller.
1370      */
1371     private static Set<String> rmmbMethodNames;
1372     private static synchronized boolean isRMMBMethodName(String name) {
1373         if (rmmbMethodNames == null) {
1374             try {
1375                 Set<String> names = new HashSet<String>();
1376                 Method[] methods = RequiredModelMBean.class.getMethods();
1377                 for (int i = 0; i < methods.length; i++)
1378                     names.add(methods[i].getName());
1379                 rmmbMethodNames = names;
1380             } catch (Exception e) {
1381                 return true;
1382                 // This is only an optimization so we'll go on to discover
1383                 // whether the name really is an RMMB method.
1384             }
1385         }
1386         return rmmbMethodNames.contains(name);
1387     }
1388 
1389     /**
1390      * Returns the value of a specific attribute defined for this
1391      * ModelMBean.
1392      * The last value returned by an attribute may be cached in the
1393      * attribute's descriptor.
1394      * The valid value will be in the 'value' field if there is one.
1395      * If the 'currencyTimeLimit' field in the descriptor is:
1396      * <UL>
1397      * <LI>  <b>&lt;0</b> Then the value is not cached and is never valid.
1398      *       The getter method is invoked for the attribute.
1399      *       The 'value' and 'lastUpdatedTimeStamp' fields are cleared.</LI>
1400      * <LI>  <b>=0</b> Then the value is always cached and always valid.
1401      *       The 'value' field is returned. If there is no'value' field
1402      *       then the getter method is invoked for the attribute.
1403      *       The 'lastUpdatedTimeStamp' field and `value' fields are set
1404      *       to the attribute's value and the current time stamp.</LI>
1405      * <LI>  <b>&gt;0</b> Represents the number of seconds that the 'value'
1406      *       field is valid.
1407      *       The 'value' field is no longer valid when
1408      *       'lastUpdatedTimeStamp' + 'currencyTimeLimit' &gt; Now.
1409      *   <UL>
1410      *        <LI>When 'value' is valid, 'value' is returned.</LI>
1411      *        <LI>When 'value' is no longer valid then the getter
1412      *            method is invoked for the attribute.
1413      *            The 'lastUpdatedTimeStamp' field and `value' fields
1414      *            are updated.</LI>
1415      *   </UL></LI>
1416      * </UL>
1417      *
1418      * <p><b>Note:</b> because of inconsistencies in previous versions of
1419      * this specification, it is recommended not to use negative or zero
1420      * values for <code>currencyTimeLimit</code>.  To indicate that a
1421      * cached value is never valid, omit the
1422      * <code>currencyTimeLimit</code> field.  To indicate that it is
1423      * always valid, use a very large number for this field.</p>
1424      *
1425      * <p>If the 'getMethod' field contains the name of a valid
1426      * operation descriptor, then the method described by the
1427      * operation descriptor is executed.  The response from the
1428      * method is returned as the value of the attribute.  If the
1429      * operation fails or the returned value is not compatible with
1430      * the declared type of the attribute, an exception will be thrown.</p>
1431      *
1432      * <p>If no 'getMethod' field is defined then the default value of the
1433      * attribute is returned. If the returned value is not compatible with
1434      * the declared type of the attribute, an exception will be thrown.</p>
1435      *
1436      * <p>The declared type of the attribute is the String returned by
1437      * {@link ModelMBeanAttributeInfo#getType()}.  A value is compatible
1438      * with this type if one of the following is true:
1439      * <ul>
1440      * <li>the value is null;</li>
1441      * <li>the declared name is a primitive type name (such as "int")
1442      *     and the value is an instance of the corresponding wrapper
1443      *     type (such as java.lang.Integer);</li>
1444      * <li>the name of the value's class is identical to the declared name;</li>
1445      * <li>the declared name can be loaded by the value's class loader and
1446      *     produces a class to which the value can be assigned.</li>
1447      * </ul>
1448      *
1449      * <p>In this implementation, in every case where the getMethod needs to
1450      * be called, because the method is invoked through the standard "invoke"
1451      * method and thus needs operationInfo, an operation must be specified
1452      * for that getMethod so that the invocation works correctly.</p>
1453      *
1454      * @param attrName A String specifying the name of the
1455      * attribute to be retrieved. It must match the name of a
1456      * ModelMBeanAttributeInfo.
1457      *
1458      * @return The value of the retrieved attribute from the
1459      * descriptor 'value' field or from the invocation of the
1460      * operation in the 'getMethod' field of the descriptor.
1461      *
1462      * @exception AttributeNotFoundException The specified attribute is
1463      *    not accessible in the MBean.
1464      *    The following cases may result in an AttributeNotFoundException:
1465      *    <UL>
1466      *      <LI> No ModelMBeanInfo was found for the Model MBean.</LI>
1467      *      <LI> No ModelMBeanAttributeInfo was found for the specified
1468      *           attribute name.</LI>
1469      *      <LI> The ModelMBeanAttributeInfo isReadable method returns
1470      *           'false'.</LI>
1471      *    </UL>
1472      * @exception MBeanException  Wraps one of the following Exceptions:
1473      *    <UL>
1474      *      <LI> {@link InvalidAttributeValueException}: A wrong value type
1475      *           was received from the attribute's getter method or
1476      *           no 'getMethod' field defined in the descriptor for
1477      *           the attribute and no default value exists.</LI>
1478      *      <LI> {@link ServiceNotFoundException}: No
1479      *           ModelMBeanOperationInfo defined for the attribute's
1480      *           getter method or no descriptor associated with the
1481      *           ModelMBeanOperationInfo or the managed resource is
1482      *           null.</LI>
1483      *      <LI> {@link InvalidTargetObjectTypeException} The 'targetType'
1484      *           field value is not 'objectReference'.</LI>
1485      *      <LI> An Exception thrown by the managed object's getter.</LI>
1486      *    </UL>
1487      * @exception ReflectionException  Wraps an {@link java.lang.Exception}
1488      *    thrown while trying to invoke the getter.
1489      * @exception RuntimeOperationsException Wraps an
1490      *    {@link IllegalArgumentException}: The attribute name in
1491      *    parameter is null.
1492      *
1493      * @see #setAttribute(javax.management.Attribute)
1494      **/
1495     public Object getAttribute(String attrName)
1496         throws AttributeNotFoundException, MBeanException,
1497                ReflectionException {
1498         if (attrName == null)
1499             throw new RuntimeOperationsException(new
1500                 IllegalArgumentException("attributeName must not be null"),
1501                 "Exception occurred trying to get attribute of a " +
1502                 "RequiredModelMBean");
1503         final String mth = "getAttribute(String)";
1504         final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.FINER);
1505         if (tracing) {
1506             MODELMBEAN_LOGGER.logp(Level.FINER,
1507                     RequiredModelMBean.class.getName(),
1508                 mth, "Entry with " + attrName);
1509         }
1510 
1511         /* Check attributeDescriptor for getMethod */
1512         Object response;
1513 
1514         try {
1515             if (modelMBeanInfo == null)
1516                 throw new AttributeNotFoundException(
1517                       "getAttribute failed: ModelMBeanInfo not found for "+
1518                       attrName);
1519 
1520             ModelMBeanAttributeInfo attrInfo = modelMBeanInfo.getAttribute(attrName);
1521             Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor();
1522 
1523             if (attrInfo == null)
1524                 throw new AttributeNotFoundException("getAttribute failed:"+
1525                       " ModelMBeanAttributeInfo not found for " + attrName);
1526 
1527             Descriptor attrDescr = attrInfo.getDescriptor();
1528             if (attrDescr != null) {
1529                 if (!attrInfo.isReadable())
1530                     throw new AttributeNotFoundException(
1531                           "getAttribute failed: " + attrName +
1532                           " is not readable ");
1533 
1534                 response = resolveForCacheValue(attrDescr);
1535 
1536                 /* return current cached value */
1537                 if (tracing) {
1538                     MODELMBEAN_LOGGER.logp(Level.FINER,
1539                             RequiredModelMBean.class.getName(), mth,
1540                         "*** cached value is " + response);
1541                 }
1542 
1543                 if (response == null) {
1544                     /* no cached value, run getMethod */
1545                     if (tracing) {
1546                         MODELMBEAN_LOGGER.logp(Level.FINER,
1547                                 RequiredModelMBean.class.getName(), mth,
1548                             "**** cached value is null - getting getMethod");
1549                     }
1550                     String attrGetMethod =
1551                         (String)(attrDescr.getFieldValue("getMethod"));
1552 
1553                     if (attrGetMethod != null) {
1554                         /* run method from operations descriptor */
1555                         if (tracing) {
1556                             MODELMBEAN_LOGGER.logp(Level.FINER,
1557                                     RequiredModelMBean.class.getName(),
1558                                 mth, "invoking a getMethod for " +  attrName);
1559                         }
1560 
1561                         Object getResponse =
1562                             invoke(attrGetMethod, new Object[] {},
1563                                    new String[] {});
1564 
1565                         if (getResponse != null) {
1566                             // error/validity check return value here
1567                             if (tracing) {
1568                                 MODELMBEAN_LOGGER.logp(Level.FINER,
1569                                         RequiredModelMBean.class.getName(),
1570                                         mth, "got a non-null response " +
1571                                         "from getMethod\n");
1572                             }
1573 
1574                             response = getResponse;
1575 
1576                             // change cached value in attribute descriptor
1577                             Object objctl =
1578                                 attrDescr.getFieldValue("currencyTimeLimit");
1579 
1580                             String ctl;
1581                             if (objctl != null) ctl = objctl.toString();
1582                             else ctl = null;
1583 
1584                             if ((ctl == null) && (mmbDesc != null)) {
1585                                 objctl = mmbDesc.
1586                                     getFieldValue("currencyTimeLimit");
1587                                 if (objctl != null) ctl = objctl.toString();
1588                                 else ctl = null;
1589                             }
1590 
1591                             if ((ctl != null) && !(ctl.equals("-1"))) {
1592                                 if (tracing) {
1593                                     MODELMBEAN_LOGGER.logp(Level.FINER,
1594                                             RequiredModelMBean.class.getName(),
1595                                             mth,
1596                                             "setting cached value and " +
1597                                             "lastUpdatedTime in descriptor");
1598                                 }
1599                                 attrDescr.setField("value", response);
1600                                 final String stamp = String.valueOf(
1601                                     (new Date()).getTime());
1602                                 attrDescr.setField("lastUpdatedTimeStamp",
1603                                                    stamp);
1604                                 attrInfo.setDescriptor(attrDescr);
1605                                 modelMBeanInfo.setDescriptor(attrDescr,
1606                                                              "attribute");
1607                                 if (tracing) {
1608                                     MODELMBEAN_LOGGER.logp(Level.FINER,
1609                                             RequiredModelMBean.class.getName(),
1610                                         mth,"new descriptor is " +attrDescr);
1611                                     MODELMBEAN_LOGGER.logp(Level.FINER,
1612                                             RequiredModelMBean.class.getName(),
1613                                         mth,"AttributeInfo descriptor is " +
1614                                             attrInfo.getDescriptor());
1615                                     final String attStr = modelMBeanInfo.
1616                                         getDescriptor(attrName,"attribute").
1617                                             toString();
1618                                     MODELMBEAN_LOGGER.logp(Level.FINER,
1619                                             RequiredModelMBean.class.getName(),
1620                                             mth,
1621                                             "modelMBeanInfo: AttributeInfo " +
1622                                             "descriptor is " + attStr);
1623                                 }
1624                             }
1625                         } else {
1626                             // response was invalid or really returned null
1627                             if (tracing) {
1628                                 MODELMBEAN_LOGGER.logp(Level.FINER,
1629                                         RequiredModelMBean.class.getName(), mth,
1630                                     "got a null response from getMethod\n");
1631                             }
1632                             response = null;
1633                         }
1634                     } else {
1635                         // not getMethod so return descriptor (default) value
1636                         String qualifier="";
1637                         response = attrDescr.getFieldValue("value");
1638                         if (response == null) {
1639                             qualifier="default ";
1640                             response = attrDescr.getFieldValue("default");
1641                         }
1642                         if (tracing) {
1643                             MODELMBEAN_LOGGER.logp(Level.FINER,
1644                                     RequiredModelMBean.class.getName(), mth,
1645                                 "could not find getMethod for " +attrName +
1646                                 ", returning descriptor " +qualifier + "value");
1647                         }
1648                         // !! cast response to right class
1649                     }
1650                 }
1651 
1652                 // make sure response class matches type field
1653                 final String respType = attrInfo.getType();
1654                 if (response != null) {
1655                     String responseClass = response.getClass().getName();
1656                     if (!respType.equals(responseClass)) {
1657                         boolean wrongType = false;
1658                         boolean primitiveType = false;
1659                         boolean correspondingTypes = false;
1660                         for (int i = 0; i < primitiveTypes.length; i++) {
1661                             if (respType.equals(primitiveTypes[i])) {
1662                                 primitiveType = true;
1663                                 if (responseClass.equals(primitiveWrappers[i]))
1664                                     correspondingTypes = true;
1665                                 break;
1666                             }
1667                         }
1668                         if (primitiveType) {
1669                             // inequality may come from primitive/wrapper class
1670                             if (!correspondingTypes)
1671                                 wrongType = true;
1672                         } else {
1673                             // inequality may come from type subclassing
1674                             boolean subtype;
1675                             try {
1676                                 final Class<?> respClass = response.getClass();
1677                                 final Exception[] caughException = new Exception[1];
1678 
1679                                 AccessControlContext stack = AccessController.getContext();
1680 
1681                                 Class<?> c = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Class<?>>() {
1682 
1683                                     @Override
1684                                     public Class<?> run() {
1685                                         try {
1686                                             ReflectUtil.checkPackageAccess(respType);
1687                                             ClassLoader cl =
1688                                                 respClass.getClassLoader();
1689                                             return Class.forName(respType, true, cl);
1690                                         } catch (Exception e) {
1691                                             caughException[0] = e;
1692                                         }
1693                                         return null;
1694                                     }
1695                                 }, stack, acc);
1696 
1697                                 if (caughException[0] != null) {
1698                                     throw caughException[0];
1699                                 }
1700 
1701                                 subtype = c.isInstance(response);
1702                             } catch (Exception e) {
1703                                 subtype = false;
1704 
1705                                 if (tracing) {
1706                                     MODELMBEAN_LOGGER.logp(Level.FINER,
1707                                             RequiredModelMBean.class.getName(),
1708                                         mth, "Exception: ",e);
1709                                 }
1710                             }
1711                             if (!subtype)
1712                                 wrongType = true;
1713                         }
1714                         if (wrongType) {
1715                             if (tracing) {
1716                                 MODELMBEAN_LOGGER.logp(Level.FINER,
1717                                         RequiredModelMBean.class.getName(), mth,
1718                                     "Wrong response type '" + respType + "'");
1719                             }
1720                             // throw exception, didn't get
1721                             // back right attribute type
1722                             throw new MBeanException(
1723                               new InvalidAttributeValueException(
1724                                 "Wrong value type received for get attribute"),
1725                               "An exception occurred while trying to get an " +
1726                               "attribute value through a RequiredModelMBean");
1727                         }
1728                     }
1729                 }
1730             } else {
1731                 if (tracing) {
1732                     MODELMBEAN_LOGGER.logp(Level.FINER,
1733                             RequiredModelMBean.class.getName(), mth,
1734                             "getMethod failed " + attrName +
1735                             " not in attributeDescriptor\n");
1736                 }
1737                 throw new MBeanException(new
1738                     InvalidAttributeValueException(
1739                     "Unable to resolve attribute value, " +
1740                     "no getMethod defined in descriptor for attribute"),
1741                     "An exception occurred while trying to get an "+
1742                     "attribute value through a RequiredModelMBean");
1743             }
1744 
1745         } catch (MBeanException mbe) {
1746             throw mbe;
1747         } catch (AttributeNotFoundException t) {
1748             throw t;
1749         } catch (Exception e) {
1750             if (tracing) {
1751                 MODELMBEAN_LOGGER.logp(Level.FINER,
1752                         RequiredModelMBean.class.getName(), mth,
1753                         "getMethod failed with " + e.getMessage() +
1754                         " exception type " + (e.getClass()).toString());
1755             }
1756             throw new MBeanException(e,"An exception occurred while trying "+
1757                       "to get an attribute value: " + e.getMessage());
1758         }
1759 
1760         if (tracing) {
1761             MODELMBEAN_LOGGER.logp(Level.FINER,
1762                     RequiredModelMBean.class.getName(), mth, "Exit");
1763         }
1764 
1765         return response;
1766     }
1767 
1768     /**
1769      * Returns the values of several attributes in the ModelMBean.
1770      * Executes a getAttribute for each attribute name in the
1771      * attrNames array passed in.
1772      *
1773      * @param attrNames A String array of names of the attributes
1774      * to be retrieved.
1775      *
1776      * @return The array of the retrieved attributes.
1777      *
1778      * @exception RuntimeOperationsException Wraps an
1779      * {@link IllegalArgumentException}: The object name in parameter is
1780      * null or attributes in parameter is null.
1781      *
1782      * @see #setAttributes(javax.management.AttributeList)
1783      */
1784     public AttributeList getAttributes(String[] attrNames)      {
1785         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
1786             MODELMBEAN_LOGGER.logp(Level.FINER,
1787                     RequiredModelMBean.class.getName(),
1788             "getAttributes(String[])","Entry");
1789         }
1790 
1791         if (attrNames == null)
1792             throw new RuntimeOperationsException(new
1793                 IllegalArgumentException("attributeNames must not be null"),
1794                 "Exception occurred trying to get attributes of a "+
1795                 "RequiredModelMBean");
1796 
1797         AttributeList responseList = new AttributeList();
1798         for (int i = 0; i < attrNames.length; i++) {
1799             try {
1800                 responseList.add(new Attribute(attrNames[i],
1801                                      getAttribute(attrNames[i])));
1802             } catch (Exception e) {
1803                 // eat exceptions because interface doesn't have an
1804                 // exception on it
1805                 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
1806                     MODELMBEAN_LOGGER.logp(Level.FINER,
1807                             RequiredModelMBean.class.getName(),
1808                         "getAttributes(String[])",
1809                             "Failed to get \"" + attrNames[i] + "\": ", e);
1810                 }
1811             }
1812         }
1813 
1814         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
1815             MODELMBEAN_LOGGER.logp(Level.FINER,
1816                 RequiredModelMBean.class.getName(),
1817                     "getAttributes(String[])","Exit");
1818         }
1819 
1820         return responseList;
1821     }
1822 
1823     /**
1824      * Sets the value of a specific attribute of a named ModelMBean.
1825      *
1826      * If the 'setMethod' field of the attribute's descriptor
1827      * contains the name of a valid operation descriptor, then the
1828      * method described by the operation descriptor is executed.
1829      * In this implementation, the operation descriptor must be specified
1830      * correctly and assigned to the modelMBeanInfo so that the 'setMethod'
1831      * works correctly.
1832      * The response from the method is set as the value of the attribute
1833      * in the descriptor.
1834      *
1835      * <p>If currencyTimeLimit is &gt; 0, then the new value for the
1836      * attribute is cached in the attribute descriptor's
1837      * 'value' field and the 'lastUpdatedTimeStamp' field is set to
1838      * the current time stamp.
1839      *
1840      * <p>If the persist field of the attribute's descriptor is not null
1841      * then Persistence policy from the attribute descriptor is used to
1842      * guide storing the attribute in a persistent store.
1843      * <br>Store the MBean if 'persistPolicy' field is:
1844      * <UL>
1845      * <Li> != "never"</Li>
1846      * <Li> = "always"</Li>
1847      * <Li> = "onUpdate"</Li>
1848      * <Li> {@literal = "onTimer" and now > 'lastPersistTime' + 'persistPeriod'}</Li>
1849      * <Li> {@literal = "NoMoreOftenThan" and now > 'lastPersistTime' +
1850      *         'persistPeriod'}</Li>
1851      * </UL>
1852      * Do not store the MBean if 'persistPolicy' field is:
1853      * <UL>
1854      * <Li> = "never"</Li>
1855      * <Li> = {@literal = "onTimer" && now < 'lastPersistTime' + 'persistPeriod'}</Li>
1856      * <Li> = "onUnregister"</Li>
1857      * <Li> = {@literal = "NoMoreOftenThan" and now < 'lastPersistTime' +
1858      *        'persistPeriod'}</Li>
1859      * </UL>
1860      *
1861      * <p>The ModelMBeanInfo of the Model MBean is stored in a file.
1862      *
1863      * @param attribute The Attribute instance containing the name of
1864      *        the attribute to be set and the value it is to be set to.
1865      *
1866      *
1867      * @exception AttributeNotFoundException The specified attribute is
1868      *   not accessible in the MBean.
1869      *   <br>The following cases may result in an AttributeNotFoundException:
1870      *   <UL>
1871      *     <LI> No ModelMBeanAttributeInfo is found for the specified
1872      *          attribute.</LI>
1873      *     <LI> The ModelMBeanAttributeInfo's isWritable method returns
1874      *          'false'.</LI>
1875      *   </UL>
1876      * @exception InvalidAttributeValueException No descriptor is defined
1877      *   for the specified attribute.
1878      * @exception MBeanException Wraps one of the following Exceptions:
1879      *   <UL>
1880      *     <LI> An Exception thrown by the managed object's setter.</LI>
1881      *     <LI> A {@link ServiceNotFoundException} if a setMethod field is
1882      *          defined in the descriptor for the attribute and the managed
1883      *          resource is null; or if no setMethod field is defined and
1884      *          caching is not enabled for the attribute.
1885      *          Note that if there is no getMethod field either, then caching
1886      *          is automatically enabled.</LI>
1887      *     <LI> {@link InvalidTargetObjectTypeException} The 'targetType'
1888      *          field value is not 'objectReference'.</LI>
1889      *     <LI> An Exception thrown by the managed object's getter.</LI>
1890      *   </UL>
1891      * @exception ReflectionException  Wraps an {@link java.lang.Exception}
1892      *   thrown while trying to invoke the setter.
1893      * @exception RuntimeOperationsException Wraps an
1894      *   {@link IllegalArgumentException}: The attribute in parameter is
1895      *   null.
1896      *
1897      * @see #getAttribute(java.lang.String)
1898      **/
1899     public void setAttribute(Attribute attribute)
1900         throws AttributeNotFoundException, InvalidAttributeValueException,
1901                MBeanException, ReflectionException {
1902         final boolean tracing = MODELMBEAN_LOGGER.isLoggable(Level.FINER);
1903         if (tracing) {
1904             MODELMBEAN_LOGGER.logp(Level.FINER,
1905                     RequiredModelMBean.class.getName(),
1906                 "setAttribute()","Entry");
1907         }
1908 
1909         if (attribute == null)
1910             throw new RuntimeOperationsException(new
1911                 IllegalArgumentException("attribute must not be null"),
1912                 "Exception occurred trying to set an attribute of a "+
1913                 "RequiredModelMBean");
1914 
1915         /* run setMethod if there is one */
1916         /* return cached value if its current */
1917         /* set cached value in descriptor and set date/time */
1918         /* send attribute change Notification */
1919         /* check persistence policy and persist if need be */
1920         String attrName = attribute.getName();
1921         Object attrValue = attribute.getValue();
1922         boolean updateDescriptor = false;
1923 
1924         ModelMBeanAttributeInfo attrInfo =
1925             modelMBeanInfo.getAttribute(attrName);
1926 
1927         if (attrInfo == null)
1928             throw new AttributeNotFoundException("setAttribute failed: " +
1929                                                attrName + " is not found ");
1930 
1931         Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor();
1932         Descriptor attrDescr = attrInfo.getDescriptor();
1933 
1934         if (attrDescr != null) {
1935             if (!attrInfo.isWritable())
1936                 throw new AttributeNotFoundException("setAttribute failed: "
1937                                           + attrName + " is not writable ");
1938 
1939             String attrSetMethod = (String)
1940                 (attrDescr.getFieldValue("setMethod"));
1941             String attrGetMethod = (String)
1942                 (attrDescr.getFieldValue("getMethod"));
1943 
1944             String attrType = attrInfo.getType();
1945             Object currValue = "Unknown";
1946 
1947             try {
1948                 currValue = this.getAttribute(attrName);
1949             } catch (Throwable t) {
1950                 // OK: Default "Unknown" value used for unknown attribute
1951             }
1952 
1953             Attribute oldAttr = new Attribute(attrName, currValue);
1954 
1955             /* run method from operations descriptor */
1956             if (attrSetMethod == null) {
1957                 if (attrValue != null) {
1958                     try {
1959                         final Class<?> clazz = loadClass(attrType);
1960                         if (! clazz.isInstance(attrValue))  throw new
1961                             InvalidAttributeValueException(clazz.getName() +
1962                                                            " expected, "   +
1963                                             attrValue.getClass().getName() +
1964                                                            " received.");
1965                     } catch (ClassNotFoundException x) {
1966                         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
1967                             MODELMBEAN_LOGGER.logp(Level.FINER,
1968                                     RequiredModelMBean.class.getName(),
1969                                 "setAttribute(Attribute)","Class " +
1970                                     attrType + " for attribute "
1971                                 + attrName + " not found: ", x);
1972                         }
1973                     }
1974                 }
1975                 updateDescriptor = true;
1976             } else {
1977                 invoke(attrSetMethod,
1978                        (new Object[] {attrValue}),
1979                        (new String[] {attrType}) );
1980             }
1981 
1982             /* change cached value */
1983             Object objctl = attrDescr.getFieldValue("currencyTimeLimit");
1984             String ctl;
1985             if (objctl != null) ctl = objctl.toString();
1986             else ctl = null;
1987 
1988             if ((ctl == null) && (mmbDesc != null)) {
1989                 objctl = mmbDesc.getFieldValue("currencyTimeLimit");
1990                 if (objctl != null) ctl = objctl.toString();
1991                 else ctl = null;
1992             }
1993 
1994             final boolean updateCache = ((ctl != null) && !(ctl.equals("-1")));
1995 
1996              if(attrSetMethod == null  && !updateCache && attrGetMethod != null)
1997                 throw new MBeanException(new ServiceNotFoundException("No " +
1998                         "setMethod field is defined in the descriptor for " +
1999                         attrName + " attribute and caching is not enabled " +
2000                         "for it"));
2001 
2002             if (updateCache || updateDescriptor) {
2003                 if (tracing) {
2004                     MODELMBEAN_LOGGER.logp(Level.FINER,
2005                         RequiredModelMBean.class.getName(),
2006                             "setAttribute(Attribute)",
2007                             "setting cached value of " +
2008                             attrName + " to " + attrValue);
2009                 }
2010 
2011                 attrDescr.setField("value", attrValue);
2012 
2013                 if (updateCache) {
2014                     final String currtime = String.valueOf(
2015                         (new Date()).getTime());
2016 
2017                     attrDescr.setField("lastUpdatedTimeStamp", currtime);
2018                 }
2019 
2020                 attrInfo.setDescriptor(attrDescr);
2021 
2022                 modelMBeanInfo.setDescriptor(attrDescr,"attribute");
2023                 if (tracing) {
2024                     final StringBuilder strb = new StringBuilder()
2025                     .append("new descriptor is ").append(attrDescr)
2026                     .append(". AttributeInfo descriptor is ")
2027                     .append(attrInfo.getDescriptor())
2028                     .append(". AttributeInfo descriptor is ")
2029                     .append(modelMBeanInfo.getDescriptor(attrName,"attribute"));
2030                     MODELMBEAN_LOGGER.logp(Level.FINER,
2031                             RequiredModelMBean.class.getName(),
2032                             "setAttribute(Attribute)",strb.toString());
2033                 }
2034 
2035             }
2036 
2037             if (tracing) {
2038                 MODELMBEAN_LOGGER.logp(Level.FINER,
2039                         RequiredModelMBean.class.getName(),
2040                 "setAttribute(Attribute)","sending sendAttributeNotification");
2041             }
2042             sendAttributeChangeNotification(oldAttr,attribute);
2043 
2044         } else { // if descriptor ... else no descriptor
2045 
2046             if (tracing) {
2047                     MODELMBEAN_LOGGER.logp(Level.FINER,
2048                             RequiredModelMBean.class.getName(),
2049                         "setAttribute(Attribute)","setMethod failed "+attrName+
2050                         " not in attributeDescriptor\n");
2051             }
2052 
2053             throw new InvalidAttributeValueException(
2054                       "Unable to resolve attribute value, "+
2055                       "no defined in descriptor for attribute");
2056         } // else no descriptor
2057 
2058         if (tracing) {
2059             MODELMBEAN_LOGGER.logp(Level.FINER,
2060                     RequiredModelMBean.class.getName(),
2061                 "setAttribute(Attribute)", "Exit");
2062         }
2063 
2064     }
2065 
2066     /**
2067      * Sets the values of an array of attributes of this ModelMBean.
2068      * Executes the setAttribute() method for each attribute in the list.
2069      *
2070      * @param attributes A list of attributes: The identification of the
2071      * attributes to be set and  the values they are to be set to.
2072      *
2073      * @return  The array of attributes that were set, with their new
2074      *    values in Attribute instances.
2075      *
2076      * @exception RuntimeOperationsException Wraps an
2077      *   {@link IllegalArgumentException}: The object name in parameter
2078      *   is null or attributes in parameter is null.
2079      *
2080      * @see #getAttributes
2081      **/
2082     public AttributeList setAttributes(AttributeList attributes) {
2083 
2084         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2085             MODELMBEAN_LOGGER.logp(Level.FINER,
2086                     RequiredModelMBean.class.getName(),
2087                 "setAttribute(Attribute)", "Entry");
2088         }
2089 
2090         if (attributes == null)
2091             throw new RuntimeOperationsException(new
2092                 IllegalArgumentException("attributes must not be null"),
2093                 "Exception occurred trying to set attributes of a "+
2094                 "RequiredModelMBean");
2095 
2096         final AttributeList responseList = new AttributeList();
2097 
2098         // Go through the list of attributes
2099         for (Attribute attr : attributes.asList()) {
2100             try {
2101                 setAttribute(attr);
2102                 responseList.add(attr);
2103             } catch (Exception excep) {
2104                 responseList.remove(attr);
2105             }
2106         }
2107 
2108         return responseList;
2109     }
2110 
2111 
2112 
2113     private ModelMBeanInfo createDefaultModelMBeanInfo() {
2114         return(new ModelMBeanInfoSupport((this.getClass().getName()),
2115                    "Default ModelMBean", null, null, null, null));
2116     }
2117 
2118     /*************************************/
2119     /* NotificationBroadcaster Interface */
2120     /*************************************/
2121 
2122 
2123     private synchronized void writeToLog(String logFileName,
2124                                          String logEntry) throws Exception {
2125 
2126         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2127             MODELMBEAN_LOGGER.logp(Level.FINER,
2128                     RequiredModelMBean.class.getName(),
2129                 "writeToLog(String, String)",
2130                 "Notification Logging to " + logFileName + ": " + logEntry);
2131         }
2132         if ((logFileName == null) || (logEntry == null)) {
2133             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2134                 MODELMBEAN_LOGGER.logp(Level.FINER,
2135                         RequiredModelMBean.class.getName(),
2136                     "writeToLog(String, String)",
2137                     "Bad input parameters, will not log this entry.");
2138             }
2139             return;
2140         }
2141 
2142         FileOutputStream fos = new FileOutputStream(logFileName, true);
2143         try {
2144             PrintStream logOut = new PrintStream(fos);
2145             logOut.println(logEntry);
2146             logOut.close();
2147             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2148                 MODELMBEAN_LOGGER.logp(Level.FINER,
2149                         RequiredModelMBean.class.getName(),
2150                     "writeToLog(String, String)","Successfully opened log " +
2151                         logFileName);
2152             }
2153         } catch (Exception e) {
2154             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2155                 MODELMBEAN_LOGGER.logp(Level.FINER,
2156                     RequiredModelMBean.class.getName(),
2157                         "writeToLog(String, String)",
2158                         "Exception " + e.toString() +
2159                         " trying to write to the Notification log file " +
2160                         logFileName);
2161             }
2162             throw e;
2163         } finally {
2164             fos.close();
2165         }
2166     }
2167 
2168 
2169     /**
2170      * Registers an object which implements the NotificationListener
2171      * interface as a listener.  This
2172      * object's 'handleNotification()' method will be invoked when any
2173      * notification is issued through or by the ModelMBean.  This does
2174      * not include attributeChangeNotifications.  They must be registered
2175      * for independently.
2176      *
2177      * @param listener The listener object which will handles
2178      *        notifications emitted by the registered MBean.
2179      * @param filter The filter object. If null, no filtering will be
2180      *        performed before handling notifications.
2181      * @param handback The context to be sent to the listener with
2182      *        the notification when a notification is emitted.
2183      *
2184      * @exception IllegalArgumentException The listener cannot be null.
2185      *
2186      * @see #removeNotificationListener
2187      */
2188     public void addNotificationListener(NotificationListener listener,
2189                                         NotificationFilter filter,
2190                                         Object handback)
2191         throws java.lang.IllegalArgumentException {
2192         final String mth = "addNotificationListener(" +
2193                 "NotificationListener, NotificationFilter, Object)";
2194         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2195                 MODELMBEAN_LOGGER.logp(Level.FINER,
2196                         RequiredModelMBean.class.getName(), mth, "Entry");
2197         }
2198 
2199         if (listener == null)
2200             throw new IllegalArgumentException(
2201                   "notification listener must not be null");
2202 
2203         if (generalBroadcaster == null)
2204             generalBroadcaster = new NotificationBroadcasterSupport();
2205 
2206         generalBroadcaster.addNotificationListener(listener, filter,
2207                                                    handback);
2208         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2209                 MODELMBEAN_LOGGER.logp(Level.FINER,
2210                         RequiredModelMBean.class.getName(), mth,
2211                     "NotificationListener added");
2212                 MODELMBEAN_LOGGER.logp(Level.FINER,
2213                         RequiredModelMBean.class.getName(), mth, "Exit");
2214         }
2215     }
2216 
2217     /**
2218      * Removes a listener for Notifications from the RequiredModelMBean.
2219      *
2220      * @param listener The listener name which was handling notifications
2221      *    emitted by the registered MBean.
2222      *    This method will remove all information related to this listener.
2223      *
2224      * @exception ListenerNotFoundException The listener is not registered
2225      *    in the MBean or is null.
2226      *
2227      * @see #addNotificationListener
2228      **/
2229     public void removeNotificationListener(NotificationListener listener)
2230         throws ListenerNotFoundException {
2231         if (listener == null)
2232             throw new ListenerNotFoundException(
2233                       "Notification listener is null");
2234 
2235         final String mth="removeNotificationListener(NotificationListener)";
2236         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2237                 MODELMBEAN_LOGGER.logp(Level.FINER,
2238                     RequiredModelMBean.class.getName(), mth, "Entry");
2239         }
2240 
2241         if (generalBroadcaster == null)
2242             throw new ListenerNotFoundException(
2243                   "No notification listeners registered");
2244 
2245 
2246         generalBroadcaster.removeNotificationListener(listener);
2247         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2248             MODELMBEAN_LOGGER.logp(Level.FINER,
2249                     RequiredModelMBean.class.getName(), mth, "Exit");
2250         }
2251 
2252     }
2253 
2254     public void removeNotificationListener(NotificationListener listener,
2255                                            NotificationFilter filter,
2256                                            Object handback)
2257         throws ListenerNotFoundException {
2258 
2259         if (listener == null)
2260             throw new ListenerNotFoundException(
2261                       "Notification listener is null");
2262 
2263         final String mth = "removeNotificationListener(" +
2264                 "NotificationListener, NotificationFilter, Object)";
2265 
2266         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2267             MODELMBEAN_LOGGER.logp(Level.FINER,
2268                     RequiredModelMBean.class.getName(), mth, "Entry");
2269         }
2270 
2271         if (generalBroadcaster == null)
2272             throw new ListenerNotFoundException(
2273                   "No notification listeners registered");
2274 
2275 
2276         generalBroadcaster.removeNotificationListener(listener,filter,
2277                                                       handback);
2278 
2279         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2280             MODELMBEAN_LOGGER.logp(Level.FINER,
2281                     RequiredModelMBean.class.getName(), mth, "Exit");
2282         }
2283 
2284     }
2285 
2286     public void sendNotification(Notification ntfyObj)
2287         throws MBeanException, RuntimeOperationsException {
2288         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2289             MODELMBEAN_LOGGER.logp(Level.FINER,
2290                     RequiredModelMBean.class.getName(),
2291                 "sendNotification(Notification)", "Entry");
2292         }
2293 
2294         if (ntfyObj == null)
2295             throw new RuntimeOperationsException(new
2296                 IllegalArgumentException("notification object must not be "+
2297                                          "null"),
2298                 "Exception occurred trying to send a notification from a "+
2299                 "RequiredModelMBean");
2300 
2301 
2302         // log notification if specified in descriptor
2303         Descriptor ntfyDesc =
2304             modelMBeanInfo.getDescriptor(ntfyObj.getType(),"notification");
2305         Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor();
2306 
2307         if (ntfyDesc != null) {
2308             String logging = (String) ntfyDesc.getFieldValue("log");
2309 
2310             if (logging == null) {
2311                 if (mmbDesc != null)
2312                     logging = (String) mmbDesc.getFieldValue("log");
2313             }
2314 
2315             if ((logging != null) &&
2316                 (logging.equalsIgnoreCase("t") ||
2317                  logging.equalsIgnoreCase("true"))) {
2318 
2319                 String logfile = (String) ntfyDesc.getFieldValue("logfile");
2320                 if (logfile == null) {
2321                     if (mmbDesc != null)
2322                         logfile = (String)mmbDesc.getFieldValue("logfile");
2323                 }
2324                 if (logfile != null) {
2325                     try {
2326                         writeToLog(logfile,"LogMsg: " +
2327                             ((new Date(ntfyObj.getTimeStamp())).toString())+
2328                             " " + ntfyObj.getType() + " " +
2329                             ntfyObj.getMessage() + " Severity = " +
2330                             (String)ntfyDesc.getFieldValue("severity"));
2331                     } catch (Exception e) {
2332                         if (MODELMBEAN_LOGGER.isLoggable(Level.FINE)) {
2333                             MODELMBEAN_LOGGER.logp(Level.FINE,
2334                                     RequiredModelMBean.class.getName(),
2335                                     "sendNotification(Notification)",
2336                                     "Failed to log " +
2337                                     ntfyObj.getType() + " notification: ", e);
2338                         }
2339                     }
2340                 }
2341             }
2342         }
2343         if (generalBroadcaster != null) {
2344             generalBroadcaster.sendNotification(ntfyObj);
2345         }
2346 
2347         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2348             MODELMBEAN_LOGGER.logp(Level.FINER,
2349                 RequiredModelMBean.class.getName(),
2350                     "sendNotification(Notification)",
2351                     "sendNotification sent provided notification object");
2352             MODELMBEAN_LOGGER.logp(Level.FINER,
2353                 RequiredModelMBean.class.getName(),
2354                     "sendNotification(Notification)"," Exit");
2355         }
2356 
2357     }
2358 
2359 
2360     public void sendNotification(String ntfyText)
2361         throws MBeanException, RuntimeOperationsException {
2362         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2363             MODELMBEAN_LOGGER.logp(Level.FINER,
2364                     RequiredModelMBean.class.getName(),
2365                 "sendNotification(String)","Entry");
2366         }
2367 
2368         if (ntfyText == null)
2369             throw new RuntimeOperationsException(new
2370                 IllegalArgumentException("notification message must not "+
2371                                          "be null"),
2372                 "Exception occurred trying to send a text notification "+
2373                 "from a ModelMBean");
2374 
2375         Notification myNtfyObj = new Notification("jmx.modelmbean.generic",
2376                                                   this, 1, ntfyText);
2377         sendNotification(myNtfyObj);
2378         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2379             MODELMBEAN_LOGGER.logp(Level.FINER,
2380                     RequiredModelMBean.class.getName(),
2381                 "sendNotification(String)","Notification sent");
2382             MODELMBEAN_LOGGER.logp(Level.FINER,
2383                     RequiredModelMBean.class.getName(),
2384                 "sendNotification(String)","Exit");
2385         }
2386     }
2387 
2388     /**
2389      * Returns `true' if the notification `notifName' is found
2390      * in `info'. (bug 4744667)
2391      **/
2392     private static final
2393         boolean hasNotification(final ModelMBeanInfo info,
2394                                 final String notifName) {
2395         try {
2396             if (info == null) return false;
2397             else return (info.getNotification(notifName)!=null);
2398         } catch (MBeanException x) {
2399             return false;
2400         } catch (RuntimeOperationsException r) {
2401             return false;
2402         }
2403     }
2404 
2405     /**
2406      * Creates a default ModelMBeanNotificationInfo for GENERIC
2407      * notification.  (bug 4744667)
2408      **/
2409     private static final ModelMBeanNotificationInfo makeGenericInfo() {
2410         final Descriptor genericDescriptor = new DescriptorSupport( new
2411             String[] {
2412                 "name=GENERIC",
2413                 "descriptorType=notification",
2414                 "log=T",
2415                 "severity=6",
2416                 "displayName=jmx.modelmbean.generic"} );
2417 
2418         return new ModelMBeanNotificationInfo(new
2419             String[] {"jmx.modelmbean.generic"},
2420             "GENERIC",
2421             "A text notification has been issued by the managed resource",
2422             genericDescriptor);
2423     }
2424 
2425     /**
2426      * Creates a default ModelMBeanNotificationInfo for ATTRIBUTE_CHANGE
2427      * notification.  (bug 4744667)
2428      **/
2429     private static final
2430         ModelMBeanNotificationInfo makeAttributeChangeInfo() {
2431         final Descriptor attributeDescriptor = new DescriptorSupport(new
2432             String[] {
2433                 "name=ATTRIBUTE_CHANGE",
2434                 "descriptorType=notification",
2435                 "log=T",
2436                 "severity=6",
2437                 "displayName=jmx.attribute.change"});
2438 
2439         return new ModelMBeanNotificationInfo(new
2440             String[] {"jmx.attribute.change"},
2441             "ATTRIBUTE_CHANGE",
2442             "Signifies that an observed MBean attribute value has changed",
2443             attributeDescriptor );
2444     }
2445 
2446     /**
2447      * Returns the array of Notifications always generated by the
2448      * RequiredModelMBean.
2449      * <P>
2450      *
2451      * RequiredModelMBean may always send also two additional notifications:
2452      * <UL>
2453      *   <LI> One with descriptor <code>"name=GENERIC,descriptorType=notification,log=T,severity=6,displayName=jmx.modelmbean.generic"</code></LI>
2454      *   <LI> Second is a standard attribute change notification
2455      *        with descriptor <code>"name=ATTRIBUTE_CHANGE,descriptorType=notification,log=T,severity=6,displayName=jmx.attribute.change"</code></LI>
2456      * </UL>
2457      * Thus these two notifications are always added to those specified
2458      * by the application.
2459      *
2460      * @return MBeanNotificationInfo[]
2461      *
2462      **/
2463     public MBeanNotificationInfo[] getNotificationInfo() {
2464         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2465             MODELMBEAN_LOGGER.logp(Level.FINER,
2466                     RequiredModelMBean.class.getName(),
2467                 "getNotificationInfo()","Entry");
2468         }
2469 
2470         // Using hasNotification() is not optimal, but shouldn't really
2471         // matter in this context...
2472 
2473         // hasGeneric==true if GENERIC notification is present.
2474         // (bug 4744667)
2475         final boolean hasGeneric = hasNotification(modelMBeanInfo,"GENERIC");
2476 
2477         // hasAttributeChange==true if ATTRIBUTE_CHANGE notification is
2478         // present.
2479         // (bug 4744667)
2480         final boolean hasAttributeChange =
2481            hasNotification(modelMBeanInfo,"ATTRIBUTE_CHANGE");
2482 
2483         // User supplied list of notification infos.
2484         //
2485         final ModelMBeanNotificationInfo[] currInfo =
2486            (ModelMBeanNotificationInfo[])modelMBeanInfo.getNotifications();
2487 
2488         // Length of the returned list of notification infos:
2489         //    length of user suplied list + possibly 1 for GENERIC, +
2490         //    possibly 1 for ATTRIBUTE_CHANGE
2491         //    (bug 4744667)
2492         final int len = ((currInfo==null?0:currInfo.length) +
2493                          (hasGeneric?0:1) + (hasAttributeChange?0:1));
2494 
2495         // Returned list of notification infos:
2496         //
2497         final ModelMBeanNotificationInfo[] respInfo =
2498            new ModelMBeanNotificationInfo[len];
2499 
2500         // Preserve previous ordering (JMX 1.1)
2501         //
2502 
2503         // Counter of "standard" notification inserted before user
2504         // supplied notifications.
2505         //
2506         int inserted=0;
2507         if (!hasGeneric)
2508             // We need to add description for GENERIC notification
2509             // (bug 4744667)
2510             respInfo[inserted++] = makeGenericInfo();
2511 
2512 
2513         if (!hasAttributeChange)
2514             // We need to add description for ATTRIBUTE_CHANGE notification
2515             // (bug 4744667)
2516             respInfo[inserted++] = makeAttributeChangeInfo();
2517 
2518         // Now copy user supplied list in returned list.
2519         //
2520         final int count  = currInfo.length;
2521         final int offset = inserted;
2522         for (int j=0; j < count; j++) {
2523             respInfo[offset+j] = currInfo[j];
2524         }
2525 
2526         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2527             MODELMBEAN_LOGGER.logp(Level.FINER,
2528                     RequiredModelMBean.class.getName(),
2529                 "getNotificationInfo()","Exit");
2530         }
2531 
2532         return respInfo;
2533     }
2534 
2535 
2536     public void addAttributeChangeNotificationListener(NotificationListener
2537                                                        inlistener,
2538                                                        String
2539                                                        inAttributeName,
2540                                                        Object inhandback)
2541         throws MBeanException, RuntimeOperationsException,
2542                IllegalArgumentException {
2543         final String mth="addAttributeChangeNotificationListener(" +
2544                 "NotificationListener, String, Object)";
2545 
2546         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2547             MODELMBEAN_LOGGER.logp(Level.FINER,
2548                     RequiredModelMBean.class.getName(),mth,"Entry");
2549         }
2550 
2551         if (inlistener == null)
2552             throw new IllegalArgumentException(
2553                   "Listener to be registered must not be null");
2554 
2555 
2556         if (attributeBroadcaster == null)
2557             attributeBroadcaster = new NotificationBroadcasterSupport();
2558 
2559         AttributeChangeNotificationFilter currFilter =
2560             new AttributeChangeNotificationFilter();
2561 
2562         MBeanAttributeInfo[] attrInfo = modelMBeanInfo.getAttributes();
2563         boolean found = false;
2564         if (inAttributeName == null) {
2565             if ((attrInfo != null) && (attrInfo.length>0)) {
2566                 for (int i=0; i<attrInfo.length; i++) {
2567                     currFilter.enableAttribute(attrInfo[i].getName());
2568                 }
2569             }
2570         } else {
2571             if ((attrInfo != null) && (attrInfo.length>0)) {
2572                 for (int i=0; i<attrInfo.length; i++) {
2573                     if (inAttributeName.equals(attrInfo[i].getName())) {
2574                         found = true;
2575                         currFilter.enableAttribute(inAttributeName);
2576                         break;
2577                     }
2578                 }
2579             }
2580             if (!found) {
2581                 throw new RuntimeOperationsException(new
2582                     IllegalArgumentException(
2583                     "The attribute name does not exist"),
2584                     "Exception occurred trying to add an "+
2585                     "AttributeChangeNotification listener");
2586             }
2587         }
2588 
2589         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2590             Vector<String> enabledAttrs = currFilter.getEnabledAttributes();
2591             String s = (enabledAttrs.size() > 1) ?
2592                         "[" + enabledAttrs.firstElement() + ", ...]" :
2593                         enabledAttrs.toString();
2594             MODELMBEAN_LOGGER.logp(Level.FINER,
2595                     RequiredModelMBean.class.getName(), mth,
2596                 "Set attribute change filter to " + s);
2597         }
2598 
2599         attributeBroadcaster.addNotificationListener(inlistener,currFilter,
2600                                                      inhandback);
2601         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2602             MODELMBEAN_LOGGER.logp(Level.FINER,
2603                     RequiredModelMBean.class.getName(),mth,
2604                     "Notification listener added for " + inAttributeName);
2605             MODELMBEAN_LOGGER.logp(Level.FINER,
2606                     RequiredModelMBean.class.getName(),mth,"Exit");
2607         }
2608     }
2609 
2610     public void removeAttributeChangeNotificationListener(
2611             NotificationListener inlistener, String inAttributeName)
2612         throws MBeanException, RuntimeOperationsException,
2613                ListenerNotFoundException {
2614         if (inlistener == null) throw new
2615             ListenerNotFoundException("Notification listener is null");
2616 
2617         final String mth = "removeAttributeChangeNotificationListener(" +
2618                 "NotificationListener, String)";
2619 
2620         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2621             MODELMBEAN_LOGGER.logp(Level.FINER,
2622                     RequiredModelMBean.class.getName(),mth,"Entry");
2623         }
2624 
2625 
2626         if (attributeBroadcaster == null)
2627             throw new ListenerNotFoundException(
2628                   "No attribute change notification listeners registered");
2629 
2630 
2631         MBeanAttributeInfo[] attrInfo = modelMBeanInfo.getAttributes();
2632         boolean found = false;
2633         if ((attrInfo != null) && (attrInfo.length>0)) {
2634             for (int i=0; i<attrInfo.length; i++) {
2635                 if (attrInfo[i].getName().equals(inAttributeName)) {
2636                     found = true;
2637                     break;
2638                 }
2639             }
2640         }
2641 
2642         if ((!found) && (inAttributeName != null)) {
2643             throw new RuntimeOperationsException(new
2644                 IllegalArgumentException("Invalid attribute name"),
2645                 "Exception occurred trying to remove "+
2646                 "attribute change notification listener");
2647         }
2648 
2649         /* note: */
2650         /* this may be a problem if the same listener is registered for
2651            multiple attributes with multiple filters and/or handback
2652            objects.  It may remove all of them */
2653 
2654         attributeBroadcaster.removeNotificationListener(inlistener);
2655 
2656         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2657             MODELMBEAN_LOGGER.logp(Level.FINER,
2658                     RequiredModelMBean.class.getName(),mth,"Exit");
2659         }
2660     }
2661 
2662     public void sendAttributeChangeNotification(AttributeChangeNotification
2663                                                 ntfyObj)
2664         throws MBeanException, RuntimeOperationsException {
2665         final String mth = "sendAttributeChangeNotification(" +
2666                 "AttributeChangeNotification)";
2667 
2668         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2669             MODELMBEAN_LOGGER.logp(Level.FINER,
2670                     RequiredModelMBean.class.getName(),mth,"Entry");
2671         }
2672 
2673         if (ntfyObj == null)
2674             throw new RuntimeOperationsException(new
2675                 IllegalArgumentException(
2676                 "attribute change notification object must not be null"),
2677                 "Exception occurred trying to send "+
2678                 "attribute change notification of a ModelMBean");
2679 
2680         Object oldv = ntfyObj.getOldValue();
2681         Object newv =  ntfyObj.getNewValue();
2682 
2683         if (oldv == null) oldv = "null";
2684         if (newv == null) newv = "null";
2685 
2686         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2687             MODELMBEAN_LOGGER.logp(Level.FINER,
2688                     RequiredModelMBean.class.getName(),mth,
2689                 "Sending AttributeChangeNotification with " +
2690                 ntfyObj.getAttributeName() + ntfyObj.getAttributeType() +
2691                 ntfyObj.getNewValue() + ntfyObj.getOldValue());
2692         }
2693 
2694         // log notification if specified in descriptor
2695         Descriptor ntfyDesc =
2696             modelMBeanInfo.getDescriptor(ntfyObj.getType(),"notification");
2697         Descriptor mmbDesc = modelMBeanInfo.getMBeanDescriptor();
2698 
2699         String logging, logfile;
2700 
2701         if (ntfyDesc != null) {
2702             logging =(String)  ntfyDesc.getFieldValue("log");
2703             if (logging == null) {
2704                 if (mmbDesc != null)
2705                     logging = (String) mmbDesc.getFieldValue("log");
2706             }
2707             if ((logging != null) &&
2708                 ( logging.equalsIgnoreCase("t") ||
2709                   logging.equalsIgnoreCase("true"))) {
2710                 logfile = (String) ntfyDesc.getFieldValue("logfile");
2711                 if (logfile == null) {
2712                     if (mmbDesc != null)
2713                         logfile = (String)mmbDesc.getFieldValue("logfile");
2714                 }
2715 
2716                 if (logfile != null) {
2717                     try {
2718                         writeToLog(logfile,"LogMsg: " +
2719                            ((new Date(ntfyObj.getTimeStamp())).toString())+
2720                            " " + ntfyObj.getType() + " " +
2721                            ntfyObj.getMessage() +
2722                            " Name = " + ntfyObj.getAttributeName() +
2723                            " Old value = " + oldv +
2724                            " New value = " + newv);
2725                     } catch (Exception e) {
2726                         if (MODELMBEAN_LOGGER.isLoggable(Level.FINE)) {
2727                             MODELMBEAN_LOGGER.logp(Level.FINE,
2728                                     RequiredModelMBean.class.getName(),mth,
2729                                 "Failed to log " + ntfyObj.getType() +
2730                                     " notification: ", e);
2731                         }
2732                     }
2733                 }
2734             }
2735         } else if (mmbDesc != null) {
2736             logging = (String) mmbDesc.getFieldValue("log");
2737             if ((logging != null) &&
2738                 ( logging.equalsIgnoreCase("t") ||
2739                   logging.equalsIgnoreCase("true") )) {
2740                 logfile = (String) mmbDesc.getFieldValue("logfile");
2741 
2742                 if (logfile != null) {
2743                     try {
2744                         writeToLog(logfile,"LogMsg: " +
2745                            ((new Date(ntfyObj.getTimeStamp())).toString())+
2746                            " " + ntfyObj.getType() + " " +
2747                            ntfyObj.getMessage() +
2748                            " Name = " + ntfyObj.getAttributeName() +
2749                            " Old value = " + oldv +
2750                            " New value = " + newv);
2751                     } catch (Exception e) {
2752                         if (MODELMBEAN_LOGGER.isLoggable(Level.FINE)) {
2753                             MODELMBEAN_LOGGER.logp(Level.FINE,
2754                                     RequiredModelMBean.class.getName(),mth,
2755                                 "Failed to log " + ntfyObj.getType() +
2756                                     " notification: ", e);
2757                         }
2758                     }
2759                 }
2760             }
2761         }
2762         if (attributeBroadcaster != null) {
2763             attributeBroadcaster.sendNotification(ntfyObj);
2764         }
2765 
2766         // XXX Revisit: This is a quickfix: it would be better to have a
2767         //     single broadcaster. However, it is not so simple because
2768         //     removeAttributeChangeNotificationListener() should
2769         //     remove only listeners whose filter is an instanceof
2770         //     AttributeChangeNotificationFilter.
2771         //
2772         if (generalBroadcaster != null) {
2773             generalBroadcaster.sendNotification(ntfyObj);
2774         }
2775 
2776         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2777             MODELMBEAN_LOGGER.logp(Level.FINER,
2778                     RequiredModelMBean.class.getName(),mth,
2779                 "sent notification");
2780             MODELMBEAN_LOGGER.logp(Level.FINER,
2781                     RequiredModelMBean.class.getName(),mth,
2782                 "Exit");
2783         }
2784     }
2785 
2786     public void sendAttributeChangeNotification(Attribute inOldVal,
2787                                                 Attribute inNewVal)
2788         throws MBeanException, RuntimeOperationsException {
2789         final String mth =
2790             "sendAttributeChangeNotification(Attribute, Attribute)";
2791         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2792             MODELMBEAN_LOGGER.logp(Level.FINER,
2793                     RequiredModelMBean.class.getName(),mth,
2794                 "Entry");
2795         }
2796 
2797         // do we really want to do this?
2798         if ((inOldVal == null) || (inNewVal == null))
2799             throw new RuntimeOperationsException(new
2800                IllegalArgumentException("Attribute object must not be null"),
2801                "Exception occurred trying to send " +
2802                "attribute change notification of a ModelMBean");
2803 
2804 
2805         if (!(inOldVal.getName().equals(inNewVal.getName())))
2806             throw new RuntimeOperationsException(new
2807                 IllegalArgumentException("Attribute names are not the same"),
2808                 "Exception occurred trying to send " +
2809                 "attribute change notification of a ModelMBean");
2810 
2811 
2812         Object newVal = inNewVal.getValue();
2813         Object oldVal = inOldVal.getValue();
2814         String className = "unknown";
2815         if (newVal != null)
2816             className = newVal.getClass().getName();
2817         if (oldVal != null)
2818             className = oldVal.getClass().getName();
2819 
2820         AttributeChangeNotification myNtfyObj = new
2821             AttributeChangeNotification(this,
2822                                         1,
2823                                         ((new Date()).getTime()),
2824                                         "AttributeChangeDetected",
2825                                         inOldVal.getName(),
2826                                         className,
2827                                         inOldVal.getValue(),
2828                                         inNewVal.getValue());
2829 
2830         sendAttributeChangeNotification(myNtfyObj);
2831 
2832         if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
2833             MODELMBEAN_LOGGER.logp(Level.FINER,
2834                     RequiredModelMBean.class.getName(),mth,
2835                 "Exit");
2836         }
2837 
2838     }
2839 
2840     /**
2841      * Return the Class Loader Repository used to perform class loading.
2842      * Subclasses may wish to redefine this method in order to return
2843      * the appropriate {@link javax.management.loading.ClassLoaderRepository}
2844      * that should be used in this object.
2845      *
2846      * @return the Class Loader Repository.
2847      *
2848      */
2849     protected ClassLoaderRepository getClassLoaderRepository() {
2850         return MBeanServerFactory.getClassLoaderRepository(server);
2851     }
2852 
2853     private Class<?> loadClass(final String className)
2854         throws ClassNotFoundException {
2855         AccessControlContext stack = AccessController.getContext();
2856         final ClassNotFoundException[] caughtException = new ClassNotFoundException[1];
2857 
2858         Class<?> c = javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Class<?>>() {
2859 
2860             @Override
2861             public Class<?> run() {
2862                 try {
2863                     ReflectUtil.checkPackageAccess(className);
2864                     return Class.forName(className);
2865                 } catch (ClassNotFoundException e) {
2866                     final ClassLoaderRepository clr =
2867                         getClassLoaderRepository();
2868                     try {
2869                         if (clr == null) throw new ClassNotFoundException(className);
2870                         return clr.loadClass(className);
2871                     } catch (ClassNotFoundException ex) {
2872                         caughtException[0] = ex;
2873                     }
2874                 }
2875                 return null;
2876             }
2877         }, stack, acc);
2878 
2879         if (caughtException[0] != null) {
2880             throw caughtException[0];
2881         }
2882 
2883         return c;
2884     }
2885 
2886 
2887     /*************************************/
2888     /* MBeanRegistration Interface       */
2889     /*************************************/
2890 
2891     /**
2892      * Allows the MBean to perform any operations it needs before
2893      * being registered in the MBean server.  If the name of the MBean
2894      * is not specified, the MBean can provide a name for its
2895      * registration.  If any exception is raised, the MBean will not be
2896      * registered in the MBean server.
2897      * <P>
2898      * In order to ensure proper run-time semantics of RequireModelMBean,
2899      * Any subclass of RequiredModelMBean overloading or overriding this
2900      * method should call <code>super.preRegister(server, name)</code>
2901      * in its own <code>preRegister</code> implementation.
2902      *
2903      * @param server The MBean server in which the MBean will be registered.
2904      *
2905      * @param name The object name of the MBean.  This name is null if
2906      * the name parameter to one of the <code>createMBean</code> or
2907      * <code>registerMBean</code> methods in the {@link MBeanServer}
2908      * interface is null.  In that case, this method must return a
2909      * non-null ObjectName for the new MBean.
2910      *
2911      * @return The name under which the MBean is to be registered.
2912      * This value must not be null.  If the <code>name</code>
2913      * parameter is not null, it will usually but not necessarily be
2914      * the returned value.
2915      *
2916      * @exception java.lang.Exception This exception will be caught by
2917      * the MBean server and re-thrown as an
2918      * {@link javax.management.MBeanRegistrationException
2919      * MBeanRegistrationException}.
2920      */
2921     public ObjectName preRegister(MBeanServer server,
2922                                   ObjectName name)
2923         throws java.lang.Exception  {
2924         // Since ModelMbeanInfo cannot be null (otherwise exception
2925         // thrown at creation)
2926         // no exception thrown on ModelMBeanInfo not set.
2927         if (name == null) throw new NullPointerException(
2928                      "name of RequiredModelMBean to registered is null");
2929         this.server = server;
2930         return name;
2931     }
2932 
2933     /**
2934      * Allows the MBean to perform any operations needed after having been
2935      * registered in the MBean server or after the registration has failed.
2936      * <P>
2937      * In order to ensure proper run-time semantics of RequireModelMBean,
2938      * Any subclass of RequiredModelMBean overloading or overriding this
2939      * method should call <code>super.postRegister(registrationDone)</code>
2940      * in its own <code>postRegister</code> implementation.
2941      *
2942      * @param registrationDone Indicates whether or not the MBean has
2943      * been successfully registered in the MBean server. The value
2944      * false means that the registration phase has failed.
2945      */
2946     public void postRegister(Boolean registrationDone) {
2947         registered = registrationDone.booleanValue();
2948     }
2949 
2950     /**
2951      * Allows the MBean to perform any operations it needs before
2952      * being unregistered by the MBean server.
2953      * <P>
2954      * In order to ensure proper run-time semantics of RequireModelMBean,
2955      * Any subclass of RequiredModelMBean overloading or overriding this
2956      * method should call <code>super.preDeregister()</code> in its own
2957      * <code>preDeregister</code> implementation.
2958      *
2959      * @exception java.lang.Exception This exception will be caught by
2960      * the MBean server and re-thrown as an
2961      * {@link javax.management.MBeanRegistrationException
2962      * MBeanRegistrationException}.
2963      */
2964     public void preDeregister() throws java.lang.Exception {
2965     }
2966 
2967     /**
2968      * Allows the MBean to perform any operations needed after having been
2969      * unregistered in the MBean server.
2970      * <P>
2971      * In order to ensure proper run-time semantics of RequireModelMBean,
2972      * Any subclass of RequiredModelMBean overloading or overriding this
2973      * method should call <code>super.postDeregister()</code> in its own
2974      * <code>postDeregister</code> implementation.
2975      */
2976     public void postDeregister() {
2977         registered = false;
2978         this.server=null;
2979     }
2980 
2981     private static final String[] primitiveTypes;
2982     private static final String[] primitiveWrappers;
2983     static {
2984         primitiveTypes = new String[] {
2985             Boolean.TYPE.getName(),
2986             Byte.TYPE.getName(),
2987             Character.TYPE.getName(),
2988             Short.TYPE.getName(),
2989             Integer.TYPE.getName(),
2990             Long.TYPE.getName(),
2991             Float.TYPE.getName(),
2992             Double.TYPE.getName(),
2993             Void.TYPE.getName()
2994         };
2995         primitiveWrappers = new String[] {
2996             Boolean.class.getName(),
2997             Byte.class.getName(),
2998             Character.class.getName(),
2999             Short.class.getName(),
3000             Integer.class.getName(),
3001             Long.class.getName(),
3002             Float.class.getName(),
3003             Double.class.getName(),
3004             Void.class.getName()
3005         };
3006     }
3007 }