1 /*
   2  * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 /*
  26  * @author    IBM Corp.
  27  *
  28  * Copyright IBM Corp. 1999-2000.  All rights reserved.
  29  */
  30 
  31 package javax.management.modelmbean;
  32 
  33 import static com.sun.jmx.defaults.JmxProperties.MODELMBEAN_LOGGER;
  34 import com.sun.jmx.mbeanserver.GetPropertyAction;
  35 
  36 import java.io.IOException;
  37 import java.io.ObjectInputStream;
  38 import java.io.ObjectOutputStream;
  39 import java.io.ObjectStreamField;
  40 import java.lang.reflect.Method;
  41 import java.security.AccessController;
  42 import java.util.logging.Level;
  43 
  44 import javax.management.Descriptor;
  45 import javax.management.DescriptorKey;
  46 import javax.management.DescriptorAccess;
  47 import javax.management.MBeanAttributeInfo;
  48 import javax.management.RuntimeOperationsException;
  49 
  50 /**
  51  * <p>The ModelMBeanAttributeInfo object describes an attribute of the ModelMBean.
  52  * It is a subclass of MBeanAttributeInfo with the addition of an associated Descriptor
  53  * and an implementation of the DescriptorAccess interface.</p>
  54  *
  55  * <P id="descriptor">
  56  * The fields in the descriptor are defined, but not limited to, the following.
  57  * Note that when the Type in this table is Number, a String that is the decimal
  58  * representation of a Long can also be used.</P>
  59  *
  60  * <table border="1" cellpadding="5">
  61  * <tr><th>Name</th><th>Type</th><th>Meaning</th></tr>
  62  * <tr><td>name</td><td>String</td>
  63  *     <td>Attribute name.</td></tr>
  64  * <tr><td>descriptorType</td><td>String</td>
  65  *     <td>Must be "attribute".</td></tr>
  66  * <tr id="value-field"><td>value</td><td>Object</td>
  67  *     <td>Current (cached) value for attribute.</td></tr>
  68  * <tr><td>default</td><td>Object</td>
  69  *     <td>Default value for attribute.</td></tr>
  70  * <tr><td>displayName</td><td>String</td>
  71  *     <td>Name of attribute to be used in displays.</td></tr>
  72  * <tr><td>getMethod</td><td>String</td>
  73  *     <td>Name of operation descriptor for get method.</td></tr>
  74  * <tr><td>setMethod</td><td>String</td>
  75  *     <td>Name of operation descriptor for set method.</td></tr>
  76  * <tr><td>protocolMap</td><td>Descriptor</td>
  77  *     <td>See the section "Protocol Map Support" in the JMX specification
  78  *         document.  Mappings must be appropriate for the attribute and entries
  79  *         can be updated or augmented at runtime.</td></tr>
  80  * <tr><td>persistPolicy</td><td>String</td>
  81  *     <td>One of: OnUpdate|OnTimer|NoMoreOftenThan|OnUnregister|Always|Never.
  82  *         See the section "MBean Descriptor Fields" in the JMX specification
  83  *         document.</td></tr>
  84  * <tr><td>persistPeriod</td><td>Number</td>
  85  *     <td>Frequency of persist cycle in seconds. Used when persistPolicy is
  86  *         "OnTimer" or "NoMoreOftenThan".</td></tr>
  87  * <tr><td>currencyTimeLimit</td><td>Number</td>
  88  *     <td>How long <a href="#value=field">value</a> is valid: &lt;0 never,
  89  *         =0 always, &gt;0 seconds.</td></tr>
  90  * <tr><td>lastUpdatedTimeStamp</td><td>Number</td>
  91  *     <td>When <a href="#value-field">value</a> was set.</td></tr>
  92  * <tr><td>visibility</td><td>Number</td>
  93  *     <td>1-4 where 1: always visible, 4: rarely visible.</td></tr>
  94  * <tr><td>presentationString</td><td>String</td>
  95  *     <td>XML formatted string to allow presentation of data.</td></tr>
  96  * </table>
  97  *
  98  * <p>The default descriptor contains the name, descriptorType and displayName
  99  * fields.  The default value of the name and displayName fields is the name of
 100  * the attribute.</p>
 101  *
 102  * <p><b>Note:</b> because of inconsistencies in previous versions of
 103  * this specification, it is recommended not to use negative or zero
 104  * values for <code>currencyTimeLimit</code>.  To indicate that a
 105  * cached value is never valid, omit the
 106  * <code>currencyTimeLimit</code> field.  To indicate that it is
 107  * always valid, use a very large number for this field.</p>
 108  *
 109  * <p>The <b>serialVersionUID</b> of this class is <code>6181543027787327345L</code>.
 110  *
 111  * @since 1.5
 112  */
 113 
 114 @SuppressWarnings("serial")  // serialVersionUID is not constant
 115 public class ModelMBeanAttributeInfo
 116     extends MBeanAttributeInfo
 117     implements DescriptorAccess {
 118 
 119     // Serialization compatibility stuff:
 120     // Two serial forms are supported in this class. The selected form depends
 121     // on system property "jmx.serial.form":
 122     //  - "1.0" for JMX 1.0
 123     //  - any other value for JMX 1.1 and higher
 124     //
 125     // Serial version for old serial form
 126     private static final long oldSerialVersionUID = 7098036920755973145L;
 127     //
 128     // Serial version for new serial form
 129     private static final long newSerialVersionUID = 6181543027787327345L;
 130     //
 131     // Serializable fields in old serial form
 132     private static final ObjectStreamField[] oldSerialPersistentFields =
 133     {
 134       new ObjectStreamField("attrDescriptor", Descriptor.class),
 135       new ObjectStreamField("currClass", String.class)
 136     };
 137     //
 138     // Serializable fields in new serial form
 139     private static final ObjectStreamField[] newSerialPersistentFields =
 140     {
 141       new ObjectStreamField("attrDescriptor", Descriptor.class)
 142     };
 143     //
 144     // Actual serial version and serial form
 145     private static final long serialVersionUID;
 146     /**
 147      * @serialField attrDescriptor Descriptor The {@link Descriptor}
 148      * containing the metadata corresponding to this attribute
 149      */
 150     private static final ObjectStreamField[] serialPersistentFields;
 151     private static boolean compat = false;
 152     static {
 153         try {
 154             GetPropertyAction act = new GetPropertyAction("jmx.serial.form");
 155             String form = AccessController.doPrivileged(act);
 156             compat = (form != null && form.equals("1.0"));
 157         } catch (Exception e) {
 158             // OK: No compat with 1.0
 159         }
 160         if (compat) {
 161             serialPersistentFields = oldSerialPersistentFields;
 162             serialVersionUID = oldSerialVersionUID;
 163         } else {
 164             serialPersistentFields = newSerialPersistentFields;
 165             serialVersionUID = newSerialVersionUID;
 166         }
 167     }
 168     //
 169     // END Serialization compatibility stuff
 170 
 171         /**
 172          * @serial The {@link Descriptor} containing the metadata corresponding to
 173          * this attribute
 174          */
 175         private Descriptor attrDescriptor = validDescriptor(null);
 176 
 177         private final static String currClass = "ModelMBeanAttributeInfo";
 178 
 179         /**
 180          * Constructs a ModelMBeanAttributeInfo object with a default
 181          * descriptor. The {@link Descriptor} of the constructed
 182          * object will include fields contributed by any annotations
 183          * on the {@code Method} objects that contain the {@link
 184          * DescriptorKey} meta-annotation.
 185          *
 186          * @param name The name of the attribute.
 187          * @param description A human readable description of the attribute. Optional.
 188          * @param getter The method used for reading the attribute value.
 189          *          May be null if the property is write-only.
 190          * @param setter The method used for writing the attribute value.
 191          *          May be null if the attribute is read-only.
 192          * @exception IntrospectionException There is a consistency
 193          * problem in the definition of this attribute.
 194          *
 195          */
 196 
 197         public ModelMBeanAttributeInfo(String name,
 198                                        String description,
 199                                        Method getter,
 200                                        Method setter)
 201         throws javax.management.IntrospectionException {
 202                 super(name, description, getter, setter);
 203 
 204                 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 205                     MODELMBEAN_LOGGER.logp(Level.FINER,
 206                             ModelMBeanAttributeInfo.class.getName(),
 207                             "ModelMBeanAttributeInfo(" +
 208                             "String,String,Method,Method)",
 209                             "Entry", name);
 210                 }
 211 
 212                 attrDescriptor = validDescriptor(null);
 213                 // put getter and setter methods in operations list
 214                 // create default descriptor
 215 
 216         }
 217 
 218         /**
 219          * Constructs a ModelMBeanAttributeInfo object.  The {@link
 220          * Descriptor} of the constructed object will include fields
 221          * contributed by any annotations on the {@code Method}
 222          * objects that contain the {@link DescriptorKey}
 223          * meta-annotation.
 224          *
 225          * @param name The name of the attribute.
 226          * @param description A human readable description of the attribute. Optional.
 227          * @param getter The method used for reading the attribute value.
 228          *          May be null if the property is write-only.
 229          * @param setter The method used for writing the attribute value.
 230          *          May be null if the attribute is read-only.
 231          * @param descriptor An instance of Descriptor containing the
 232          * appropriate metadata for this instance of the Attribute. If
 233          * it is null, then a default descriptor will be created.  If
 234          * the descriptor does not contain the field "displayName" this field is added
 235          * in the descriptor with its default value.
 236          * @exception IntrospectionException There is a consistency
 237          * problem in the definition of this attribute.
 238          * @exception RuntimeOperationsException Wraps an
 239          * IllegalArgumentException. The descriptor is invalid, or descriptor
 240          * field "name" is not equal to name parameter, or descriptor field
 241          * "descriptorType" is not equal to "attribute".
 242          *
 243          */
 244 
 245         public ModelMBeanAttributeInfo(String name,
 246                                        String description,
 247                                        Method getter,
 248                                        Method setter,
 249                                        Descriptor descriptor)
 250         throws javax.management.IntrospectionException {
 251 
 252                 super(name, description, getter, setter);
 253                 // put getter and setter methods in operations list
 254                 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 255                     MODELMBEAN_LOGGER.logp(Level.FINER,
 256                             ModelMBeanAttributeInfo.class.getName(),
 257                             "ModelMBeanAttributeInfo(" +
 258                             "String,String,Method,Method,Descriptor)",
 259                             "Entry", name);
 260                 }
 261                 attrDescriptor = validDescriptor(descriptor);
 262         }
 263 
 264         /**
 265          * Constructs a ModelMBeanAttributeInfo object with a default descriptor.
 266          *
 267          * @param name The name of the attribute
 268          * @param type The type or class name of the attribute
 269          * @param description A human readable description of the attribute.
 270          * @param isReadable True if the attribute has a getter method, false otherwise.
 271          * @param isWritable True if the attribute has a setter method, false otherwise.
 272          * @param isIs True if the attribute has an "is" getter, false otherwise.
 273          *
 274          */
 275         public ModelMBeanAttributeInfo(String name,
 276                                        String type,
 277                                        String description,
 278                                        boolean isReadable,
 279                                        boolean isWritable,
 280                                        boolean isIs)
 281     {
 282 
 283                 super(name, type, description, isReadable, isWritable, isIs);
 284                 // create default descriptor
 285                 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 286                     MODELMBEAN_LOGGER.logp(Level.FINER,
 287                             ModelMBeanAttributeInfo.class.getName(),
 288                             "ModelMBeanAttributeInfo(" +
 289                             "String,String,String,boolean,boolean,boolean)",
 290                             "Entry", name);
 291                 }
 292                 attrDescriptor = validDescriptor(null);
 293         }
 294 
 295         /**
 296          * Constructs a ModelMBeanAttributeInfo object.
 297          *
 298          * @param name The name of the attribute
 299          * @param type The type or class name of the attribute
 300          * @param description A human readable description of the attribute.
 301          * @param isReadable True if the attribute has a getter method, false otherwise.
 302          * @param isWritable True if the attribute has a setter method, false otherwise.
 303          * @param isIs True if the attribute has an "is" getter, false otherwise.
 304          * @param descriptor An instance of Descriptor containing the
 305          * appropriate metadata for this instance of the Attribute. If
 306          * it is null then a default descriptor will be created.  If
 307          * the descriptor does not contain the field "displayName" this field
 308          * is added in the descriptor with its default value.
 309          * @exception RuntimeOperationsException Wraps an
 310          * IllegalArgumentException. The descriptor is invalid, or descriptor
 311          * field "name" is not equal to name parameter, or descriptor field
 312          * "descriptorType" is not equal to "attribute".
 313          *
 314          */
 315         public ModelMBeanAttributeInfo(String name,
 316                                        String type,
 317                                        String description,
 318                                        boolean isReadable,
 319                                        boolean isWritable,
 320                                        boolean isIs,
 321                                        Descriptor descriptor)
 322         {
 323                 super(name, type, description, isReadable, isWritable, isIs);
 324                 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 325                     MODELMBEAN_LOGGER.logp(Level.FINER,
 326                             ModelMBeanAttributeInfo.class.getName(),
 327                             "ModelMBeanAttributeInfo(String,String,String," +
 328                             "boolean,boolean,boolean,Descriptor)",
 329                             "Entry", name);
 330                 }
 331                 attrDescriptor = validDescriptor(descriptor);
 332         }
 333 
 334         /**
 335          * Constructs a new ModelMBeanAttributeInfo object from this
 336          * ModelMBeanAttributeInfo Object.  A default descriptor will
 337          * be created.
 338          *
 339          * @param inInfo the ModelMBeanAttributeInfo to be duplicated
 340          */
 341 
 342         public ModelMBeanAttributeInfo(ModelMBeanAttributeInfo inInfo)
 343         {
 344                 super(inInfo.getName(),
 345                           inInfo.getType(),
 346                           inInfo.getDescription(),
 347                           inInfo.isReadable(),
 348                           inInfo.isWritable(),
 349                           inInfo.isIs());
 350                 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 351                     MODELMBEAN_LOGGER.logp(Level.FINER,
 352                             ModelMBeanAttributeInfo.class.getName(),
 353                             "ModelMBeanAttributeInfo(ModelMBeanAttributeInfo)",
 354                             "Entry");
 355                 }
 356                 Descriptor newDesc = inInfo.getDescriptor();
 357                 attrDescriptor = validDescriptor(newDesc);
 358         }
 359 
 360         /**
 361          * Gets a copy of the associated Descriptor for the
 362          * ModelMBeanAttributeInfo.
 363          *
 364          * @return Descriptor associated with the
 365          * ModelMBeanAttributeInfo object.
 366          *
 367          * @see #setDescriptor
 368          */
 369 
 370         public Descriptor getDescriptor() {
 371             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 372                 MODELMBEAN_LOGGER.logp(Level.FINER,
 373                         ModelMBeanAttributeInfo.class.getName(),
 374                         "getDescriptor()", "Entry");
 375             }
 376                 if (attrDescriptor == null) {
 377                     attrDescriptor = validDescriptor(null);
 378                 }
 379                 return((Descriptor)attrDescriptor.clone());
 380         }
 381 
 382 
 383         /**
 384         * Sets associated Descriptor (full replace) for the
 385         * ModelMBeanAttributeDescriptor.  If the new Descriptor is
 386         * null, then the associated Descriptor reverts to a default
 387         * descriptor.  The Descriptor is validated before it is
 388         * assigned.  If the new Descriptor is invalid, then a
 389         * RuntimeOperationsException wrapping an
 390         * IllegalArgumentException is thrown.
 391         * @param inDescriptor replaces the Descriptor associated with the
 392         * ModelMBeanAttributeInfo
 393         *
 394         * @exception RuntimeOperationsException Wraps an
 395         * IllegalArgumentException for an invalid Descriptor
 396         *
 397         * @see #getDescriptor
 398         */
 399         public void setDescriptor(Descriptor inDescriptor) {
 400             attrDescriptor =  validDescriptor(inDescriptor);
 401         }
 402 
 403         /**
 404         * Creates and returns a new ModelMBeanAttributeInfo which is a duplicate of this ModelMBeanAttributeInfo.
 405         *
 406         * @exception RuntimeOperationsException for illegal value for
 407         * field Names or field Values.  If the descriptor construction
 408         * fails for any reason, this exception will be thrown.
 409         */
 410 
 411         @Override
 412         public Object clone()
 413         {
 414             if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) {
 415                 MODELMBEAN_LOGGER.logp(Level.FINER,
 416                         ModelMBeanAttributeInfo.class.getName(),
 417                         "clone()", "Entry");
 418             }
 419                 return(new ModelMBeanAttributeInfo(this));
 420         }
 421 
 422         /**
 423         * Returns a human-readable version of the
 424         * ModelMBeanAttributeInfo instance.
 425         */
 426         @Override
 427         public String toString()
 428         {
 429             return
 430                 "ModelMBeanAttributeInfo: " + this.getName() +
 431                 " ; Description: " + this.getDescription() +
 432                 " ; Types: " + this.getType() +
 433                 " ; isReadable: " + this.isReadable() +
 434                 " ; isWritable: " + this.isWritable() +
 435                 " ; Descriptor: " + this.getDescriptor();
 436         }
 437 
 438 
 439         /**
 440          * Clones the passed in Descriptor, sets default values, and checks for validity.
 441          * If the Descriptor is invalid (for instance by having the wrong "name"),
 442          * this indicates programming error and a RuntimeOperationsException will be thrown.
 443          *
 444          * The following fields will be defaulted if they are not already set:
 445          * displayName=this.getName(),name=this.getName(),descriptorType = "attribute"
 446          *
 447          * @param in Descriptor to be checked, or null which is equivalent to
 448          * an empty Descriptor.
 449          * @exception RuntimeOperationsException if Descriptor is invalid
 450          */
 451         private Descriptor validDescriptor(final Descriptor in) throws RuntimeOperationsException {
 452 
 453             Descriptor clone;
 454             boolean defaulted = (in == null);
 455             if (defaulted) {
 456                 clone = new DescriptorSupport();
 457                 MODELMBEAN_LOGGER.finer("Null Descriptor, creating new.");
 458             } else {
 459                 clone = (Descriptor) in.clone();
 460             }
 461 
 462             //Setting defaults.
 463             if (defaulted && clone.getFieldValue("name")==null) {
 464                 clone.setField("name", this.getName());
 465                 MODELMBEAN_LOGGER.finer("Defaulting Descriptor name to " + this.getName());
 466             }
 467             if (defaulted && clone.getFieldValue("descriptorType")==null) {
 468                 clone.setField("descriptorType", "attribute");
 469                 MODELMBEAN_LOGGER.finer("Defaulting descriptorType to \"attribute\"");
 470             }
 471             if (clone.getFieldValue("displayName") == null) {
 472                 clone.setField("displayName",this.getName());
 473                 MODELMBEAN_LOGGER.finer("Defaulting Descriptor displayName to " + this.getName());
 474             }
 475 
 476             //Checking validity
 477             if (!clone.isValid()) {
 478                  throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"),
 479                     "The isValid() method of the Descriptor object itself returned false,"+
 480                     "one or more required fields are invalid. Descriptor:" + clone.toString());
 481             }
 482             if (!getName().equalsIgnoreCase((String)clone.getFieldValue("name"))) {
 483                     throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"),
 484                     "The Descriptor \"name\" field does not match the object described. " +
 485                      " Expected: "+ this.getName() + " , was: " + clone.getFieldValue("name"));
 486             }
 487 
 488             if (!"attribute".equalsIgnoreCase((String)clone.getFieldValue("descriptorType"))) {
 489                      throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"),
 490                     "The Descriptor \"descriptorType\" field does not match the object described. " +
 491                      " Expected: \"attribute\" ," + " was: " + clone.getFieldValue("descriptorType"));
 492             }
 493 
 494             return clone;
 495         }
 496 
 497 
 498     /**
 499      * Deserializes a {@link ModelMBeanAttributeInfo} from an {@link ObjectInputStream}.
 500      */
 501     private void readObject(ObjectInputStream in)
 502             throws IOException, ClassNotFoundException {
 503       // New serial form ignores extra field "currClass"
 504       in.defaultReadObject();
 505     }
 506 
 507 
 508     /**
 509      * Serializes a {@link ModelMBeanAttributeInfo} to an {@link ObjectOutputStream}.
 510      */
 511     private void writeObject(ObjectOutputStream out)
 512             throws IOException {
 513       if (compat)
 514       {
 515         // Serializes this instance in the old serial form
 516         //
 517         ObjectOutputStream.PutField fields = out.putFields();
 518         fields.put("attrDescriptor", attrDescriptor);
 519         fields.put("currClass", currClass);
 520         out.writeFields();
 521       }
 522       else
 523       {
 524         // Serializes this instance in the new serial form
 525         //
 526         out.defaultWriteObject();
 527       }
 528     }
 529 
 530 }