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