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