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: <0 never, 89 * =0 always, >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 }