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