1 /* 2 * Copyright (c) 2000, 2015, 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 27 package javax.management.openmbean; 28 29 30 // java import 31 // 32 import java.lang.reflect.Array; 33 import java.lang.reflect.Constructor; 34 import java.lang.reflect.Method; 35 import java.lang.reflect.Modifier; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.HashMap; 40 import java.util.HashSet; 41 import java.util.Map; 42 import java.util.Set; 43 import javax.management.Descriptor; 44 import javax.management.DescriptorRead; 45 import javax.management.ImmutableDescriptor; 46 import javax.management.MBeanAttributeInfo; 47 import com.sun.jmx.remote.util.EnvHelp; 48 import sun.reflect.misc.MethodUtil; 49 import sun.reflect.misc.ReflectUtil; 50 51 /** 52 * Describes an attribute of an open MBean. 53 * 54 * 55 * @since 1.5 56 */ 57 public class OpenMBeanAttributeInfoSupport 58 extends MBeanAttributeInfo 59 implements OpenMBeanAttributeInfo { 60 61 /* Serial version */ 62 static final long serialVersionUID = -4867215622149721849L; 63 64 /** 65 * @serial The open mbean attribute's <i>open type</i> 66 */ 67 private OpenType<?> openType; 68 69 /** 70 * @serial The open mbean attribute's default value 71 */ 72 private final Object defaultValue; 73 74 /** 75 * @serial The open mbean attribute's legal values. This {@link 76 * Set} is unmodifiable 77 */ 78 private final Set<?> legalValues; // to be constructed unmodifiable 79 80 /** 81 * @serial The open mbean attribute's min value 82 */ 83 private final Comparable<?> minValue; 84 85 /** 86 * @serial The open mbean attribute's max value 87 */ 88 private final Comparable<?> maxValue; 89 90 91 // As this instance is immutable, these two values need only 92 // be calculated once. 93 private transient Integer myHashCode = null; 94 private transient String myToString = null; 95 96 97 /** 98 * Constructs an {@code OpenMBeanAttributeInfoSupport} instance, 99 * which describes the attribute of an open MBean with the 100 * specified {@code name}, {@code openType} and {@code 101 * description}, and the specified read/write access properties. 102 * 103 * @param name cannot be a null or empty string. 104 * 105 * @param description cannot be a null or empty string. 106 * 107 * @param openType cannot be null. 108 * 109 * @param isReadable {@code true} if the attribute has a getter 110 * exposed for management. 111 * 112 * @param isWritable {@code true} if the attribute has a setter 113 * exposed for management. 114 * 115 * @param isIs {@code true} if the attribute's getter is of the 116 * form <code>is<i>XXX</i></code>. 117 * 118 * @throws IllegalArgumentException if {@code name} or {@code 119 * description} are null or empty string, or {@code openType} is 120 * null. 121 */ 122 public OpenMBeanAttributeInfoSupport(String name, 123 String description, 124 OpenType<?> openType, 125 boolean isReadable, 126 boolean isWritable, 127 boolean isIs) { 128 this(name, description, openType, isReadable, isWritable, isIs, 129 (Descriptor) null); 130 } 131 132 /** 133 * <p>Constructs an {@code OpenMBeanAttributeInfoSupport} instance, 134 * which describes the attribute of an open MBean with the 135 * specified {@code name}, {@code openType}, {@code 136 * description}, read/write access properties, and {@code Descriptor}.</p> 137 * 138 * <p>The {@code descriptor} can contain entries that will define 139 * the values returned by certain methods of this class, as 140 * explained in the <a href="package-summary.html#constraints"> 141 * package description</a>. 142 * 143 * @param name cannot be a null or empty string. 144 * 145 * @param description cannot be a null or empty string. 146 * 147 * @param openType cannot be null. 148 * 149 * @param isReadable {@code true} if the attribute has a getter 150 * exposed for management. 151 * 152 * @param isWritable {@code true} if the attribute has a setter 153 * exposed for management. 154 * 155 * @param isIs {@code true} if the attribute's getter is of the 156 * form <code>is<i>XXX</i></code>. 157 * 158 * @param descriptor The descriptor for the attribute. This may be null 159 * which is equivalent to an empty descriptor. 160 * 161 * @throws IllegalArgumentException if {@code name} or {@code 162 * description} are null or empty string, or {@code openType} is 163 * null, or the descriptor entries are invalid as described in the 164 * <a href="package-summary.html#constraints">package description</a>. 165 * 166 * @since 1.6 167 */ 168 public OpenMBeanAttributeInfoSupport(String name, 169 String description, 170 OpenType<?> openType, 171 boolean isReadable, 172 boolean isWritable, 173 boolean isIs, 174 Descriptor descriptor) { 175 // Construct parent's state 176 // 177 super(name, 178 (openType==null) ? null : openType.getClassName(), 179 description, 180 isReadable, 181 isWritable, 182 isIs, 183 ImmutableDescriptor.union(descriptor, (openType==null)?null: 184 openType.getDescriptor())); 185 186 // Initialize this instance's specific state 187 // 188 this.openType = openType; 189 190 descriptor = getDescriptor(); // replace null by empty 191 this.defaultValue = valueFrom(descriptor, "defaultValue", openType); 192 this.legalValues = valuesFrom(descriptor, "legalValues", openType); 193 this.minValue = comparableValueFrom(descriptor, "minValue", openType); 194 this.maxValue = comparableValueFrom(descriptor, "maxValue", openType); 195 196 try { 197 check(this); 198 } catch (OpenDataException e) { 199 throw new IllegalArgumentException(e.getMessage(), e); 200 } 201 } 202 203 /** 204 * Constructs an {@code OpenMBeanAttributeInfoSupport} instance, 205 * which describes the attribute of an open MBean with the 206 * specified {@code name}, {@code openType}, {@code description} 207 * and {@code defaultValue}, and the specified read/write access 208 * properties. 209 * 210 * @param name cannot be a null or empty string. 211 * 212 * @param description cannot be a null or empty string. 213 * 214 * @param openType cannot be null. 215 * 216 * @param isReadable {@code true} if the attribute has a getter 217 * exposed for management. 218 * 219 * @param isWritable {@code true} if the attribute has a setter 220 * exposed for management. 221 * 222 * @param isIs {@code true} if the attribute's getter is of the 223 * form <code>is<i>XXX</i></code>. 224 * 225 * @param defaultValue must be a valid value for the {@code 226 * openType} specified for this attribute; default value not 227 * supported for {@code ArrayType} and {@code TabularType}; can 228 * be null, in which case it means that no default value is set. 229 * 230 * @param <T> allows the compiler to check that the {@code defaultValue}, 231 * if non-null, has the correct Java type for the given {@code openType}. 232 * 233 * @throws IllegalArgumentException if {@code name} or {@code 234 * description} are null or empty string, or {@code openType} is 235 * null. 236 * 237 * @throws OpenDataException if {@code defaultValue} is not a 238 * valid value for the specified {@code openType}, or {@code 239 * defaultValue} is non null and {@code openType} is an {@code 240 * ArrayType} or a {@code TabularType}. 241 */ 242 public <T> OpenMBeanAttributeInfoSupport(String name, 243 String description, 244 OpenType<T> openType, 245 boolean isReadable, 246 boolean isWritable, 247 boolean isIs, 248 T defaultValue) 249 throws OpenDataException { 250 this(name, description, openType, isReadable, isWritable, isIs, 251 defaultValue, (T[]) null); 252 } 253 254 255 /** 256 * <p>Constructs an {@code OpenMBeanAttributeInfoSupport} instance, 257 * which describes the attribute of an open MBean with the 258 * specified {@code name}, {@code openType}, {@code description}, 259 * {@code defaultValue} and {@code legalValues}, and the specified 260 * read/write access properties.</p> 261 * 262 * <p>The contents of {@code legalValues} are copied, so subsequent 263 * modifications of the array referenced by {@code legalValues} 264 * have no impact on this {@code OpenMBeanAttributeInfoSupport} 265 * instance.</p> 266 * 267 * @param name cannot be a null or empty string. 268 * 269 * @param description cannot be a null or empty string. 270 * 271 * @param openType cannot be null. 272 * 273 * @param isReadable {@code true} if the attribute has a getter 274 * exposed for management. 275 * 276 * @param isWritable {@code true} if the attribute has a setter 277 * exposed for management. 278 * 279 * @param isIs {@code true} if the attribute's getter is of the 280 * form <code>is<i>XXX</i></code>. 281 * 282 * @param defaultValue must be a valid value 283 * for the {@code 284 * openType} specified for this attribute; default value not 285 * supported for {@code ArrayType} and {@code TabularType}; can 286 * be null, in which case it means that no default value is set. 287 * 288 * @param legalValues each contained value must be valid for the 289 * {@code openType} specified for this attribute; legal values 290 * not supported for {@code ArrayType} and {@code TabularType}; 291 * can be null or empty. 292 * 293 * @param <T> allows the compiler to check that the {@code 294 * defaultValue} and {@code legalValues}, if non-null, have the 295 * correct Java type for the given {@code openType}. 296 * 297 * @throws IllegalArgumentException if {@code name} or {@code 298 * description} are null or empty string, or {@code openType} is 299 * null. 300 * 301 * @throws OpenDataException if {@code defaultValue} is not a 302 * valid value for the specified {@code openType}, or one value in 303 * {@code legalValues} is not valid for the specified {@code 304 * openType}, or {@code defaultValue} is non null and {@code 305 * openType} is an {@code ArrayType} or a {@code TabularType}, or 306 * {@code legalValues} is non null and non empty and {@code 307 * openType} is an {@code ArrayType} or a {@code TabularType}, or 308 * {@code legalValues} is non null and non empty and {@code 309 * defaultValue} is not contained in {@code legalValues}. 310 */ 311 public <T> OpenMBeanAttributeInfoSupport(String name, 312 String description, 313 OpenType<T> openType, 314 boolean isReadable, 315 boolean isWritable, 316 boolean isIs, 317 T defaultValue, 318 T[] legalValues) 319 throws OpenDataException { 320 this(name, description, openType, isReadable, isWritable, isIs, 321 defaultValue, legalValues, null, null); 322 } 323 324 325 /** 326 * Constructs an {@code OpenMBeanAttributeInfoSupport} instance, 327 * which describes the attribute of an open MBean, with the 328 * specified {@code name}, {@code openType}, {@code description}, 329 * {@code defaultValue}, {@code minValue} and {@code maxValue}. 330 * 331 * It is possible to specify minimal and maximal values only for 332 * an open type whose values are {@code Comparable}. 333 * 334 * @param name cannot be a null or empty string. 335 * 336 * @param description cannot be a null or empty string. 337 * 338 * @param openType cannot be null. 339 * 340 * @param isReadable {@code true} if the attribute has a getter 341 * exposed for management. 342 * 343 * @param isWritable {@code true} if the attribute has a setter 344 * exposed for management. 345 * 346 * @param isIs {@code true} if the attribute's getter is of the 347 * form <code>is<i>XXX</i></code>. 348 * 349 * @param defaultValue must be a valid value for the {@code 350 * openType} specified for this attribute; default value not 351 * supported for {@code ArrayType} and {@code TabularType}; can be 352 * null, in which case it means that no default value is set. 353 * 354 * @param minValue must be valid for the {@code openType} 355 * specified for this attribute; can be null, in which case it 356 * means that no minimal value is set. 357 * 358 * @param maxValue must be valid for the {@code openType} 359 * specified for this attribute; can be null, in which case it 360 * means that no maximal value is set. 361 * 362 * @param <T> allows the compiler to check that the {@code 363 * defaultValue}, {@code minValue}, and {@code maxValue}, if 364 * non-null, have the correct Java type for the given {@code 365 * openType}. 366 * 367 * @throws IllegalArgumentException if {@code name} or {@code 368 * description} are null or empty string, or {@code openType} is 369 * null. 370 * 371 * @throws OpenDataException if {@code defaultValue}, {@code 372 * minValue} or {@code maxValue} is not a valid value for the 373 * specified {@code openType}, or {@code defaultValue} is non null 374 * and {@code openType} is an {@code ArrayType} or a {@code 375 * TabularType}, or both {@code minValue} and {@code maxValue} are 376 * non-null and {@code minValue.compareTo(maxValue) > 0} is {@code 377 * true}, or both {@code defaultValue} and {@code minValue} are 378 * non-null and {@code minValue.compareTo(defaultValue) > 0} is 379 * {@code true}, or both {@code defaultValue} and {@code maxValue} 380 * are non-null and {@code defaultValue.compareTo(maxValue) > 0} 381 * is {@code true}. 382 */ 383 public <T> OpenMBeanAttributeInfoSupport(String name, 384 String description, 385 OpenType<T> openType, 386 boolean isReadable, 387 boolean isWritable, 388 boolean isIs, 389 T defaultValue, 390 Comparable<T> minValue, 391 Comparable<T> maxValue) 392 throws OpenDataException { 393 this(name, description, openType, isReadable, isWritable, isIs, 394 defaultValue, null, minValue, maxValue); 395 } 396 397 private <T> OpenMBeanAttributeInfoSupport(String name, 398 String description, 399 OpenType<T> openType, 400 boolean isReadable, 401 boolean isWritable, 402 boolean isIs, 403 T defaultValue, 404 T[] legalValues, 405 Comparable<T> minValue, 406 Comparable<T> maxValue) 407 throws OpenDataException { 408 super(name, 409 (openType==null) ? null : openType.getClassName(), 410 description, 411 isReadable, 412 isWritable, 413 isIs, 414 makeDescriptor(openType, 415 defaultValue, legalValues, minValue, maxValue)); 416 417 this.openType = openType; 418 419 Descriptor d = getDescriptor(); 420 this.defaultValue = defaultValue; 421 this.minValue = minValue; 422 this.maxValue = maxValue; 423 // We already converted the array into an unmodifiable Set 424 // in the descriptor. 425 this.legalValues = (Set<?>) d.getFieldValue("legalValues"); 426 427 check(this); 428 } 429 430 /** 431 * An object serialized in a version of the API before Descriptors were 432 * added to this class will have an empty or null Descriptor. 433 * For consistency with our 434 * behavior in this version, we must replace the object with one 435 * where the Descriptors reflect the same values of openType, defaultValue, 436 * etc. 437 **/ 438 private Object readResolve() { 439 if (getDescriptor().getFieldNames().length == 0) { 440 OpenType<Object> xopenType = cast(openType); 441 Set<Object> xlegalValues = cast(legalValues); 442 Comparable<Object> xminValue = cast(minValue); 443 Comparable<Object> xmaxValue = cast(maxValue); 444 return new OpenMBeanAttributeInfoSupport( 445 name, description, openType, 446 isReadable(), isWritable(), isIs(), 447 makeDescriptor(xopenType, defaultValue, xlegalValues, 448 xminValue, xmaxValue)); 449 } else 450 return this; 451 } 452 453 static void check(OpenMBeanParameterInfo info) throws OpenDataException { 454 OpenType<?> openType = info.getOpenType(); 455 if (openType == null) 456 throw new IllegalArgumentException("OpenType cannot be null"); 457 458 if (info.getName() == null || 459 info.getName().trim().equals("")) 460 throw new IllegalArgumentException("Name cannot be null or empty"); 461 462 if (info.getDescription() == null || 463 info.getDescription().trim().equals("")) 464 throw new IllegalArgumentException("Description cannot be null or empty"); 465 466 // Check and initialize defaultValue 467 // 468 if (info.hasDefaultValue()) { 469 // Default value not supported for ArrayType and TabularType 470 // Cast to Object because "OpenType<T> instanceof" is illegal 471 if (openType.isArray() || (Object)openType instanceof TabularType) { 472 throw new OpenDataException("Default value not supported " + 473 "for ArrayType and TabularType"); 474 } 475 // Check defaultValue's class 476 if (!openType.isValue(info.getDefaultValue())) { 477 final String msg = 478 "Argument defaultValue's class [\"" + 479 info.getDefaultValue().getClass().getName() + 480 "\"] does not match the one defined in openType[\"" + 481 openType.getClassName() +"\"]"; 482 throw new OpenDataException(msg); 483 } 484 } 485 486 // Check that we don't have both legalValues and min or max 487 // 488 if (info.hasLegalValues() && 489 (info.hasMinValue() || info.hasMaxValue())) { 490 throw new OpenDataException("cannot have both legalValue and " + 491 "minValue or maxValue"); 492 } 493 494 // Check minValue and maxValue 495 if (info.hasMinValue() && !openType.isValue(info.getMinValue())) { 496 final String msg = 497 "Type of minValue [" + info.getMinValue().getClass().getName() + 498 "] does not match OpenType [" + openType.getClassName() + "]"; 499 throw new OpenDataException(msg); 500 } 501 if (info.hasMaxValue() && !openType.isValue(info.getMaxValue())) { 502 final String msg = 503 "Type of maxValue [" + info.getMaxValue().getClass().getName() + 504 "] does not match OpenType [" + openType.getClassName() + "]"; 505 throw new OpenDataException(msg); 506 } 507 508 // Check that defaultValue is a legal value 509 // 510 if (info.hasDefaultValue()) { 511 Object defaultValue = info.getDefaultValue(); 512 if (info.hasLegalValues() && 513 !info.getLegalValues().contains(defaultValue)) { 514 throw new OpenDataException("defaultValue is not contained " + 515 "in legalValues"); 516 } 517 518 // Check that minValue <= defaultValue <= maxValue 519 // 520 if (info.hasMinValue()) { 521 if (compare(info.getMinValue(), defaultValue) > 0) { 522 throw new OpenDataException("minValue cannot be greater " + 523 "than defaultValue"); 524 } 525 } 526 if (info.hasMaxValue()) { 527 if (compare(info.getMaxValue(), defaultValue) < 0) { 528 throw new OpenDataException("maxValue cannot be less " + 529 "than defaultValue"); 530 } 531 } 532 } 533 534 // Check legalValues 535 // 536 if (info.hasLegalValues()) { 537 // legalValues not supported for TabularType and arrays 538 if ((Object)openType instanceof TabularType || openType.isArray()) { 539 throw new OpenDataException("Legal values not supported " + 540 "for TabularType and arrays"); 541 } 542 // Check legalValues are valid with openType 543 for (Object v : info.getLegalValues()) { 544 if (!openType.isValue(v)) { 545 final String msg = 546 "Element of legalValues [" + v + 547 "] is not a valid value for the specified openType [" + 548 openType.toString() +"]"; 549 throw new OpenDataException(msg); 550 } 551 } 552 } 553 554 555 // Check that, if both specified, minValue <= maxValue 556 // 557 if (info.hasMinValue() && info.hasMaxValue()) { 558 if (compare(info.getMinValue(), info.getMaxValue()) > 0) { 559 throw new OpenDataException("minValue cannot be greater " + 560 "than maxValue"); 561 } 562 } 563 564 } 565 566 @SuppressWarnings({"unchecked", "rawtypes"}) 567 static int compare(Object x, Object y) { 568 return ((Comparable) x).compareTo(y); 569 } 570 571 static <T> Descriptor makeDescriptor(OpenType<T> openType, 572 T defaultValue, 573 T[] legalValues, 574 Comparable<T> minValue, 575 Comparable<T> maxValue) { 576 Map<String, Object> map = new HashMap<String, Object>(); 577 if (defaultValue != null) 578 map.put("defaultValue", defaultValue); 579 if (legalValues != null) { 580 Set<T> set = new HashSet<T>(); 581 for (T v : legalValues) 582 set.add(v); 583 set = Collections.unmodifiableSet(set); 584 map.put("legalValues", set); 585 } 586 if (minValue != null) 587 map.put("minValue", minValue); 588 if (maxValue != null) 589 map.put("maxValue", maxValue); 590 if (map.isEmpty()) { 591 return openType.getDescriptor(); 592 } else { 593 map.put("openType", openType); 594 return new ImmutableDescriptor(map); 595 } 596 } 597 598 static <T> Descriptor makeDescriptor(OpenType<T> openType, 599 T defaultValue, 600 Set<T> legalValues, 601 Comparable<T> minValue, 602 Comparable<T> maxValue) { 603 T[] legals; 604 if (legalValues == null) 605 legals = null; 606 else { 607 legals = cast(new Object[legalValues.size()]); 608 legalValues.toArray(legals); 609 } 610 return makeDescriptor(openType, defaultValue, legals, minValue, maxValue); 611 } 612 613 614 static <T> T valueFrom(Descriptor d, String name, OpenType<T> openType) { 615 Object x = d.getFieldValue(name); 616 if (x == null) 617 return null; 618 try { 619 return convertFrom(x, openType); 620 } catch (Exception e) { 621 final String msg = 622 "Cannot convert descriptor field " + name + " to " + 623 openType.getTypeName(); 624 throw EnvHelp.initCause(new IllegalArgumentException(msg), e); 625 } 626 } 627 628 static <T> Set<T> valuesFrom(Descriptor d, String name, 629 OpenType<T> openType) { 630 Object x = d.getFieldValue(name); 631 if (x == null) 632 return null; 633 Collection<?> coll; 634 if (x instanceof Set<?>) { 635 Set<?> set = (Set<?>) x; 636 boolean asis = true; 637 for (Object element : set) { 638 if (!openType.isValue(element)) { 639 asis = false; 640 break; 641 } 642 } 643 if (asis) 644 return cast(set); 645 coll = set; 646 } else if (x instanceof Object[]) { 647 coll = Arrays.asList((Object[]) x); 648 } else { 649 final String msg = 650 "Descriptor value for " + name + " must be a Set or " + 651 "an array: " + x.getClass().getName(); 652 throw new IllegalArgumentException(msg); 653 } 654 655 Set<T> result = new HashSet<T>(); 656 for (Object element : coll) 657 result.add(convertFrom(element, openType)); 658 return result; 659 } 660 661 static <T> Comparable<?> comparableValueFrom(Descriptor d, String name, 662 OpenType<T> openType) { 663 T t = valueFrom(d, name, openType); 664 if (t == null || t instanceof Comparable<?>) 665 return (Comparable<?>) t; 666 final String msg = 667 "Descriptor field " + name + " with value " + t + 668 " is not Comparable"; 669 throw new IllegalArgumentException(msg); 670 } 671 672 private static <T> T convertFrom(Object x, OpenType<T> openType) { 673 if (openType.isValue(x)) { 674 T t = OpenMBeanAttributeInfoSupport.<T>cast(x); 675 return t; 676 } 677 return convertFromStrings(x, openType); 678 } 679 680 private static <T> T convertFromStrings(Object x, OpenType<T> openType) { 681 if (openType instanceof ArrayType<?>) 682 return convertFromStringArray(x, openType); 683 else if (x instanceof String) 684 return convertFromString((String) x, openType); 685 final String msg = 686 "Cannot convert value " + x + " of type " + 687 x.getClass().getName() + " to type " + openType.getTypeName(); 688 throw new IllegalArgumentException(msg); 689 } 690 691 private static <T> T convertFromString(String s, OpenType<T> openType) { 692 Class<T> c; 693 try { 694 String className = openType.safeGetClassName(); 695 ReflectUtil.checkPackageAccess(className); 696 c = cast(Class.forName(className)); 697 } catch (ClassNotFoundException e) { 698 throw new NoClassDefFoundError(e.toString()); // can't happen 699 } 700 701 // Look for: public static T valueOf(String) 702 Method valueOf; 703 try { 704 // It is safe to call this plain Class.getMethod because the class "c" 705 // was checked before by ReflectUtil.checkPackageAccess(openType.safeGetClassName()); 706 valueOf = c.getMethod("valueOf", String.class); 707 if (!Modifier.isStatic(valueOf.getModifiers()) || 708 valueOf.getReturnType() != c) 709 valueOf = null; 710 } catch (NoSuchMethodException e) { 711 valueOf = null; 712 } 713 if (valueOf != null) { 714 try { 715 return c.cast(MethodUtil.invoke(valueOf, null, new Object[] {s})); 716 } catch (Exception e) { 717 final String msg = 718 "Could not convert \"" + s + "\" using method: " + valueOf; 719 throw new IllegalArgumentException(msg, e); 720 } 721 } 722 723 // Look for: public T(String) 724 Constructor<T> con; 725 try { 726 // It is safe to call this plain Class.getConstructor because the class "c" 727 // was checked before by ReflectUtil.checkPackageAccess(openType.safeGetClassName()); 728 con = c.getConstructor(String.class); 729 } catch (NoSuchMethodException e) { 730 con = null; 731 } 732 if (con != null) { 733 try { 734 return con.newInstance(s); 735 } catch (Exception e) { 736 final String msg = 737 "Could not convert \"" + s + "\" using constructor: " + con; 738 throw new IllegalArgumentException(msg, e); 739 } 740 } 741 742 throw new IllegalArgumentException("Don't know how to convert " + 743 "string to " + 744 openType.getTypeName()); 745 } 746 747 748 /* A Descriptor contained an array value encoded as Strings. The 749 Strings must be organized in an array corresponding to the desired 750 array. If the desired array has n dimensions, so must the String 751 array. We will convert element by element from String to desired 752 component type. */ 753 private static <T> T convertFromStringArray(Object x, 754 OpenType<T> openType) { 755 ArrayType<?> arrayType = (ArrayType<?>) openType; 756 OpenType<?> baseType = arrayType.getElementOpenType(); 757 int dim = arrayType.getDimension(); 758 String squareBrackets = "["; 759 for (int i = 1; i < dim; i++) 760 squareBrackets += "["; 761 Class<?> stringArrayClass; 762 Class<?> targetArrayClass; 763 try { 764 String baseClassName = baseType.safeGetClassName(); 765 766 // check access to the provided base type class name and bail out early 767 ReflectUtil.checkPackageAccess(baseClassName); 768 769 stringArrayClass = 770 Class.forName(squareBrackets + "Ljava.lang.String;"); 771 targetArrayClass = 772 Class.forName(squareBrackets + "L" + baseClassName + ";"); 773 } catch (ClassNotFoundException e) { 774 throw new NoClassDefFoundError(e.toString()); // can't happen 775 } 776 if (!stringArrayClass.isInstance(x)) { 777 final String msg = 778 "Value for " + dim + "-dimensional array of " + 779 baseType.getTypeName() + " must be same type or a String " + 780 "array with same dimensions"; 781 throw new IllegalArgumentException(msg); 782 } 783 OpenType<?> componentOpenType; 784 if (dim == 1) 785 componentOpenType = baseType; 786 else { 787 try { 788 componentOpenType = new ArrayType<T>(dim - 1, baseType); 789 } catch (OpenDataException e) { 790 throw new IllegalArgumentException(e.getMessage(), e); 791 // can't happen 792 } 793 } 794 int n = Array.getLength(x); 795 Object[] targetArray = (Object[]) 796 Array.newInstance(targetArrayClass.getComponentType(), n); 797 for (int i = 0; i < n; i++) { 798 Object stringish = Array.get(x, i); // String or String[] etc 799 Object converted = 800 convertFromStrings(stringish, componentOpenType); 801 Array.set(targetArray, i, converted); 802 } 803 return OpenMBeanAttributeInfoSupport.<T>cast(targetArray); 804 } 805 806 @SuppressWarnings("unchecked") 807 static <T> T cast(Object x) { 808 return (T) x; 809 } 810 811 /** 812 * Returns the open type for the values of the attribute described 813 * by this {@code OpenMBeanAttributeInfoSupport} instance. 814 */ 815 public OpenType<?> getOpenType() { 816 return openType; 817 } 818 819 /** 820 * Returns the default value for the attribute described by this 821 * {@code OpenMBeanAttributeInfoSupport} instance, if specified, 822 * or {@code null} otherwise. 823 */ 824 public Object getDefaultValue() { 825 826 // Special case for ArrayType and TabularType 827 // [JF] TODO: clone it so that it cannot be altered, 828 // [JF] TODO: if we decide to support defaultValue as an array itself. 829 // [JF] As of today (oct 2000) it is not supported so 830 // defaultValue is null for arrays. Nothing to do. 831 832 return defaultValue; 833 } 834 835 /** 836 * Returns an unmodifiable Set of legal values for the attribute 837 * described by this {@code OpenMBeanAttributeInfoSupport} 838 * instance, if specified, or {@code null} otherwise. 839 */ 840 public Set<?> getLegalValues() { 841 842 // Special case for ArrayType and TabularType 843 // [JF] TODO: clone values so that they cannot be altered, 844 // [JF] TODO: if we decide to support LegalValues as an array itself. 845 // [JF] As of today (oct 2000) it is not supported so 846 // legalValues is null for arrays. Nothing to do. 847 848 // Returns our legalValues Set (set was constructed unmodifiable) 849 return (legalValues); 850 } 851 852 /** 853 * Returns the minimal value for the attribute described by this 854 * {@code OpenMBeanAttributeInfoSupport} instance, if specified, 855 * or {@code null} otherwise. 856 */ 857 public Comparable<?> getMinValue() { 858 859 // Note: only comparable values have a minValue, 860 // so that's not the case of arrays and tabulars (always null). 861 862 return minValue; 863 } 864 865 /** 866 * Returns the maximal value for the attribute described by this 867 * {@code OpenMBeanAttributeInfoSupport} instance, if specified, 868 * or {@code null} otherwise. 869 */ 870 public Comparable<?> getMaxValue() { 871 872 // Note: only comparable values have a maxValue, 873 // so that's not the case of arrays and tabulars (always null). 874 875 return maxValue; 876 } 877 878 /** 879 * Returns {@code true} if this {@code 880 * OpenMBeanAttributeInfoSupport} instance specifies a non-null 881 * default value for the described attribute, {@code false} 882 * otherwise. 883 */ 884 public boolean hasDefaultValue() { 885 886 return (defaultValue != null); 887 } 888 889 /** 890 * Returns {@code true} if this {@code 891 * OpenMBeanAttributeInfoSupport} instance specifies a non-null 892 * set of legal values for the described attribute, {@code false} 893 * otherwise. 894 */ 895 public boolean hasLegalValues() { 896 897 return (legalValues != null); 898 } 899 900 /** 901 * Returns {@code true} if this {@code 902 * OpenMBeanAttributeInfoSupport} instance specifies a non-null 903 * minimal value for the described attribute, {@code false} 904 * otherwise. 905 */ 906 public boolean hasMinValue() { 907 908 return (minValue != null); 909 } 910 911 /** 912 * Returns {@code true} if this {@code 913 * OpenMBeanAttributeInfoSupport} instance specifies a non-null 914 * maximal value for the described attribute, {@code false} 915 * otherwise. 916 */ 917 public boolean hasMaxValue() { 918 919 return (maxValue != null); 920 } 921 922 923 /** 924 * Tests whether {@code obj} is a valid value for the attribute 925 * described by this {@code OpenMBeanAttributeInfoSupport} 926 * instance. 927 * 928 * @param obj the object to be tested. 929 * 930 * @return {@code true} if {@code obj} is a valid value for 931 * the parameter described by this {@code 932 * OpenMBeanAttributeInfoSupport} instance, {@code false} 933 * otherwise. 934 */ 935 public boolean isValue(Object obj) { 936 return isValue(this, obj); 937 } 938 939 @SuppressWarnings({"unchecked", "rawtypes"}) // cast to Comparable 940 static boolean isValue(OpenMBeanParameterInfo info, Object obj) { 941 if (info.hasDefaultValue() && obj == null) 942 return true; 943 return 944 info.getOpenType().isValue(obj) && 945 (!info.hasLegalValues() || info.getLegalValues().contains(obj)) && 946 (!info.hasMinValue() || 947 ((Comparable) info.getMinValue()).compareTo(obj) <= 0) && 948 (!info.hasMaxValue() || 949 ((Comparable) info.getMaxValue()).compareTo(obj) >= 0); 950 } 951 952 /* *** Commodity methods from java.lang.Object *** */ 953 954 955 /** 956 * Compares the specified {@code obj} parameter with this {@code 957 * OpenMBeanAttributeInfoSupport} instance for equality. 958 * <p> 959 * Returns {@code true} if and only if all of the following statements are true: 960 * <ul> 961 * <li>{@code obj} is non null,</li> 962 * <li>{@code obj} also implements the {@code OpenMBeanAttributeInfo} interface,</li> 963 * <li>their names are equal</li> 964 * <li>their open types are equal</li> 965 * <li>their access properties (isReadable, isWritable and isIs) are equal</li> 966 * <li>their default, min, max and legal values are equal.</li> 967 * </ul> 968 * This ensures that this {@code equals} method works properly for 969 * {@code obj} parameters which are different implementations of 970 * the {@code OpenMBeanAttributeInfo} interface. 971 * 972 * <p>If {@code obj} also implements {@link DescriptorRead}, then its 973 * {@link DescriptorRead#getDescriptor() getDescriptor()} method must 974 * also return the same value as for this object.</p> 975 * 976 * @param obj the object to be compared for equality with this 977 * {@code OpenMBeanAttributeInfoSupport} instance. 978 * 979 * @return {@code true} if the specified object is equal to this 980 * {@code OpenMBeanAttributeInfoSupport} instance. 981 */ 982 public boolean equals(Object obj) { 983 if (!(obj instanceof OpenMBeanAttributeInfo)) 984 return false; 985 986 OpenMBeanAttributeInfo other = (OpenMBeanAttributeInfo) obj; 987 988 return 989 this.isReadable() == other.isReadable() && 990 this.isWritable() == other.isWritable() && 991 this.isIs() == other.isIs() && 992 equal(this, other); 993 } 994 995 static boolean equal(OpenMBeanParameterInfo x1, OpenMBeanParameterInfo x2) { 996 if (x1 instanceof DescriptorRead) { 997 if (!(x2 instanceof DescriptorRead)) 998 return false; 999 Descriptor d1 = ((DescriptorRead) x1).getDescriptor(); 1000 Descriptor d2 = ((DescriptorRead) x2).getDescriptor(); 1001 if (!d1.equals(d2)) 1002 return false; 1003 } else if (x2 instanceof DescriptorRead) 1004 return false; 1005 1006 return 1007 x1.getName().equals(x2.getName()) && 1008 x1.getOpenType().equals(x2.getOpenType()) && 1009 (x1.hasDefaultValue() ? 1010 x1.getDefaultValue().equals(x2.getDefaultValue()) : 1011 !x2.hasDefaultValue()) && 1012 (x1.hasMinValue() ? 1013 x1.getMinValue().equals(x2.getMinValue()) : 1014 !x2.hasMinValue()) && 1015 (x1.hasMaxValue() ? 1016 x1.getMaxValue().equals(x2.getMaxValue()) : 1017 !x2.hasMaxValue()) && 1018 (x1.hasLegalValues() ? 1019 x1.getLegalValues().equals(x2.getLegalValues()) : 1020 !x2.hasLegalValues()); 1021 } 1022 1023 /** 1024 * <p>Returns the hash code value for this {@code 1025 * OpenMBeanAttributeInfoSupport} instance.</p> 1026 * 1027 * <p>The hash code of an {@code OpenMBeanAttributeInfoSupport} 1028 * instance is the sum of the hash codes of all elements of 1029 * information used in {@code equals} comparisons (ie: its name, 1030 * its <i>open type</i>, its default, min, max and legal 1031 * values, and its Descriptor). 1032 * 1033 * <p>This ensures that {@code t1.equals(t2)} implies that {@code 1034 * t1.hashCode()==t2.hashCode()} for any two {@code 1035 * OpenMBeanAttributeInfoSupport} instances {@code t1} and {@code 1036 * t2}, as required by the general contract of the method {@link 1037 * Object#hashCode() Object.hashCode()}. 1038 * 1039 * <p>However, note that another instance of a class implementing 1040 * the {@code OpenMBeanAttributeInfo} interface may be equal to 1041 * this {@code OpenMBeanAttributeInfoSupport} instance as defined 1042 * by {@link #equals(java.lang.Object)}, but may have a different 1043 * hash code if it is calculated differently. 1044 * 1045 * <p>As {@code OpenMBeanAttributeInfoSupport} instances are 1046 * immutable, the hash code for this instance is calculated once, 1047 * on the first call to {@code hashCode}, and then the same value 1048 * is returned for subsequent calls. 1049 * 1050 * @return the hash code value for this {@code 1051 * OpenMBeanAttributeInfoSupport} instance 1052 */ 1053 public int hashCode() { 1054 1055 // Calculate the hash code value if it has not yet been done 1056 // (ie 1st call to hashCode()) 1057 // 1058 if (myHashCode == null) 1059 myHashCode = hashCode(this); 1060 1061 // return always the same hash code for this instance (immutable) 1062 // 1063 return myHashCode.intValue(); 1064 } 1065 1066 static int hashCode(OpenMBeanParameterInfo info) { 1067 int value = 0; 1068 value += info.getName().hashCode(); 1069 value += info.getOpenType().hashCode(); 1070 if (info.hasDefaultValue()) 1071 value += info.getDefaultValue().hashCode(); 1072 if (info.hasMinValue()) 1073 value += info.getMinValue().hashCode(); 1074 if (info.hasMaxValue()) 1075 value += info.getMaxValue().hashCode(); 1076 if (info.hasLegalValues()) 1077 value += info.getLegalValues().hashCode(); 1078 if (info instanceof DescriptorRead) 1079 value += ((DescriptorRead) info).getDescriptor().hashCode(); 1080 return value; 1081 } 1082 1083 /** 1084 * Returns a string representation of this 1085 * {@code OpenMBeanAttributeInfoSupport} instance. 1086 * <p> 1087 * The string representation consists of the name of this class (i.e. 1088 * {@code javax.management.openmbean.OpenMBeanAttributeInfoSupport}), 1089 * the string representation of the name and open type of the 1090 * described parameter, the string representation of its 1091 * default, min, max and legal values and the string 1092 * representation of its descriptor. 1093 * 1094 * <p>As {@code OpenMBeanAttributeInfoSupport} instances are 1095 * immutable, the string representation for this instance is 1096 * calculated once, on the first call to {@code toString}, and 1097 * then the same value is returned for subsequent calls. 1098 * 1099 * @return a string representation of this 1100 * {@code OpenMBeanAttributeInfoSupport} instance. 1101 */ 1102 public String toString() { 1103 1104 // Calculate the string value if it has not yet been done 1105 // (ie 1st call to toString()) 1106 // 1107 if (myToString == null) 1108 myToString = toString(this); 1109 1110 // return always the same string representation for this 1111 // instance (immutable) 1112 // 1113 return myToString; 1114 } 1115 1116 static String toString(OpenMBeanParameterInfo info) { 1117 Descriptor d = (info instanceof DescriptorRead) ? 1118 ((DescriptorRead) info).getDescriptor() : null; 1119 return 1120 info.getClass().getName() + 1121 "(name=" + info.getName() + 1122 ",openType=" + info.getOpenType() + 1123 ",default=" + info.getDefaultValue() + 1124 ",minValue=" + info.getMinValue() + 1125 ",maxValue=" + info.getMaxValue() + 1126 ",legalValues=" + info.getLegalValues() + 1127 ((d == null) ? "" : ",descriptor=" + d) + 1128 ")"; 1129 } 1130 }