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 static com.sun.jmx.mbeanserver.Util.cast; 35 import com.sun.jmx.mbeanserver.GetPropertyAction; 36 import com.sun.jmx.mbeanserver.Util; 37 38 import java.io.IOException; 39 import java.io.ObjectInputStream; 40 import java.io.ObjectOutputStream; 41 import java.io.ObjectStreamField; 42 43 import java.lang.reflect.Constructor; 44 45 import java.security.AccessController; 46 import java.util.HashMap; 47 import java.util.Iterator; 48 import java.util.Map; 49 import java.util.Set; 50 import java.util.SortedMap; 51 import java.util.StringTokenizer; 52 import java.util.TreeMap; 53 import java.util.logging.Level; 54 55 import javax.management.Descriptor; 56 import javax.management.ImmutableDescriptor; 57 import javax.management.MBeanException; 58 import javax.management.RuntimeOperationsException; 59 60 import sun.reflect.misc.ReflectUtil; 61 62 /** 63 * This class represents the metadata set for a ModelMBean element. A 64 * descriptor is part of the ModelMBeanInfo, 65 * ModelMBeanNotificationInfo, ModelMBeanAttributeInfo, 66 * ModelMBeanConstructorInfo, and ModelMBeanParameterInfo. 67 * <P> 68 * A descriptor consists of a collection of fields. Each field is in 69 * fieldname=fieldvalue format. Field names are not case sensitive, 70 * case will be preserved on field values. 71 * <P> 72 * All field names and values are not predefined. New fields can be 73 * defined and added by any program. Some fields have been predefined 74 * for consistency of implementation and support by the 75 * ModelMBeanInfo, ModelMBeanAttributeInfo, ModelMBeanConstructorInfo, 76 * ModelMBeanNotificationInfo, ModelMBeanOperationInfo and ModelMBean 77 * classes. 78 * 79 * <p>The <b>serialVersionUID</b> of this class is <code>-6292969195866300415L</code>. 80 * 81 * @since 1.5 82 */ 83 @SuppressWarnings("serial") // serialVersionUID not constant 84 public class DescriptorSupport 85 implements javax.management.Descriptor 86 { 87 88 // Serialization compatibility stuff: 89 // Two serial forms are supported in this class. The selected form depends 90 // on system property "jmx.serial.form": 91 // - "1.0" for JMX 1.0 92 // - any other value for JMX 1.1 and higher 93 // 94 // Serial version for old serial form 95 private static final long oldSerialVersionUID = 8071560848919417985L; 96 // 97 // Serial version for new serial form 98 private static final long newSerialVersionUID = -6292969195866300415L; 99 // 100 // Serializable fields in old serial form 101 private static final ObjectStreamField[] oldSerialPersistentFields = 102 { 103 new ObjectStreamField("descriptor", HashMap.class), 104 new ObjectStreamField("currClass", String.class) 105 }; 106 // 107 // Serializable fields in new serial form 108 private static final ObjectStreamField[] newSerialPersistentFields = 109 { 110 new ObjectStreamField("descriptor", HashMap.class) 111 }; 112 // 113 // Actual serial version and serial form 114 private static final long serialVersionUID; 115 /** 116 * @serialField descriptor HashMap The collection of fields representing this descriptor 117 */ 118 private static final ObjectStreamField[] serialPersistentFields; 119 private static final String serialForm; 120 static { 121 String form = null; 122 boolean compat = false; 123 try { 124 GetPropertyAction act = new GetPropertyAction("jmx.serial.form"); 125 form = AccessController.doPrivileged(act); 126 compat = "1.0".equals(form); // form may be null 127 } catch (Exception e) { 128 // OK: No compat with 1.0 129 } 130 serialForm = form; 131 if (compat) { 132 serialPersistentFields = oldSerialPersistentFields; 133 serialVersionUID = oldSerialVersionUID; 134 } else { 135 serialPersistentFields = newSerialPersistentFields; 136 serialVersionUID = newSerialVersionUID; 137 } 138 } 139 // 140 // END Serialization compatibility stuff 141 142 /* Spec says that field names are case-insensitive, but that case 143 is preserved. This means that we need to be able to map from a 144 name that may differ in case to the actual name that is used in 145 the HashMap. Thus, descriptorMap is a TreeMap with a Comparator 146 that ignores case. 147 148 Previous versions of this class had a field called "descriptor" 149 of type HashMap where the keys were directly Strings. This is 150 hard to reconcile with the required semantics, so we fabricate 151 that field virtually during serialization and deserialization 152 but keep the real information in descriptorMap. 153 */ 154 private transient SortedMap<String, Object> descriptorMap; 155 156 private static final String currClass = "DescriptorSupport"; 157 158 159 /** 160 * Descriptor default constructor. 161 * Default initial descriptor size is 20. It will grow as needed.<br> 162 * Note that the created empty descriptor is not a valid descriptor 163 * (the method {@link #isValid isValid} returns <CODE>false</CODE>) 164 */ 165 public DescriptorSupport() { 166 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 167 MODELMBEAN_LOGGER.logp(Level.FINEST, 168 DescriptorSupport.class.getName(), 169 "DescriptorSupport()" , "Constructor"); 170 } 171 init(null); 172 } 173 174 /** 175 * Descriptor constructor. Takes as parameter the initial 176 * capacity of the Map that stores the descriptor fields. 177 * Capacity will grow as needed.<br> Note that the created empty 178 * descriptor is not a valid descriptor (the method {@link 179 * #isValid isValid} returns <CODE>false</CODE>). 180 * 181 * @param initNumFields The initial capacity of the Map that 182 * stores the descriptor fields. 183 * 184 * @exception RuntimeOperationsException for illegal value for 185 * initNumFields (<= 0) 186 * @exception MBeanException Wraps a distributed communication Exception. 187 */ 188 public DescriptorSupport(int initNumFields) 189 throws MBeanException, RuntimeOperationsException { 190 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 191 MODELMBEAN_LOGGER.logp(Level.FINEST, 192 DescriptorSupport.class.getName(), 193 "Descriptor(initNumFields = " + initNumFields + ")", 194 "Constructor"); 195 } 196 if (initNumFields <= 0) { 197 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 198 MODELMBEAN_LOGGER.logp(Level.FINEST, 199 DescriptorSupport.class.getName(), 200 "Descriptor(initNumFields)", 201 "Illegal arguments: initNumFields <= 0"); 202 } 203 final String msg = 204 "Descriptor field limit invalid: " + initNumFields; 205 final RuntimeException iae = new IllegalArgumentException(msg); 206 throw new RuntimeOperationsException(iae, msg); 207 } 208 init(null); 209 } 210 211 /** 212 * Descriptor constructor taking a Descriptor as parameter. 213 * Creates a new descriptor initialized to the values of the 214 * descriptor passed in parameter. 215 * 216 * @param inDescr the descriptor to be used to initialize the 217 * constructed descriptor. If it is null or contains no descriptor 218 * fields, an empty Descriptor will be created. 219 */ 220 public DescriptorSupport(DescriptorSupport inDescr) { 221 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 222 MODELMBEAN_LOGGER.logp(Level.FINEST, 223 DescriptorSupport.class.getName(), 224 "Descriptor(Descriptor)", "Constructor"); 225 } 226 if (inDescr == null) 227 init(null); 228 else 229 init(inDescr.descriptorMap); 230 } 231 232 233 /** 234 * <p>Descriptor constructor taking an XML String.</p> 235 * 236 * <p>The format of the XML string is not defined, but an 237 * implementation must ensure that the string returned by 238 * {@link #toXMLString() toXMLString()} on an existing 239 * descriptor can be used to instantiate an equivalent 240 * descriptor using this constructor.</p> 241 * 242 * <p>In this implementation, all field values will be created 243 * as Strings. If the field values are not Strings, the 244 * programmer will have to reset or convert these fields 245 * correctly.</p> 246 * 247 * @param inStr An XML-formatted string used to populate this 248 * Descriptor. The format is not defined, but any 249 * implementation must ensure that the string returned by 250 * method {@link #toXMLString toXMLString} on an existing 251 * descriptor can be used to instantiate an equivalent 252 * descriptor when instantiated using this constructor. 253 * 254 * @exception RuntimeOperationsException If the String inStr 255 * passed in parameter is null 256 * @exception XMLParseException XML parsing problem while parsing 257 * the input String 258 * @exception MBeanException Wraps a distributed communication Exception. 259 */ 260 /* At some stage we should rewrite this code to be cleverer. Using 261 a StringTokenizer as we do means, first, that we accept a lot of 262 bogus strings without noticing they are bogus, and second, that we 263 split the string being parsed at characters like > even if they 264 occur in the middle of a field value. */ 265 public DescriptorSupport(String inStr) 266 throws MBeanException, RuntimeOperationsException, 267 XMLParseException { 268 /* parse an XML-formatted string and populate internal 269 * structure with it */ 270 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 271 MODELMBEAN_LOGGER.logp(Level.FINEST, 272 DescriptorSupport.class.getName(), 273 "Descriptor(String = '" + inStr + "')", "Constructor"); 274 } 275 if (inStr == null) { 276 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 277 MODELMBEAN_LOGGER.logp(Level.FINEST, 278 DescriptorSupport.class.getName(), 279 "Descriptor(String = null)", "Illegal arguments"); 280 } 281 final String msg = "String in parameter is null"; 282 final RuntimeException iae = new IllegalArgumentException(msg); 283 throw new RuntimeOperationsException(iae, msg); 284 } 285 286 final String lowerInStr = inStr.toLowerCase(); 287 if (!lowerInStr.startsWith("<descriptor>") 288 || !lowerInStr.endsWith("</descriptor>")) { 289 throw new XMLParseException("No <descriptor>, </descriptor> pair"); 290 } 291 292 // parse xmlstring into structures 293 init(null); 294 // create dummy descriptor: should have same size 295 // as number of fields in xmlstring 296 // loop through structures and put them in descriptor 297 298 StringTokenizer st = new StringTokenizer(inStr, "<> \t\n\r\f"); 299 300 boolean inFld = false; 301 boolean inDesc = false; 302 String fieldName = null; 303 String fieldValue = null; 304 305 306 while (st.hasMoreTokens()) { // loop through tokens 307 String tok = st.nextToken(); 308 309 if (tok.equalsIgnoreCase("FIELD")) { 310 inFld = true; 311 } else if (tok.equalsIgnoreCase("/FIELD")) { 312 if ((fieldName != null) && (fieldValue != null)) { 313 fieldName = 314 fieldName.substring(fieldName.indexOf('"') + 1, 315 fieldName.lastIndexOf('"')); 316 final Object fieldValueObject = 317 parseQuotedFieldValue(fieldValue); 318 setField(fieldName, fieldValueObject); 319 } 320 fieldName = null; 321 fieldValue = null; 322 inFld = false; 323 } else if (tok.equalsIgnoreCase("DESCRIPTOR")) { 324 inDesc = true; 325 } else if (tok.equalsIgnoreCase("/DESCRIPTOR")) { 326 inDesc = false; 327 fieldName = null; 328 fieldValue = null; 329 inFld = false; 330 } else if (inFld && inDesc) { 331 // want kw=value, eg, name="myname" value="myvalue" 332 int eq_separator = tok.indexOf("="); 333 if (eq_separator > 0) { 334 String kwPart = tok.substring(0,eq_separator); 335 String valPart = tok.substring(eq_separator+1); 336 if (kwPart.equalsIgnoreCase("NAME")) 337 fieldName = valPart; 338 else if (kwPart.equalsIgnoreCase("VALUE")) 339 fieldValue = valPart; 340 else { // xml parse exception 341 final String msg = 342 "Expected `name' or `value', got `" + tok + "'"; 343 throw new XMLParseException(msg); 344 } 345 } else { // xml parse exception 346 final String msg = 347 "Expected `keyword=value', got `" + tok + "'"; 348 throw new XMLParseException(msg); 349 } 350 } 351 } // while tokens 352 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 353 MODELMBEAN_LOGGER.logp(Level.FINEST, 354 DescriptorSupport.class.getName(), 355 "Descriptor(XMLString)", "Exit"); 356 } 357 } 358 359 /** 360 * Constructor taking field names and field values. Neither array 361 * can be null. 362 * 363 * @param fieldNames String array of field names. No elements of 364 * this array can be null. 365 * @param fieldValues Object array of the corresponding field 366 * values. Elements of the array can be null. The 367 * <code>fieldValue</code> must be valid for the 368 * <code>fieldName</code> (as defined in method {@link #isValid 369 * isValid}) 370 * 371 * <p>Note: array sizes of parameters should match. If both arrays 372 * are empty, then an empty descriptor is created.</p> 373 * 374 * @exception RuntimeOperationsException for illegal value for 375 * field Names or field Values. The array lengths must be equal. 376 * If the descriptor construction fails for any reason, this 377 * exception will be thrown. 378 * 379 */ 380 public DescriptorSupport(String[] fieldNames, Object[] fieldValues) 381 throws RuntimeOperationsException { 382 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 383 MODELMBEAN_LOGGER.logp(Level.FINEST, 384 DescriptorSupport.class.getName(), 385 "Descriptor(fieldNames,fieldObjects)", "Constructor"); 386 } 387 388 if ((fieldNames == null) || (fieldValues == null) || 389 (fieldNames.length != fieldValues.length)) { 390 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 391 MODELMBEAN_LOGGER.logp(Level.FINEST, 392 DescriptorSupport.class.getName(), 393 "Descriptor(fieldNames,fieldObjects)", 394 "Illegal arguments"); 395 } 396 397 final String msg = 398 "Null or invalid fieldNames or fieldValues"; 399 final RuntimeException iae = new IllegalArgumentException(msg); 400 throw new RuntimeOperationsException(iae, msg); 401 } 402 403 /* populate internal structure with fields */ 404 init(null); 405 for (int i=0; i < fieldNames.length; i++) { 406 // setField will throw an exception if a fieldName is be null. 407 // the fieldName and fieldValue will be validated in setField. 408 setField(fieldNames[i], fieldValues[i]); 409 } 410 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 411 MODELMBEAN_LOGGER.logp(Level.FINEST, 412 DescriptorSupport.class.getName(), 413 "Descriptor(fieldNames,fieldObjects)", "Exit"); 414 } 415 } 416 417 /** 418 * Constructor taking fields in the <i>fieldName=fieldValue</i> 419 * format. 420 * 421 * @param fields String array with each element containing a 422 * field name and value. If this array is null or empty, then the 423 * default constructor will be executed. Null strings or empty 424 * strings will be ignored. 425 * 426 * <p>All field values should be Strings. If the field values are 427 * not Strings, the programmer will have to reset or convert these 428 * fields correctly. 429 * 430 * <p>Note: Each string should be of the form 431 * <i>fieldName=fieldValue</i>. The field name 432 * ends at the first {@code =} character; for example if the String 433 * is {@code a=b=c} then the field name is {@code a} and its value 434 * is {@code b=c}. 435 * 436 * @exception RuntimeOperationsException for illegal value for 437 * field Names or field Values. The field must contain an 438 * "=". "=fieldValue", "fieldName", and "fieldValue" are illegal. 439 * FieldName cannot be null. "fieldName=" will cause the value to 440 * be null. If the descriptor construction fails for any reason, 441 * this exception will be thrown. 442 * 443 */ 444 public DescriptorSupport(String... fields) 445 { 446 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 447 MODELMBEAN_LOGGER.logp(Level.FINEST, 448 DescriptorSupport.class.getName(), 449 "Descriptor(String... fields)", "Constructor"); 450 } 451 init(null); 452 if (( fields == null ) || ( fields.length == 0)) 453 return; 454 455 init(null); 456 457 for (int i=0; i < fields.length; i++) { 458 if ((fields[i] == null) || (fields[i].equals(""))) { 459 continue; 460 } 461 int eq_separator = fields[i].indexOf("="); 462 if (eq_separator < 0) { 463 // illegal if no = or is first character 464 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 465 MODELMBEAN_LOGGER.logp(Level.FINEST, 466 DescriptorSupport.class.getName(), 467 "Descriptor(String... fields)", 468 "Illegal arguments: field does not have " + 469 "'=' as a name and value separator"); 470 } 471 final String msg = "Field in invalid format: no equals sign"; 472 final RuntimeException iae = new IllegalArgumentException(msg); 473 throw new RuntimeOperationsException(iae, msg); 474 } 475 476 String fieldName = fields[i].substring(0,eq_separator); 477 String fieldValue = null; 478 if (eq_separator < fields[i].length()) { 479 // = is not in last character 480 fieldValue = fields[i].substring(eq_separator+1); 481 } 482 483 if (fieldName.equals("")) { 484 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 485 MODELMBEAN_LOGGER.logp(Level.FINEST, 486 DescriptorSupport.class.getName(), 487 "Descriptor(String... fields)", 488 "Illegal arguments: fieldName is empty"); 489 } 490 491 final String msg = "Field in invalid format: no fieldName"; 492 final RuntimeException iae = new IllegalArgumentException(msg); 493 throw new RuntimeOperationsException(iae, msg); 494 } 495 496 setField(fieldName,fieldValue); 497 } 498 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 499 MODELMBEAN_LOGGER.logp(Level.FINEST, 500 DescriptorSupport.class.getName(), 501 "Descriptor(String... fields)", "Exit"); 502 } 503 } 504 505 private void init(Map<String, ?> initMap) { 506 descriptorMap = 507 new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER); 508 if (initMap != null) 509 descriptorMap.putAll(initMap); 510 } 511 512 // Implementation of the Descriptor interface 513 514 515 public synchronized Object getFieldValue(String fieldName) 516 throws RuntimeOperationsException { 517 518 if ((fieldName == null) || (fieldName.equals(""))) { 519 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 520 MODELMBEAN_LOGGER.logp(Level.FINEST, 521 DescriptorSupport.class.getName(), 522 "getFieldValue(String fieldName)", 523 "Illegal arguments: null field name"); 524 } 525 final String msg = "Fieldname requested is null"; 526 final RuntimeException iae = new IllegalArgumentException(msg); 527 throw new RuntimeOperationsException(iae, msg); 528 } 529 Object retValue = descriptorMap.get(fieldName); 530 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 531 MODELMBEAN_LOGGER.logp(Level.FINEST, 532 DescriptorSupport.class.getName(), 533 "getFieldValue(String fieldName = " + fieldName + ")", 534 "Returns '" + retValue + "'"); 535 } 536 return(retValue); 537 } 538 539 public synchronized void setField(String fieldName, Object fieldValue) 540 throws RuntimeOperationsException { 541 542 // field name cannot be null or empty 543 if ((fieldName == null) || (fieldName.equals(""))) { 544 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 545 MODELMBEAN_LOGGER.logp(Level.FINEST, 546 DescriptorSupport.class.getName(), 547 "setField(fieldName,fieldValue)", 548 "Illegal arguments: null or empty field name"); 549 } 550 551 final String msg = "Field name to be set is null or empty"; 552 final RuntimeException iae = new IllegalArgumentException(msg); 553 throw new RuntimeOperationsException(iae, msg); 554 } 555 556 if (!validateField(fieldName, fieldValue)) { 557 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 558 MODELMBEAN_LOGGER.logp(Level.FINEST, 559 DescriptorSupport.class.getName(), 560 "setField(fieldName,fieldValue)", 561 "Illegal arguments"); 562 } 563 564 final String msg = 565 "Field value invalid: " + fieldName + "=" + fieldValue; 566 final RuntimeException iae = new IllegalArgumentException(msg); 567 throw new RuntimeOperationsException(iae, msg); 568 } 569 570 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 571 MODELMBEAN_LOGGER.logp(Level.FINEST, 572 DescriptorSupport.class.getName(), 573 "setField(fieldName,fieldValue)", "Entry: setting '" 574 + fieldName + "' to '" + fieldValue + "'"); 575 } 576 577 // Since we do not remove any existing entry with this name, 578 // the field will preserve whatever case it had, ignoring 579 // any difference there might be in fieldName. 580 descriptorMap.put(fieldName, fieldValue); 581 } 582 583 public synchronized String[] getFields() { 584 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 585 MODELMBEAN_LOGGER.logp(Level.FINEST, 586 DescriptorSupport.class.getName(), 587 "getFields()", "Entry"); 588 } 589 int numberOfEntries = descriptorMap.size(); 590 591 String[] responseFields = new String[numberOfEntries]; 592 Set<Map.Entry<String, Object>> returnedSet = descriptorMap.entrySet(); 593 594 int i = 0; 595 596 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 597 MODELMBEAN_LOGGER.logp(Level.FINEST, 598 DescriptorSupport.class.getName(), 599 "getFields()", "Returning " + numberOfEntries + " fields"); 600 } 601 for (Iterator<Map.Entry<String, Object>> iter = returnedSet.iterator(); 602 iter.hasNext(); i++) { 603 Map.Entry<String, Object> currElement = iter.next(); 604 605 if (currElement == null) { 606 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 607 MODELMBEAN_LOGGER.logp(Level.FINEST, 608 DescriptorSupport.class.getName(), 609 "getFields()", "Element is null"); 610 } 611 } else { 612 Object currValue = currElement.getValue(); 613 if (currValue == null) { 614 responseFields[i] = currElement.getKey() + "="; 615 } else { 616 if (currValue instanceof java.lang.String) { 617 responseFields[i] = 618 currElement.getKey() + "=" + currValue.toString(); 619 } else { 620 responseFields[i] = 621 currElement.getKey() + "=(" + 622 currValue.toString() + ")"; 623 } 624 } 625 } 626 } 627 628 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 629 MODELMBEAN_LOGGER.logp(Level.FINEST, 630 DescriptorSupport.class.getName(), 631 "getFields()", "Exit"); 632 } 633 634 return responseFields; 635 } 636 637 public synchronized String[] getFieldNames() { 638 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 639 MODELMBEAN_LOGGER.logp(Level.FINEST, 640 DescriptorSupport.class.getName(), 641 "getFieldNames()", "Entry"); 642 } 643 int numberOfEntries = descriptorMap.size(); 644 645 String[] responseFields = new String[numberOfEntries]; 646 Set<Map.Entry<String, Object>> returnedSet = descriptorMap.entrySet(); 647 648 int i = 0; 649 650 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 651 MODELMBEAN_LOGGER.logp(Level.FINEST, 652 DescriptorSupport.class.getName(), 653 "getFieldNames()", 654 "Returning " + numberOfEntries + " fields"); 655 } 656 657 for (Iterator<Map.Entry<String, Object>> iter = returnedSet.iterator(); 658 iter.hasNext(); i++) { 659 Map.Entry<String, Object> currElement = iter.next(); 660 661 if (( currElement == null ) || (currElement.getKey() == null)) { 662 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 663 MODELMBEAN_LOGGER.logp(Level.FINEST, 664 DescriptorSupport.class.getName(), 665 "getFieldNames()", "Field is null"); 666 } 667 } else { 668 responseFields[i] = currElement.getKey().toString(); 669 } 670 } 671 672 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 673 MODELMBEAN_LOGGER.logp(Level.FINEST, 674 DescriptorSupport.class.getName(), 675 "getFieldNames()", "Exit"); 676 } 677 678 return responseFields; 679 } 680 681 682 public synchronized Object[] getFieldValues(String... fieldNames) { 683 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 684 MODELMBEAN_LOGGER.logp(Level.FINEST, 685 DescriptorSupport.class.getName(), 686 "getFieldValues(String... fieldNames)", "Entry"); 687 } 688 // if fieldNames == null return all values 689 // if fieldNames is String[0] return no values 690 691 final int numberOfEntries = 692 (fieldNames == null) ? descriptorMap.size() : fieldNames.length; 693 final Object[] responseFields = new Object[numberOfEntries]; 694 695 int i = 0; 696 697 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 698 MODELMBEAN_LOGGER.logp(Level.FINEST, 699 DescriptorSupport.class.getName(), 700 "getFieldValues(String... fieldNames)", 701 "Returning " + numberOfEntries + " fields"); 702 } 703 704 if (fieldNames == null) { 705 for (Object value : descriptorMap.values()) 706 responseFields[i++] = value; 707 } else { 708 for (i=0; i < fieldNames.length; i++) { 709 if ((fieldNames[i] == null) || (fieldNames[i].equals(""))) { 710 responseFields[i] = null; 711 } else { 712 responseFields[i] = getFieldValue(fieldNames[i]); 713 } 714 } 715 } 716 717 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 718 MODELMBEAN_LOGGER.logp(Level.FINEST, 719 DescriptorSupport.class.getName(), 720 "getFieldValues(String... fieldNames)", "Exit"); 721 } 722 723 return responseFields; 724 } 725 726 public synchronized void setFields(String[] fieldNames, 727 Object[] fieldValues) 728 throws RuntimeOperationsException { 729 730 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 731 MODELMBEAN_LOGGER.logp(Level.FINEST, 732 DescriptorSupport.class.getName(), 733 "setFields(fieldNames,fieldValues)", "Entry"); 734 } 735 736 if ((fieldNames == null) || (fieldValues == null) || 737 (fieldNames.length != fieldValues.length)) { 738 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 739 MODELMBEAN_LOGGER.logp(Level.FINEST, 740 DescriptorSupport.class.getName(), 741 "setFields(fieldNames,fieldValues)", 742 "Illegal arguments"); 743 } 744 745 final String msg = "fieldNames and fieldValues are null or invalid"; 746 final RuntimeException iae = new IllegalArgumentException(msg); 747 throw new RuntimeOperationsException(iae, msg); 748 } 749 750 for (int i=0; i < fieldNames.length; i++) { 751 if (( fieldNames[i] == null) || (fieldNames[i].equals(""))) { 752 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 753 MODELMBEAN_LOGGER.logp(Level.FINEST, 754 DescriptorSupport.class.getName(), 755 "setFields(fieldNames,fieldValues)", 756 "Null field name encountered at element " + i); 757 } 758 final String msg = "fieldNames is null or invalid"; 759 final RuntimeException iae = new IllegalArgumentException(msg); 760 throw new RuntimeOperationsException(iae, msg); 761 } 762 setField(fieldNames[i], fieldValues[i]); 763 } 764 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 765 MODELMBEAN_LOGGER.logp(Level.FINEST, 766 DescriptorSupport.class.getName(), 767 "setFields(fieldNames,fieldValues)", "Exit"); 768 } 769 } 770 771 /** 772 * Returns a new Descriptor which is a duplicate of the Descriptor. 773 * 774 * @exception RuntimeOperationsException for illegal value for 775 * field Names or field Values. If the descriptor construction 776 * fails for any reason, this exception will be thrown. 777 */ 778 779 @Override 780 public synchronized Object clone() throws RuntimeOperationsException { 781 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 782 MODELMBEAN_LOGGER.logp(Level.FINEST, 783 DescriptorSupport.class.getName(), 784 "clone()", "Entry"); 785 } 786 return(new DescriptorSupport(this)); 787 } 788 789 public synchronized void removeField(String fieldName) { 790 if ((fieldName == null) || (fieldName.equals(""))) { 791 return; 792 } 793 794 descriptorMap.remove(fieldName); 795 } 796 797 /** 798 * Compares this descriptor to the given object. The objects are equal if 799 * the given object is also a Descriptor, and if the two Descriptors have 800 * the same field names (possibly differing in case) and the same 801 * associated values. The respective values for a field in the two 802 * Descriptors are equal if the following conditions hold:</p> 803 * 804 * <ul> 805 * <li>If one value is null then the other must be too.</li> 806 * <li>If one value is a primitive array then the other must be a primitive 807 * array of the same type with the same elements.</li> 808 * <li>If one value is an object array then the other must be too and 809 * {@link java.util.Arrays#deepEquals(Object[],Object[]) Arrays.deepEquals} 810 * must return true.</li> 811 * <li>Otherwise {@link Object#equals(Object)} must return true.</li> 812 * </ul> 813 * 814 * @param o the object to compare with. 815 * 816 * @return {@code true} if the objects are the same; {@code false} 817 * otherwise. 818 * 819 */ 820 // Note: this Javadoc is copied from javax.management.Descriptor 821 // due to 6369229. 822 @Override 823 public synchronized boolean equals(Object o) { 824 if (o == this) 825 return true; 826 if (! (o instanceof Descriptor)) 827 return false; 828 if (o instanceof ImmutableDescriptor) 829 return o.equals(this); 830 return new ImmutableDescriptor(descriptorMap).equals(o); 831 } 832 833 /** 834 * <p>Returns the hash code value for this descriptor. The hash 835 * code is computed as the sum of the hash codes for each field in 836 * the descriptor. The hash code of a field with name {@code n} 837 * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}. 838 * Here {@code h} is the hash code of {@code v}, computed as 839 * follows:</p> 840 * 841 * <ul> 842 * <li>If {@code v} is null then {@code h} is 0.</li> 843 * <li>If {@code v} is a primitive array then {@code h} is computed using 844 * the appropriate overloading of {@code java.util.Arrays.hashCode}.</li> 845 * <li>If {@code v} is an object array then {@code h} is computed using 846 * {@link java.util.Arrays#deepHashCode(Object[]) Arrays.deepHashCode}.</li> 847 * <li>Otherwise {@code h} is {@code v.hashCode()}.</li> 848 * </ul> 849 * 850 * @return A hash code value for this object. 851 * 852 */ 853 // Note: this Javadoc is copied from javax.management.Descriptor 854 // due to 6369229. 855 @Override 856 public synchronized int hashCode() { 857 final int size = descriptorMap.size(); 858 // descriptorMap is sorted with a comparator that ignores cases. 859 // 860 return Util.hashCode( 861 descriptorMap.keySet().toArray(new String[size]), 862 descriptorMap.values().toArray(new Object[size])); 863 } 864 865 /** 866 * Returns true if all of the fields have legal values given their 867 * names. 868 * <P> 869 * This implementation does not support interoperating with a directory 870 * or lookup service. Thus, conforming to the specification, no checking is 871 * done on the <i>"export"</i> field. 872 * <P> 873 * Otherwise this implementation returns false if: 874 * <P> 875 * <UL> 876 * <LI> name and descriptorType fieldNames are not defined, or 877 * null, or empty, or not String 878 * <LI> class, role, getMethod, setMethod fieldNames, if defined, 879 * are null or not String 880 * <LI> persistPeriod, currencyTimeLimit, lastUpdatedTimeStamp, 881 * lastReturnedTimeStamp if defined, are null, or not a Numeric 882 * String or not a Numeric Value >= -1 883 * <LI> log fieldName, if defined, is null, or not a Boolean or 884 * not a String with value "t", "f", "true", "false". These String 885 * values must not be case sensitive. 886 * <LI> visibility fieldName, if defined, is null, or not a 887 * Numeric String or a not Numeric Value >= 1 and <= 4 888 * <LI> severity fieldName, if defined, is null, or not a Numeric 889 * String or not a Numeric Value >= 0 and <= 6<br> 890 * <LI> persistPolicy fieldName, if defined, is null, or not one of 891 * the following strings:<br> 892 * "OnUpdate", "OnTimer", "NoMoreOftenThan", "OnUnregister", "Always", 893 * "Never". These String values must not be case sensitive.<br> 894 * </UL> 895 * 896 * @exception RuntimeOperationsException If the validity checking 897 * fails for any reason, this exception will be thrown. 898 */ 899 900 public synchronized boolean isValid() throws RuntimeOperationsException { 901 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 902 MODELMBEAN_LOGGER.logp(Level.FINEST, 903 DescriptorSupport.class.getName(), 904 "isValid()", "Entry"); 905 } 906 // verify that the descriptor is valid, by iterating over each field... 907 908 Set<Map.Entry<String, Object>> returnedSet = descriptorMap.entrySet(); 909 910 if (returnedSet == null) { // null descriptor, not valid 911 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 912 MODELMBEAN_LOGGER.logp(Level.FINEST, 913 DescriptorSupport.class.getName(), 914 "isValid()", "Returns false (null set)"); 915 } 916 return false; 917 } 918 // must have a name and descriptor type field 919 String thisName = (String)(this.getFieldValue("name")); 920 String thisDescType = (String)(getFieldValue("descriptorType")); 921 922 if ((thisName == null) || (thisDescType == null) || 923 (thisName.equals("")) || (thisDescType.equals(""))) { 924 return false; 925 } 926 927 // According to the descriptor type we validate the fields contained 928 929 for (Map.Entry<String, Object> currElement : returnedSet) { 930 if (currElement != null) { 931 if (currElement.getValue() != null) { 932 // validate the field valued... 933 if (validateField((currElement.getKey()).toString(), 934 (currElement.getValue()).toString())) { 935 continue; 936 } else { 937 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 938 MODELMBEAN_LOGGER.logp(Level.FINEST, 939 DescriptorSupport.class.getName(), 940 "isValid()", 941 "Field " + currElement.getKey() + "=" + 942 currElement.getValue() + " is not valid"); 943 } 944 return false; 945 } 946 } 947 } 948 } 949 950 // fell through, all fields OK 951 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 952 MODELMBEAN_LOGGER.logp(Level.FINEST, 953 DescriptorSupport.class.getName(), 954 "isValid()", "Returns true"); 955 } 956 return true; 957 } 958 959 960 // worker routine for isValid() 961 // name is not null 962 // descriptorType is not null 963 // getMethod and setMethod are not null 964 // persistPeriod is numeric 965 // currencyTimeLimit is numeric 966 // lastUpdatedTimeStamp is numeric 967 // visibility is 1-4 968 // severity is 0-6 969 // log is T or F 970 // role is not null 971 // class is not null 972 // lastReturnedTimeStamp is numeric 973 974 975 private boolean validateField(String fldName, Object fldValue) { 976 if ((fldName == null) || (fldName.equals(""))) 977 return false; 978 String SfldValue = ""; 979 boolean isAString = false; 980 if ((fldValue != null) && (fldValue instanceof java.lang.String)) { 981 SfldValue = (String) fldValue; 982 isAString = true; 983 } 984 985 boolean nameOrDescriptorType = 986 (fldName.equalsIgnoreCase("Name") || 987 fldName.equalsIgnoreCase("DescriptorType")); 988 if (nameOrDescriptorType || 989 fldName.equalsIgnoreCase("SetMethod") || 990 fldName.equalsIgnoreCase("GetMethod") || 991 fldName.equalsIgnoreCase("Role") || 992 fldName.equalsIgnoreCase("Class")) { 993 if (fldValue == null || !isAString) 994 return false; 995 if (nameOrDescriptorType && SfldValue.equals("")) 996 return false; 997 return true; 998 } else if (fldName.equalsIgnoreCase("visibility")) { 999 long v; 1000 if ((fldValue != null) && (isAString)) { 1001 v = toNumeric(SfldValue); 1002 } else if (fldValue instanceof java.lang.Integer) { 1003 v = ((Integer)fldValue).intValue(); 1004 } else return false; 1005 1006 if (v >= 1 && v <= 4) 1007 return true; 1008 else 1009 return false; 1010 } else if (fldName.equalsIgnoreCase("severity")) { 1011 1012 long v; 1013 if ((fldValue != null) && (isAString)) { 1014 v = toNumeric(SfldValue); 1015 } else if (fldValue instanceof java.lang.Integer) { 1016 v = ((Integer)fldValue).intValue(); 1017 } else return false; 1018 1019 return (v >= 0 && v <= 6); 1020 } else if (fldName.equalsIgnoreCase("PersistPolicy")) { 1021 return (((fldValue != null) && (isAString)) && 1022 ( SfldValue.equalsIgnoreCase("OnUpdate") || 1023 SfldValue.equalsIgnoreCase("OnTimer") || 1024 SfldValue.equalsIgnoreCase("NoMoreOftenThan") || 1025 SfldValue.equalsIgnoreCase("Always") || 1026 SfldValue.equalsIgnoreCase("Never") || 1027 SfldValue.equalsIgnoreCase("OnUnregister"))); 1028 } else if (fldName.equalsIgnoreCase("PersistPeriod") || 1029 fldName.equalsIgnoreCase("CurrencyTimeLimit") || 1030 fldName.equalsIgnoreCase("LastUpdatedTimeStamp") || 1031 fldName.equalsIgnoreCase("LastReturnedTimeStamp")) { 1032 1033 long v; 1034 if ((fldValue != null) && (isAString)) { 1035 v = toNumeric(SfldValue); 1036 } else if (fldValue instanceof java.lang.Number) { 1037 v = ((Number)fldValue).longValue(); 1038 } else return false; 1039 1040 return (v >= -1); 1041 } else if (fldName.equalsIgnoreCase("log")) { 1042 return ((fldValue instanceof java.lang.Boolean) || 1043 (isAString && 1044 (SfldValue.equalsIgnoreCase("T") || 1045 SfldValue.equalsIgnoreCase("true") || 1046 SfldValue.equalsIgnoreCase("F") || 1047 SfldValue.equalsIgnoreCase("false") ))); 1048 } 1049 1050 // default to true, it is a field we aren't validating (user etc.) 1051 return true; 1052 } 1053 1054 1055 1056 /** 1057 * <p>Returns an XML String representing the descriptor.</p> 1058 * 1059 * <p>The format is not defined, but an implementation must 1060 * ensure that the string returned by this method can be 1061 * used to build an equivalent descriptor when instantiated 1062 * using the constructor {@link #DescriptorSupport(String) 1063 * DescriptorSupport(String inStr)}.</p> 1064 * 1065 * <p>Fields which are not String objects will have toString() 1066 * called on them to create the value. The value will be 1067 * enclosed in parentheses. It is not guaranteed that you can 1068 * reconstruct these objects unless they have been 1069 * specifically set up to support toString() in a meaningful 1070 * format and have a matching constructor that accepts a 1071 * String in the same format.</p> 1072 * 1073 * <p>If the descriptor is empty the following String is 1074 * returned: <Descriptor></Descriptor></p> 1075 * 1076 * @return the XML string. 1077 * 1078 * @exception RuntimeOperationsException for illegal value for 1079 * field Names or field Values. If the XML formatted string 1080 * construction fails for any reason, this exception will be 1081 * thrown. 1082 */ 1083 public synchronized String toXMLString() { 1084 final StringBuilder buf = new StringBuilder("<Descriptor>"); 1085 Set<Map.Entry<String, Object>> returnedSet = descriptorMap.entrySet(); 1086 for (Map.Entry<String, Object> currElement : returnedSet) { 1087 final String name = currElement.getKey(); 1088 Object value = currElement.getValue(); 1089 String valueString = null; 1090 /* Set valueString to non-null if and only if this is a string that 1091 cannot be confused with the encoding of an object. If it 1092 could be so confused (surrounded by parentheses) then we 1093 call makeFieldValue as for any non-String object and end 1094 up with an encoding like "(java.lang.String/(thing))". */ 1095 if (value instanceof String) { 1096 final String svalue = (String) value; 1097 if (!svalue.startsWith("(") || !svalue.endsWith(")")) 1098 valueString = quote(svalue); 1099 } 1100 if (valueString == null) 1101 valueString = makeFieldValue(value); 1102 buf.append("<field name=\"").append(name).append("\" value=\"") 1103 .append(valueString).append("\"></field>"); 1104 } 1105 buf.append("</Descriptor>"); 1106 return buf.toString(); 1107 } 1108 1109 private static final String[] entities = { 1110 "  ", 1111 "\""", 1112 "<<", 1113 ">>", 1114 "&&", 1115 "\r ", 1116 "\t	", 1117 "\n ", 1118 "\f", 1119 }; 1120 private static final Map<String,Character> entityToCharMap = 1121 new HashMap<String,Character>(); 1122 private static final String[] charToEntityMap; 1123 1124 static { 1125 char maxChar = 0; 1126 for (int i = 0; i < entities.length; i++) { 1127 final char c = entities[i].charAt(0); 1128 if (c > maxChar) 1129 maxChar = c; 1130 } 1131 charToEntityMap = new String[maxChar + 1]; 1132 for (int i = 0; i < entities.length; i++) { 1133 final char c = entities[i].charAt(0); 1134 final String entity = entities[i].substring(1); 1135 charToEntityMap[c] = entity; 1136 entityToCharMap.put(entity, c); 1137 } 1138 } 1139 1140 private static boolean isMagic(char c) { 1141 return (c < charToEntityMap.length && charToEntityMap[c] != null); 1142 } 1143 1144 /* 1145 * Quote the string so that it will be acceptable to the (String) 1146 * constructor. Since the parsing code in that constructor is fairly 1147 * stupid, we're obliged to quote apparently innocuous characters like 1148 * space, <, and >. In a future version, we should rewrite the parser 1149 * and only quote " plus either \ or & (depending on the quote syntax). 1150 */ 1151 private static String quote(String s) { 1152 boolean found = false; 1153 for (int i = 0; i < s.length(); i++) { 1154 if (isMagic(s.charAt(i))) { 1155 found = true; 1156 break; 1157 } 1158 } 1159 if (!found) 1160 return s; 1161 final StringBuilder buf = new StringBuilder(); 1162 for (int i = 0; i < s.length(); i++) { 1163 char c = s.charAt(i); 1164 if (isMagic(c)) 1165 buf.append(charToEntityMap[c]); 1166 else 1167 buf.append(c); 1168 } 1169 return buf.toString(); 1170 } 1171 1172 private static String unquote(String s) throws XMLParseException { 1173 if (!s.startsWith("\"") || !s.endsWith("\"")) 1174 throw new XMLParseException("Value must be quoted: <" + s + ">"); 1175 final StringBuilder buf = new StringBuilder(); 1176 final int len = s.length() - 1; 1177 for (int i = 1; i < len; i++) { 1178 final char c = s.charAt(i); 1179 final int semi; 1180 final Character quoted; 1181 if (c == '&' 1182 && (semi = s.indexOf(';', i + 1)) >= 0 1183 && ((quoted = entityToCharMap.get(s.substring(i, semi+1))) 1184 != null)) { 1185 buf.append(quoted); 1186 i = semi; 1187 } else 1188 buf.append(c); 1189 } 1190 return buf.toString(); 1191 } 1192 1193 /** 1194 * Make the string that will go inside "..." for a value that is not 1195 * a plain String. 1196 * @throws RuntimeOperationsException if the value cannot be encoded. 1197 */ 1198 private static String makeFieldValue(Object value) { 1199 if (value == null) 1200 return "(null)"; 1201 1202 Class<?> valueClass = value.getClass(); 1203 try { 1204 valueClass.getConstructor(String.class); 1205 } catch (NoSuchMethodException e) { 1206 final String msg = 1207 "Class " + valueClass + " does not have a public " + 1208 "constructor with a single string arg"; 1209 final RuntimeException iae = new IllegalArgumentException(msg); 1210 throw new RuntimeOperationsException(iae, 1211 "Cannot make XML descriptor"); 1212 } catch (SecurityException e) { 1213 // OK: we'll pretend the constructor is there 1214 // too bad if it's not: we'll find out when we try to 1215 // reconstruct the DescriptorSupport 1216 } 1217 1218 final String quotedValueString = quote(value.toString()); 1219 1220 return "(" + valueClass.getName() + "/" + quotedValueString + ")"; 1221 } 1222 1223 /* 1224 * Parse a field value from the XML produced by toXMLString(). 1225 * Given a descriptor XML containing <field name="nnn" value="vvv">, 1226 * the argument to this method will be "vvv" (a string including the 1227 * containing quote characters). If vvv begins and ends with parentheses, 1228 * then it may contain: 1229 * - the characters "null", in which case the result is null; 1230 * - a value of the form "some.class.name/xxx", in which case the 1231 * result is equivalent to `new some.class.name("xxx")'; 1232 * - some other string, in which case the result is that string, 1233 * without the parentheses. 1234 */ 1235 private static Object parseQuotedFieldValue(String s) 1236 throws XMLParseException { 1237 s = unquote(s); 1238 if (s.equalsIgnoreCase("(null)")) 1239 return null; 1240 if (!s.startsWith("(") || !s.endsWith(")")) 1241 return s; 1242 final int slash = s.indexOf('/'); 1243 if (slash < 0) { 1244 // compatibility: old code didn't include class name 1245 return s.substring(1, s.length() - 1); 1246 } 1247 final String className = s.substring(1, slash); 1248 final Constructor<?> constr; 1249 try { 1250 final ClassLoader contextClassLoader = 1251 Thread.currentThread().getContextClassLoader(); 1252 if (contextClassLoader == null) { 1253 ReflectUtil.checkPackageAccess(className); 1254 } 1255 final Class<?> c = 1256 Class.forName(className, false, contextClassLoader); 1257 constr = c.getConstructor(new Class<?>[] {String.class}); 1258 } catch (Exception e) { 1259 throw new XMLParseException(e, 1260 "Cannot parse value: <" + s + ">"); 1261 } 1262 final String arg = s.substring(slash + 1, s.length() - 1); 1263 try { 1264 return constr.newInstance(new Object[] {arg}); 1265 } catch (Exception e) { 1266 final String msg = 1267 "Cannot construct instance of " + className + 1268 " with arg: <" + s + ">"; 1269 throw new XMLParseException(e, msg); 1270 } 1271 } 1272 1273 /** 1274 * Returns <pv>a human readable string representing the 1275 * descriptor</pv>. The string will be in the format of 1276 * "fieldName=fieldValue,fieldName2=fieldValue2,..."<br> 1277 * 1278 * If there are no fields in the descriptor, then an empty String 1279 * is returned.<br> 1280 * 1281 * If a fieldValue is an object then the toString() method is 1282 * called on it and its returned value is used as the value for 1283 * the field enclosed in parenthesis. 1284 * 1285 * @exception RuntimeOperationsException for illegal value for 1286 * field Names or field Values. If the descriptor string fails 1287 * for any reason, this exception will be thrown. 1288 */ 1289 @Override 1290 public synchronized String toString() { 1291 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 1292 MODELMBEAN_LOGGER.logp(Level.FINEST, 1293 DescriptorSupport.class.getName(), 1294 "toString()", "Entry"); 1295 } 1296 1297 String respStr = ""; 1298 String[] fields = getFields(); 1299 1300 if ((fields == null) || (fields.length == 0)) { 1301 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 1302 MODELMBEAN_LOGGER.logp(Level.FINEST, 1303 DescriptorSupport.class.getName(), 1304 "toString()", "Empty Descriptor"); 1305 } 1306 return respStr; 1307 } 1308 1309 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 1310 MODELMBEAN_LOGGER.logp(Level.FINEST, 1311 DescriptorSupport.class.getName(), 1312 "toString()", "Printing " + fields.length + " fields"); 1313 } 1314 1315 for (int i=0; i < fields.length; i++) { 1316 if (i == (fields.length - 1)) { 1317 respStr = respStr.concat(fields[i]); 1318 } else { 1319 respStr = respStr.concat(fields[i] + ", "); 1320 } 1321 } 1322 1323 if (MODELMBEAN_LOGGER.isLoggable(Level.FINEST)) { 1324 MODELMBEAN_LOGGER.logp(Level.FINEST, 1325 DescriptorSupport.class.getName(), 1326 "toString()", "Exit returning " + respStr); 1327 } 1328 1329 return respStr; 1330 } 1331 1332 // utility to convert to int, returns -2 if bogus. 1333 1334 private long toNumeric(String inStr) { 1335 try { 1336 return java.lang.Long.parseLong(inStr); 1337 } catch (Exception e) { 1338 return -2; 1339 } 1340 } 1341 1342 1343 /** 1344 * Deserializes a {@link DescriptorSupport} from an {@link 1345 * ObjectInputStream}. 1346 */ 1347 private void readObject(ObjectInputStream in) 1348 throws IOException, ClassNotFoundException { 1349 ObjectInputStream.GetField fields = in.readFields(); 1350 Map<String, Object> descriptor = cast(fields.get("descriptor", null)); 1351 init(null); 1352 if (descriptor != null) { 1353 descriptorMap.putAll(descriptor); 1354 } 1355 } 1356 1357 1358 /** 1359 * Serializes a {@link DescriptorSupport} to an {@link ObjectOutputStream}. 1360 */ 1361 /* If you set jmx.serial.form to "1.2.0" or "1.2.1", then we are 1362 bug-compatible with those versions. Specifically, field names 1363 are forced to lower-case before being written. This 1364 contradicts the spec, which, though it does not mention 1365 serialization explicitly, does say that the case of field names 1366 is preserved. But in 1.2.0 and 1.2.1, this requirement was not 1367 met. Instead, field names in the descriptor map were forced to 1368 lower case. Those versions expect this to have happened to a 1369 descriptor they deserialize and e.g. getFieldValue will not 1370 find a field whose name is spelt with a different case. 1371 */ 1372 private void writeObject(ObjectOutputStream out) throws IOException { 1373 ObjectOutputStream.PutField fields = out.putFields(); 1374 boolean compat = "1.0".equals(serialForm); 1375 if (compat) 1376 fields.put("currClass", currClass); 1377 1378 /* Purge the field "targetObject" from the DescriptorSupport before 1379 * serializing since the referenced object is typically not 1380 * serializable. We do this here rather than purging the "descriptor" 1381 * variable below because that HashMap doesn't do case-insensitivity. 1382 * See CR 6332962. 1383 */ 1384 SortedMap<String, Object> startMap = descriptorMap; 1385 if (startMap.containsKey("targetObject")) { 1386 startMap = new TreeMap<String, Object>(descriptorMap); 1387 startMap.remove("targetObject"); 1388 } 1389 1390 final HashMap<String, Object> descriptor; 1391 if (compat || "1.2.0".equals(serialForm) || 1392 "1.2.1".equals(serialForm)) { 1393 descriptor = new HashMap<String, Object>(); 1394 for (Map.Entry<String, Object> entry : startMap.entrySet()) 1395 descriptor.put(entry.getKey().toLowerCase(), entry.getValue()); 1396 } else 1397 descriptor = new HashMap<String, Object>(startMap); 1398 1399 fields.put("descriptor", descriptor); 1400 out.writeFields(); 1401 } 1402 1403 }