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