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