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