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.DescriptorAccess; 46 import javax.management.DescriptorKey; 47 import javax.management.MBeanOperationInfo; 48 import javax.management.MBeanParameterInfo; 49 import javax.management.RuntimeOperationsException; 50 51 /** 52 * <p>The ModelMBeanOperationInfo object describes a management operation of 53 * the ModelMBean. It is a subclass of MBeanOperationInfo with the addition 54 * of an associated Descriptor and an implementation of the DescriptorAccess 55 * interface.</p> 56 * 57 * <P id="descriptor"> 58 * The fields in the descriptor are defined, but not limited to, the following. 59 * Note that when the Type in this table is Number, a String that is the decimal 60 * representation of a Long can also be used.</P> 61 * 62 * <table border="1" cellpadding="5"> 63 * <tr><th>Name</th><th>Type</th><th>Meaning</th></tr> 64 * <tr><td>name</td><td>String</td> 65 * <td>Operation name.</td></tr> 66 * <tr><td>descriptorType</td><td>String</td> 67 * <td>Must be "operation".</td></tr> 68 * <tr><td>class</td><td>String</td> 69 * <td>Class where method is defined (fully qualified).</td></tr> 70 * <tr><td>role</td><td>String</td> 71 * <td>Must be "operation", "getter", or "setter".</td></tr> 72 * <tr><td>targetObject</td><td>Object</td> 73 * <td>Object on which to execute this method.</td></tr> 74 * <tr><td>targetType</td><td>String</td> 75 * <td>type of object reference for targetObject. Can be: 76 * ObjectReference | Handle | EJBHandle | IOR | RMIReference.</td></tr> 77 * <tr><td>value</td><td>Object</td> 78 * <td>Cached value for operation.</td></tr> 79 * <tr><td>displayName</td><td>String</td> 80 * <td>Human readable display name of the operation.</td> 81 * <tr><td>currencyTimeLimit</td><td>Number</td> 82 * <td>How long cached value is valid.</td></tr> 83 * <tr><td>lastUpdatedTimeStamp</td><td>Number</td> 84 * <td>When cached value was set.</td></tr> 85 * <tr><td>visibility</td><td>Number</td> 86 * <td>1-4 where 1: always visible 4: rarely visible.</td></tr> 87 * <tr><td>presentationString</td><td>String</td> 88 * <td>XML formatted string to describe how to present operation</td></tr> 89 * </table> 90 * 91 * <p>The default descriptor will have name, descriptorType, displayName and 92 * role fields set. The default value of the name and displayName fields is 93 * the operation name.</p> 94 * 95 * <p><b>Note:</b> because of inconsistencies in previous versions of 96 * this specification, it is recommended not to use negative or zero 97 * values for <code>currencyTimeLimit</code>. To indicate that a 98 * cached value is never valid, omit the 99 * <code>currencyTimeLimit</code> field. To indicate that it is 100 * always valid, use a very large number for this field.</p> 101 * 102 * <p>The <b>serialVersionUID</b> of this class is <code>6532732096650090465L</code>. 103 * 104 * @since 1.5 105 */ 106 107 @SuppressWarnings("serial") // serialVersionUID is not constant 108 public class ModelMBeanOperationInfo extends MBeanOperationInfo 109 implements DescriptorAccess 110 { 111 112 // Serialization compatibility stuff: 113 // Two serial forms are supported in this class. The selected form depends 114 // on system property "jmx.serial.form": 115 // - "1.0" for JMX 1.0 116 // - any other value for JMX 1.1 and higher 117 // 118 // Serial version for old serial form 119 private static final long oldSerialVersionUID = 9087646304346171239L; 120 // 121 // Serial version for new serial form 122 private static final long newSerialVersionUID = 6532732096650090465L; 123 // 124 // Serializable fields in old serial form 125 private static final ObjectStreamField[] oldSerialPersistentFields = 126 { 127 new ObjectStreamField("operationDescriptor", Descriptor.class), 128 new ObjectStreamField("currClass", String.class) 129 }; 130 // 131 // Serializable fields in new serial form 132 private static final ObjectStreamField[] newSerialPersistentFields = 133 { 134 new ObjectStreamField("operationDescriptor", Descriptor.class) 135 }; 136 // 137 // Actual serial version and serial form 138 private static final long serialVersionUID; 139 /** 140 * @serialField operationDescriptor Descriptor The descriptor 141 * containing the appropriate metadata for this instance 142 */ 143 private static final ObjectStreamField[] serialPersistentFields; 144 private static boolean compat = false; 145 static { 146 try { 147 GetPropertyAction act = new GetPropertyAction("jmx.serial.form"); 148 String form = AccessController.doPrivileged(act); 149 compat = (form != null && form.equals("1.0")); 150 } catch (Exception e) { 151 // OK: No compat with 1.0 152 } 153 if (compat) { 154 serialPersistentFields = oldSerialPersistentFields; 155 serialVersionUID = oldSerialVersionUID; 156 } else { 157 serialPersistentFields = newSerialPersistentFields; 158 serialVersionUID = newSerialVersionUID; 159 } 160 } 161 // 162 // END Serialization compatibility stuff 163 164 /** 165 * @serial The descriptor containing the appropriate metadata for this instance 166 */ 167 private Descriptor operationDescriptor = validDescriptor(null); 168 169 private static final String currClass = "ModelMBeanOperationInfo"; 170 171 /** 172 * Constructs a ModelMBeanOperationInfo object with a default 173 * descriptor. The {@link Descriptor} of the constructed 174 * object will include fields contributed by any annotations 175 * on the {@code Method} object that contain the {@link 176 * DescriptorKey} meta-annotation. 177 * 178 * @param operationMethod The java.lang.reflect.Method object 179 * describing the MBean operation. 180 * @param description A human readable description of the operation. 181 */ 182 183 public ModelMBeanOperationInfo(String description, 184 Method operationMethod) 185 { 186 super(description, operationMethod); 187 // create default descriptor 188 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 189 MODELMBEAN_LOGGER.logp(Level.FINER, 190 ModelMBeanOperationInfo.class.getName(), 191 "ModelMBeanOperationInfo(String,Method)", 192 "Entry"); 193 } 194 operationDescriptor = validDescriptor(null); 195 } 196 197 /** 198 * Constructs a ModelMBeanOperationInfo object. The {@link 199 * Descriptor} of the constructed object will include fields 200 * contributed by any annotations on the {@code Method} object 201 * that contain the {@link DescriptorKey} meta-annotation. 202 * 203 * @param operationMethod The java.lang.reflect.Method object 204 * describing the MBean operation. 205 * @param description A human readable description of the 206 * operation. 207 * @param descriptor An instance of Descriptor containing the 208 * appropriate metadata for this instance of the 209 * ModelMBeanOperationInfo. If it is null a default 210 * descriptor will be created. If the descriptor does not 211 * contain the fields 212 * "displayName" or "role", the missing ones are added with 213 * their default values. 214 * 215 * @exception RuntimeOperationsException Wraps an 216 * IllegalArgumentException. The descriptor is invalid; or 217 * descriptor field "name" is not equal to 218 * operation name; or descriptor field "DescriptorType" is 219 * not equal to "operation"; or descriptor 220 * optional field "role" is present but not equal to "operation", 221 * "getter", or "setter". 222 * 223 */ 224 225 public ModelMBeanOperationInfo(String description, 226 Method operationMethod, 227 Descriptor descriptor) 228 { 229 230 super(description, operationMethod); 231 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 232 MODELMBEAN_LOGGER.logp(Level.FINER, 233 ModelMBeanOperationInfo.class.getName(), 234 "ModelMBeanOperationInfo(String,Method,Descriptor)", 235 "Entry"); 236 } 237 operationDescriptor = validDescriptor(descriptor); 238 } 239 240 /** 241 * Constructs a ModelMBeanOperationInfo object with a default descriptor. 242 * 243 * @param name The name of the method. 244 * @param description A human readable description of the operation. 245 * @param signature MBeanParameterInfo objects describing the 246 * parameters(arguments) of the method. 247 * @param type The type of the method's return value. 248 * @param impact The impact of the method, one of INFO, ACTION, 249 * ACTION_INFO, UNKNOWN. 250 */ 251 252 public ModelMBeanOperationInfo(String name, 253 String description, 254 MBeanParameterInfo[] signature, 255 String type, 256 int impact) 257 { 258 259 super(name, description, signature, type, impact); 260 // create default descriptor 261 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 262 MODELMBEAN_LOGGER.logp(Level.FINER, 263 ModelMBeanOperationInfo.class.getName(), 264 "ModelMBeanOperationInfo(" + 265 "String,String,MBeanParameterInfo[],String,int)", 266 "Entry"); 267 } 268 operationDescriptor = validDescriptor(null); 269 } 270 271 /** 272 * Constructs a ModelMBeanOperationInfo object. 273 * 274 * @param name The name of the method. 275 * @param description A human readable description of the operation. 276 * @param signature MBeanParameterInfo objects describing the 277 * parameters(arguments) of the method. 278 * @param type The type of the method's return value. 279 * @param impact The impact of the method, one of INFO, ACTION, 280 * ACTION_INFO, UNKNOWN. 281 * @param descriptor An instance of Descriptor containing the 282 * appropriate metadata for this instance of the 283 * MBeanOperationInfo. If it is null then a default descriptor 284 * will be created. If the descriptor does not contain 285 * fields "displayName" or "role", 286 * the missing ones are added with their default values. 287 * 288 * @exception RuntimeOperationsException Wraps an 289 * IllegalArgumentException. The descriptor is invalid; or 290 * descriptor field "name" is not equal to 291 * operation name; or descriptor field "DescriptorType" is 292 * not equal to "operation"; or descriptor optional 293 * field "role" is present but not equal to "operation", "getter", or 294 * "setter". 295 */ 296 297 public ModelMBeanOperationInfo(String name, 298 String description, 299 MBeanParameterInfo[] signature, 300 String type, 301 int impact, 302 Descriptor descriptor) 303 { 304 super(name, description, signature, type, impact); 305 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 306 MODELMBEAN_LOGGER.logp(Level.FINER, 307 ModelMBeanOperationInfo.class.getName(), 308 "ModelMBeanOperationInfo(String,String," + 309 "MBeanParameterInfo[],String,int,Descriptor)", 310 "Entry"); 311 } 312 operationDescriptor = validDescriptor(descriptor); 313 } 314 315 /** 316 * Constructs a new ModelMBeanOperationInfo object from this 317 * ModelMBeanOperation Object. 318 * 319 * @param inInfo the ModelMBeanOperationInfo to be duplicated 320 * 321 */ 322 323 public ModelMBeanOperationInfo(ModelMBeanOperationInfo inInfo) 324 { 325 super(inInfo.getName(), 326 inInfo.getDescription(), 327 inInfo.getSignature(), 328 inInfo.getReturnType(), 329 inInfo.getImpact()); 330 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 331 MODELMBEAN_LOGGER.logp(Level.FINER, 332 ModelMBeanOperationInfo.class.getName(), 333 "ModelMBeanOperationInfo(ModelMBeanOperationInfo)", 334 "Entry"); 335 } 336 Descriptor newDesc = inInfo.getDescriptor(); 337 operationDescriptor = validDescriptor(newDesc); 338 } 339 340 /** 341 * Creates and returns a new ModelMBeanOperationInfo which is a 342 * duplicate of this ModelMBeanOperationInfo. 343 * 344 */ 345 346 public Object clone () 347 { 348 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 349 MODELMBEAN_LOGGER.logp(Level.FINER, 350 ModelMBeanOperationInfo.class.getName(), 351 "clone()", "Entry"); 352 } 353 return(new ModelMBeanOperationInfo(this)) ; 354 } 355 356 /** 357 * Returns a copy of the associated Descriptor of the 358 * ModelMBeanOperationInfo. 359 * 360 * @return Descriptor associated with the 361 * ModelMBeanOperationInfo object. 362 * 363 * @see #setDescriptor 364 */ 365 366 public Descriptor getDescriptor() 367 { 368 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 369 MODELMBEAN_LOGGER.logp(Level.FINER, 370 ModelMBeanOperationInfo.class.getName(), 371 "getDescriptor()", "Entry"); 372 } 373 if (operationDescriptor == null) { 374 operationDescriptor = validDescriptor(null); 375 } 376 377 return((Descriptor) operationDescriptor.clone()); 378 } 379 380 /** 381 * Sets associated Descriptor (full replace) for the 382 * ModelMBeanOperationInfo If the new Descriptor is null, then 383 * the associated Descriptor reverts to a default descriptor. 384 * The Descriptor is validated before it is assigned. If the 385 * new Descriptor is invalid, then a 386 * RuntimeOperationsException wrapping an 387 * IllegalArgumentException is thrown. 388 * 389 * @param inDescriptor replaces the Descriptor associated with the 390 * ModelMBeanOperation. 391 * 392 * @exception RuntimeOperationsException Wraps an 393 * IllegalArgumentException for invalid Descriptor. 394 * 395 * @see #getDescriptor 396 */ 397 public void setDescriptor(Descriptor inDescriptor) 398 { 399 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 400 MODELMBEAN_LOGGER.logp(Level.FINER, 401 ModelMBeanOperationInfo.class.getName(), 402 "setDescriptor(Descriptor)", "Entry"); 403 } 404 operationDescriptor = validDescriptor(inDescriptor); 405 } 406 407 /** 408 * Returns a string containing the entire contents of the 409 * ModelMBeanOperationInfo in human readable form. 410 */ 411 public String toString() 412 { 413 if (MODELMBEAN_LOGGER.isLoggable(Level.FINER)) { 414 MODELMBEAN_LOGGER.logp(Level.FINER, 415 ModelMBeanOperationInfo.class.getName(), 416 "toString()", "Entry"); 417 } 418 String retStr = 419 "ModelMBeanOperationInfo: " + this.getName() + 420 " ; Description: " + this.getDescription() + 421 " ; Descriptor: " + this.getDescriptor() + 422 " ; ReturnType: " + this.getReturnType() + 423 " ; Signature: "; 424 MBeanParameterInfo[] pTypes = this.getSignature(); 425 for (int i=0; i < pTypes.length; i++) 426 { 427 retStr = retStr.concat((pTypes[i]).getType() + ", "); 428 } 429 return retStr; 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(), 439 * descriptorType="operation", role="operation" 440 * 441 * 442 * @param in Descriptor to be checked, or null which is equivalent to 443 * an empty Descriptor. 444 * @exception RuntimeOperationsException if Descriptor is invalid 445 */ 446 private Descriptor validDescriptor(final Descriptor in) 447 throws RuntimeOperationsException { 448 Descriptor clone; 449 boolean defaulted = (in == null); 450 if (defaulted) { 451 clone = new DescriptorSupport(); 452 MODELMBEAN_LOGGER.finer("Null Descriptor, creating new."); 453 } else { 454 clone = (Descriptor) in.clone(); 455 } 456 457 //Setting defaults. 458 if (defaulted && clone.getFieldValue("name")==null) { 459 clone.setField("name", this.getName()); 460 MODELMBEAN_LOGGER.finer("Defaulting Descriptor name to " + this.getName()); 461 } 462 if (defaulted && clone.getFieldValue("descriptorType")==null) { 463 clone.setField("descriptorType", "operation"); 464 MODELMBEAN_LOGGER.finer("Defaulting descriptorType to \"operation\""); 465 } 466 if (clone.getFieldValue("displayName") == null) { 467 clone.setField("displayName",this.getName()); 468 MODELMBEAN_LOGGER.finer("Defaulting Descriptor displayName to " + this.getName()); 469 } 470 if (clone.getFieldValue("role") == null) { 471 clone.setField("role","operation"); 472 MODELMBEAN_LOGGER.finer("Defaulting Descriptor role field to \"operation\""); 473 } 474 475 //Checking validity 476 if (!clone.isValid()) { 477 throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"), 478 "The isValid() method of the Descriptor object itself returned false,"+ 479 "one or more required fields are invalid. Descriptor:" + clone.toString()); 480 } 481 if (!getName().equalsIgnoreCase((String) clone.getFieldValue("name"))) { 482 throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"), 483 "The Descriptor \"name\" field does not match the object described. " + 484 " Expected: "+ this.getName() + " , was: " + clone.getFieldValue("name")); 485 } 486 if (!"operation".equalsIgnoreCase((String) clone.getFieldValue("descriptorType"))) { 487 throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"), 488 "The Descriptor \"descriptorType\" field does not match the object described. " + 489 " Expected: \"operation\" ," + " was: " + clone.getFieldValue("descriptorType")); 490 } 491 final String role = (String)clone.getFieldValue("role"); 492 if (!(role.equalsIgnoreCase("operation") || 493 role.equalsIgnoreCase("setter") || 494 role.equalsIgnoreCase("getter"))) { 495 throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"), 496 "The Descriptor \"role\" field does not match the object described. " + 497 " Expected: \"operation\", \"setter\", or \"getter\" ," + " was: " + clone.getFieldValue("role")); 498 } 499 final Object targetValue = clone.getFieldValue("targetType"); 500 if (targetValue != null) { 501 if (!(targetValue instanceof java.lang.String)) { 502 throw new RuntimeOperationsException(new IllegalArgumentException("Invalid Descriptor argument"), 503 "The Descriptor field \"targetValue\" is invalid class. " + 504 " Expected: java.lang.String, " + " was: " + targetValue.getClass().getName()); 505 } 506 } 507 return clone; 508 } 509 510 /** 511 * Deserializes a {@link ModelMBeanOperationInfo} from an {@link ObjectInputStream}. 512 */ 513 private void readObject(ObjectInputStream in) 514 throws IOException, ClassNotFoundException { 515 // New serial form ignores extra field "currClass" 516 in.defaultReadObject(); 517 } 518 519 520 /** 521 * Serializes a {@link ModelMBeanOperationInfo} to an {@link ObjectOutputStream}. 522 */ 523 private void writeObject(ObjectOutputStream out) 524 throws IOException { 525 if (compat) 526 { 527 // Serializes this instance in the old serial form 528 // 529 ObjectOutputStream.PutField fields = out.putFields(); 530 fields.put("operationDescriptor", operationDescriptor); 531 fields.put("currClass", currClass); 532 out.writeFields(); 533 } 534 else 535 { 536 // Serializes this instance in the new serial form 537 // 538 out.defaultWriteObject(); 539 } 540 } 541 542 }