0001 /*
0002 * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.management.openmbean;
0027
0028 // java import
0029 //
0030 import java.lang.reflect.Array;
0031 import java.lang.reflect.Constructor;
0032 import java.lang.reflect.Method;
0033 import java.lang.reflect.Modifier;
0034 import java.util.Arrays;
0035 import java.util.Collection;
0036 import java.util.Collections;
0037 import java.util.HashMap;
0038 import java.util.HashSet;
0039 import java.util.Map;
0040 import java.util.Set;
0041 import javax.management.Descriptor;
0042 import javax.management.DescriptorRead;
0043 import javax.management.ImmutableDescriptor;
0044 import javax.management.MBeanAttributeInfo;
0045 import com.sun.jmx.remote.util.EnvHelp;
0046
0047 /**
0048 * Describes an attribute of an open MBean.
0049 *
0050 *
0051 * @since 1.5
0052 */
0053 public class OpenMBeanAttributeInfoSupport extends MBeanAttributeInfo
0054 implements OpenMBeanAttributeInfo {
0055
0056 /* Serial version */
0057 static final long serialVersionUID = -4867215622149721849L;
0058
0059 /**
0060 * @serial The open mbean attribute's <i>open type</i>
0061 */
0062 private OpenType<?> openType;
0063
0064 /**
0065 * @serial The open mbean attribute's default value
0066 */
0067 private final Object defaultValue;
0068
0069 /**
0070 * @serial The open mbean attribute's legal values. This {@link
0071 * Set} is unmodifiable
0072 */
0073 private final Set<?> legalValues; // to be constructed unmodifiable
0074
0075 /**
0076 * @serial The open mbean attribute's min value
0077 */
0078 private final Comparable minValue;
0079
0080 /**
0081 * @serial The open mbean attribute's max value
0082 */
0083 private final Comparable maxValue;
0084
0085 // As this instance is immutable, these two values need only
0086 // be calculated once.
0087 private transient Integer myHashCode = null;
0088 private transient String myToString = null;
0089
0090 /**
0091 * Constructs an {@code OpenMBeanAttributeInfoSupport} instance,
0092 * which describes the attribute of an open MBean with the
0093 * specified {@code name}, {@code openType} and {@code
0094 * description}, and the specified read/write access properties.
0095 *
0096 * @param name cannot be a null or empty string.
0097 *
0098 * @param description cannot be a null or empty string.
0099 *
0100 * @param openType cannot be null.
0101 *
0102 * @param isReadable {@code true} if the attribute has a getter
0103 * exposed for management.
0104 *
0105 * @param isWritable {@code true} if the attribute has a setter
0106 * exposed for management.
0107 *
0108 * @param isIs {@code true} if the attribute's getter is of the
0109 * form <tt>is<i>XXX</i></tt>.
0110 *
0111 * @throws IllegalArgumentException if {@code name} or {@code
0112 * description} are null or empty string, or {@code openType} is
0113 * null.
0114 */
0115 public OpenMBeanAttributeInfoSupport(String name,
0116 String description, OpenType<?> openType,
0117 boolean isReadable, boolean isWritable, boolean isIs) {
0118 this (name, description, openType, isReadable, isWritable, isIs,
0119 (Descriptor) null);
0120 }
0121
0122 /**
0123 * <p>Constructs an {@code OpenMBeanAttributeInfoSupport} instance,
0124 * which describes the attribute of an open MBean with the
0125 * specified {@code name}, {@code openType}, {@code
0126 * description}, read/write access properties, and {@code Descriptor}.</p>
0127 *
0128 * <p>The {@code descriptor} can contain entries that will define
0129 * the values returned by certain methods of this class, as
0130 * explained in the {@link <a href="package-summary.html#constraints">
0131 * package description</a>}.
0132 *
0133 * @param name cannot be a null or empty string.
0134 *
0135 * @param description cannot be a null or empty string.
0136 *
0137 * @param openType cannot be null.
0138 *
0139 * @param isReadable {@code true} if the attribute has a getter
0140 * exposed for management.
0141 *
0142 * @param isWritable {@code true} if the attribute has a setter
0143 * exposed for management.
0144 *
0145 * @param isIs {@code true} if the attribute's getter is of the
0146 * form <tt>is<i>XXX</i></tt>.
0147 *
0148 * @param descriptor The descriptor for the attribute. This may be null
0149 * which is equivalent to an empty descriptor.
0150 *
0151 * @throws IllegalArgumentException if {@code name} or {@code
0152 * description} are null or empty string, or {@code openType} is
0153 * null, or the descriptor entries are invalid as described in the
0154 * {@link <a href="package-summary.html#constraints">package
0155 * description</a>}.
0156 *
0157 * @since 1.6
0158 */
0159 public OpenMBeanAttributeInfoSupport(String name,
0160 String description, OpenType<?> openType,
0161 boolean isReadable, boolean isWritable, boolean isIs,
0162 Descriptor descriptor) {
0163 // Construct parent's state
0164 //
0165 super (name,
0166 (openType == null) ? null : openType.getClassName(),
0167 description, isReadable, isWritable, isIs,
0168 ImmutableDescriptor.union(descriptor,
0169 (openType == null) ? null : openType
0170 .getDescriptor()));
0171
0172 // Initialize this instance's specific state
0173 //
0174 this .openType = openType;
0175
0176 descriptor = getDescriptor(); // replace null by empty
0177 this .defaultValue = valueFrom(descriptor, "defaultValue",
0178 openType);
0179 this .legalValues = valuesFrom(descriptor, "legalValues",
0180 openType);
0181 this .minValue = comparableValueFrom(descriptor, "minValue",
0182 openType);
0183 this .maxValue = comparableValueFrom(descriptor, "maxValue",
0184 openType);
0185
0186 try {
0187 check(this );
0188 } catch (OpenDataException e) {
0189 throw new IllegalArgumentException(e.getMessage(), e);
0190 }
0191 }
0192
0193 /**
0194 * Constructs an {@code OpenMBeanAttributeInfoSupport} instance,
0195 * which describes the attribute of an open MBean with the
0196 * specified {@code name}, {@code openType}, {@code description}
0197 * and {@code defaultValue}, and the specified read/write access
0198 * properties.
0199 *
0200 * @param name cannot be a null or empty string.
0201 *
0202 * @param description cannot be a null or empty string.
0203 *
0204 * @param openType cannot be null.
0205 *
0206 * @param isReadable {@code true} if the attribute has a getter
0207 * exposed for management.
0208 *
0209 * @param isWritable {@code true} if the attribute has a setter
0210 * exposed for management.
0211 *
0212 * @param isIs {@code true} if the attribute's getter is of the
0213 * form <tt>is<i>XXX</i></tt>.
0214 *
0215 * @param defaultValue must be a valid value for the {@code
0216 * openType} specified for this attribute; default value not
0217 * supported for {@code ArrayType} and {@code TabularType}; can
0218 * be null, in which case it means that no default value is set.
0219 *
0220 * @param <T> allows the compiler to check that the {@code defaultValue},
0221 * if non-null, has the correct Java type for the given {@code openType}.
0222 *
0223 * @throws IllegalArgumentException if {@code name} or {@code
0224 * description} are null or empty string, or {@code openType} is
0225 * null.
0226 *
0227 * @throws OpenDataException if {@code defaultValue} is not a
0228 * valid value for the specified {@code openType}, or {@code
0229 * defaultValue} is non null and {@code openType} is an {@code
0230 * ArrayType} or a {@code TabularType}.
0231 */
0232 public <T> OpenMBeanAttributeInfoSupport(String name,
0233 String description, OpenType<T> openType,
0234 boolean isReadable, boolean isWritable, boolean isIs,
0235 T defaultValue) throws OpenDataException {
0236 this (name, description, openType, isReadable, isWritable, isIs,
0237 defaultValue, (T[]) null);
0238 }
0239
0240 /**
0241 * <p>Constructs an {@code OpenMBeanAttributeInfoSupport} instance,
0242 * which describes the attribute of an open MBean with the
0243 * specified {@code name}, {@code openType}, {@code description},
0244 * {@code defaultValue} and {@code legalValues}, and the specified
0245 * read/write access properties.</p>
0246 *
0247 * <p>The contents of {@code legalValues} are copied, so subsequent
0248 * modifications of the array referenced by {@code legalValues}
0249 * have no impact on this {@code OpenMBeanAttributeInfoSupport}
0250 * instance.</p>
0251 *
0252 * @param name cannot be a null or empty string.
0253 *
0254 * @param description cannot be a null or empty string.
0255 *
0256 * @param openType cannot be null.
0257 *
0258 * @param isReadable {@code true} if the attribute has a getter
0259 * exposed for management.
0260 *
0261 * @param isWritable {@code true} if the attribute has a setter
0262 * exposed for management.
0263 *
0264 * @param isIs {@code true} if the attribute's getter is of the
0265 * form <tt>is<i>XXX</i></tt>.
0266 *
0267 * @param defaultValue must be a valid value
0268 * for the {@code
0269 * openType} specified for this attribute; default value not
0270 * supported for {@code ArrayType} and {@code TabularType}; can
0271 * be null, in which case it means that no default value is set.
0272 *
0273 * @param legalValues each contained value must be valid for the
0274 * {@code openType} specified for this attribute; legal values
0275 * not supported for {@code ArrayType} and {@code TabularType};
0276 * can be null or empty.
0277 *
0278 * @param <T> allows the compiler to check that the {@code
0279 * defaultValue} and {@code legalValues}, if non-null, have the
0280 * correct Java type for the given {@code openType}.
0281 *
0282 * @throws IllegalArgumentException if {@code name} or {@code
0283 * description} are null or empty string, or {@code openType} is
0284 * null.
0285 *
0286 * @throws OpenDataException if {@code defaultValue} is not a
0287 * valid value for the specified {@code openType}, or one value in
0288 * {@code legalValues} is not valid for the specified {@code
0289 * openType}, or {@code defaultValue} is non null and {@code
0290 * openType} is an {@code ArrayType} or a {@code TabularType}, or
0291 * {@code legalValues} is non null and non empty and {@code
0292 * openType} is an {@code ArrayType} or a {@code TabularType}, or
0293 * {@code legalValues} is non null and non empty and {@code
0294 * defaultValue} is not contained in {@code legalValues}.
0295 */
0296 public <T> OpenMBeanAttributeInfoSupport(String name,
0297 String description, OpenType<T> openType,
0298 boolean isReadable, boolean isWritable, boolean isIs,
0299 T defaultValue, T[] legalValues) throws OpenDataException {
0300 this (name, description, openType, isReadable, isWritable, isIs,
0301 defaultValue, legalValues, null, null);
0302 }
0303
0304 /**
0305 * Constructs an {@code OpenMBeanAttributeInfoSupport} instance,
0306 * which describes the attribute of an open MBean, with the
0307 * specified {@code name}, {@code openType}, {@code description},
0308 * {@code defaultValue}, {@code minValue} and {@code maxValue}.
0309 *
0310 * It is possible to specify minimal and maximal values only for
0311 * an open type whose values are {@code Comparable}.
0312 *
0313 * @param name cannot be a null or empty string.
0314 *
0315 * @param description cannot be a null or empty string.
0316 *
0317 * @param openType cannot be null.
0318 *
0319 * @param isReadable {@code true} if the attribute has a getter
0320 * exposed for management.
0321 *
0322 * @param isWritable {@code true} if the attribute has a setter
0323 * exposed for management.
0324 *
0325 * @param isIs {@code true} if the attribute's getter is of the
0326 * form <tt>is<i>XXX</i></tt>.
0327 *
0328 * @param defaultValue must be a valid value for the {@code
0329 * openType} specified for this attribute; default value not
0330 * supported for {@code ArrayType} and {@code TabularType}; can be
0331 * null, in which case it means that no default value is set.
0332 *
0333 * @param minValue must be valid for the {@code openType}
0334 * specified for this attribute; can be null, in which case it
0335 * means that no minimal value is set.
0336 *
0337 * @param maxValue must be valid for the {@code openType}
0338 * specified for this attribute; can be null, in which case it
0339 * means that no maximal value is set.
0340 *
0341 * @param <T> allows the compiler to check that the {@code
0342 * defaultValue}, {@code minValue}, and {@code maxValue}, if
0343 * non-null, have the correct Java type for the given {@code
0344 * openType}.
0345 *
0346 * @throws IllegalArgumentException if {@code name} or {@code
0347 * description} are null or empty string, or {@code openType} is
0348 * null.
0349 *
0350 * @throws OpenDataException if {@code defaultValue}, {@code
0351 * minValue} or {@code maxValue} is not a valid value for the
0352 * specified {@code openType}, or {@code defaultValue} is non null
0353 * and {@code openType} is an {@code ArrayType} or a {@code
0354 * TabularType}, or both {@code minValue} and {@code maxValue} are
0355 * non-null and {@code minValue.compareTo(maxValue) > 0} is {@code
0356 * true}, or both {@code defaultValue} and {@code minValue} are
0357 * non-null and {@code minValue.compareTo(defaultValue) > 0} is
0358 * {@code true}, or both {@code defaultValue} and {@code maxValue}
0359 * are non-null and {@code defaultValue.compareTo(maxValue) > 0}
0360 * is {@code true}.
0361 */
0362 public <T> OpenMBeanAttributeInfoSupport(String name,
0363 String description, OpenType<T> openType,
0364 boolean isReadable, boolean isWritable, boolean isIs,
0365 T defaultValue, Comparable<T> minValue,
0366 Comparable<T> maxValue) throws OpenDataException {
0367 this (name, description, openType, isReadable, isWritable, isIs,
0368 defaultValue, null, minValue, maxValue);
0369 }
0370
0371 private <T> OpenMBeanAttributeInfoSupport(String name,
0372 String description, OpenType<T> openType,
0373 boolean isReadable, boolean isWritable, boolean isIs,
0374 T defaultValue, T[] legalValues, Comparable<T> minValue,
0375 Comparable<T> maxValue) throws OpenDataException {
0376 super (name,
0377 (openType == null) ? null : openType.getClassName(),
0378 description, isReadable, isWritable, isIs,
0379 makeDescriptor(openType, defaultValue, legalValues,
0380 minValue, maxValue));
0381
0382 this .openType = openType;
0383
0384 Descriptor d = getDescriptor();
0385 this .defaultValue = defaultValue;
0386 this .minValue = minValue;
0387 this .maxValue = maxValue;
0388 // We already converted the array into an unmodifiable Set
0389 // in the descriptor.
0390 this .legalValues = (Set<?>) d.getFieldValue("legalValues");
0391
0392 check(this );
0393 }
0394
0395 /**
0396 * An object serialized in a version of the API before Descriptors were
0397 * added to this class will have an empty or null Descriptor.
0398 * For consistency with our
0399 * behavior in this version, we must replace the object with one
0400 * where the Descriptors reflect the same values of openType, defaultValue,
0401 * etc.
0402 **/
0403 private Object readResolve() {
0404 if (getDescriptor().getFieldNames().length == 0) {
0405 OpenType<Object> xopenType = cast(openType);
0406 Set<Object> xlegalValues = cast(legalValues);
0407 Comparable<Object> xminValue = cast(minValue);
0408 Comparable<Object> xmaxValue = cast(maxValue);
0409 return new OpenMBeanAttributeInfoSupport(name, description,
0410 openType, isReadable(), isWritable(), isIs(),
0411 makeDescriptor(xopenType, defaultValue,
0412 xlegalValues, xminValue, xmaxValue));
0413 } else
0414 return this ;
0415 }
0416
0417 static void check(OpenMBeanParameterInfo info)
0418 throws OpenDataException {
0419 OpenType openType = info.getOpenType();
0420 if (openType == null)
0421 throw new IllegalArgumentException(
0422 "OpenType cannot be null");
0423
0424 if (info.getName() == null || info.getName().trim().equals(""))
0425 throw new IllegalArgumentException(
0426 "Name cannot be null or empty");
0427
0428 if (info.getDescription() == null
0429 || info.getDescription().trim().equals(""))
0430 throw new IllegalArgumentException(
0431 "Description cannot be null or empty");
0432
0433 // Check and initialize defaultValue
0434 //
0435 if (info.hasDefaultValue()) {
0436 // Default value not supported for ArrayType and TabularType
0437 // Cast to Object because "OpenType<T> instanceof" is illegal
0438 if (openType.isArray()
0439 || (Object) openType instanceof TabularType) {
0440 throw new OpenDataException(
0441 "Default value not supported "
0442 + "for ArrayType and TabularType");
0443 }
0444 // Check defaultValue's class
0445 if (!openType.isValue(info.getDefaultValue())) {
0446 final String msg = "Argument defaultValue's class [\""
0447 + info.getDefaultValue().getClass().getName()
0448 + "\"] does not match the one defined in openType[\""
0449 + openType.getClassName() + "\"]";
0450 throw new OpenDataException(msg);
0451 }
0452 }
0453
0454 // Check that we don't have both legalValues and min or max
0455 //
0456 if (info.hasLegalValues()
0457 && (info.hasMinValue() || info.hasMaxValue())) {
0458 throw new OpenDataException(
0459 "cannot have both legalValue and "
0460 + "minValue or maxValue");
0461 }
0462
0463 // Check minValue and maxValue
0464 if (info.hasMinValue() && !openType.isValue(info.getMinValue())) {
0465 final String msg = "Type of minValue ["
0466 + info.getMinValue().getClass().getName()
0467 + "] does not match OpenType ["
0468 + openType.getClassName() + "]";
0469 throw new OpenDataException(msg);
0470 }
0471 if (info.hasMaxValue() && !openType.isValue(info.getMaxValue())) {
0472 final String msg = "Type of maxValue ["
0473 + info.getMaxValue().getClass().getName()
0474 + "] does not match OpenType ["
0475 + openType.getClassName() + "]";
0476 throw new OpenDataException(msg);
0477 }
0478
0479 // Check that defaultValue is a legal value
0480 //
0481 if (info.hasDefaultValue()) {
0482 Object defaultValue = info.getDefaultValue();
0483 if (info.hasLegalValues()
0484 && !info.getLegalValues().contains(defaultValue)) {
0485 throw new OpenDataException(
0486 "defaultValue is not contained "
0487 + "in legalValues");
0488 }
0489
0490 // Check that minValue <= defaultValue <= maxValue
0491 //
0492 if (info.hasMinValue()) {
0493 if (compare(info.getMinValue(), defaultValue) > 0) {
0494 throw new OpenDataException(
0495 "minValue cannot be greater "
0496 + "than defaultValue");
0497 }
0498 }
0499 if (info.hasMaxValue()) {
0500 if (compare(info.getMaxValue(), defaultValue) < 0) {
0501 throw new OpenDataException(
0502 "maxValue cannot be less "
0503 + "than defaultValue");
0504 }
0505 }
0506 }
0507
0508 // Check legalValues
0509 //
0510 if (info.hasLegalValues()) {
0511 // legalValues not supported for TabularType and arrays
0512 if ((Object) openType instanceof TabularType
0513 || openType.isArray()) {
0514 throw new OpenDataException(
0515 "Legal values not supported "
0516 + "for TabularType and arrays");
0517 }
0518 // Check legalValues are valid with openType
0519 for (Object v : info.getLegalValues()) {
0520 if (!openType.isValue(v)) {
0521 final String msg = "Element of legalValues ["
0522 + v
0523 + "] is not a valid value for the specified openType ["
0524 + openType.toString() + "]";
0525 throw new OpenDataException(msg);
0526 }
0527 }
0528 }
0529
0530 // Check that, if both specified, minValue <= maxValue
0531 //
0532 if (info.hasMinValue() && info.hasMaxValue()) {
0533 if (compare(info.getMinValue(), info.getMaxValue()) > 0) {
0534 throw new OpenDataException(
0535 "minValue cannot be greater " + "than maxValue");
0536 }
0537 }
0538
0539 }
0540
0541 @SuppressWarnings("unchecked")
0542 static int compare(Object x, Object y) {
0543 return ((Comparable) x).compareTo(y);
0544 }
0545
0546 static <T> Descriptor makeDescriptor(OpenType<T> openType,
0547 T defaultValue, T[] legalValues, Comparable<T> minValue,
0548 Comparable<T> maxValue) {
0549 Map<String, Object> map = new HashMap<String, Object>();
0550 if (defaultValue != null)
0551 map.put("defaultValue", defaultValue);
0552 if (legalValues != null) {
0553 Set<T> set = new HashSet<T>();
0554 for (T v : legalValues)
0555 set.add(v);
0556 set = Collections.unmodifiableSet(set);
0557 map.put("legalValues", set);
0558 }
0559 if (minValue != null)
0560 map.put("minValue", minValue);
0561 if (maxValue != null)
0562 map.put("maxValue", maxValue);
0563 if (map.isEmpty()) {
0564 return openType.getDescriptor();
0565 } else {
0566 map.put("openType", openType);
0567 return new ImmutableDescriptor(map);
0568 }
0569 }
0570
0571 static <T> Descriptor makeDescriptor(OpenType<T> openType,
0572 T defaultValue, Set<T> legalValues, Comparable<T> minValue,
0573 Comparable<T> maxValue) {
0574 T[] legals;
0575 if (legalValues == null)
0576 legals = null;
0577 else {
0578 legals = cast(new Object[legalValues.size()]);
0579 legalValues.toArray(legals);
0580 }
0581 return makeDescriptor(openType, defaultValue, legals, minValue,
0582 maxValue);
0583 }
0584
0585 static <T> T valueFrom(Descriptor d, String name,
0586 OpenType<T> openType) {
0587 Object x = d.getFieldValue(name);
0588 if (x == null)
0589 return null;
0590 try {
0591 return convertFrom(x, openType);
0592 } catch (Exception e) {
0593 final String msg = "Cannot convert descriptor field "
0594 + name + " to " + openType.getTypeName();
0595 throw EnvHelp.initCause(new IllegalArgumentException(msg),
0596 e);
0597 }
0598 }
0599
0600 static <T> Set<T> valuesFrom(Descriptor d, String name,
0601 OpenType<T> openType) {
0602 Object x = d.getFieldValue(name);
0603 if (x == null)
0604 return null;
0605 Collection<?> coll;
0606 if (x instanceof Set<?>) {
0607 Set<?> set = (Set<?>) x;
0608 boolean asis = true;
0609 for (Object element : set) {
0610 if (!openType.isValue(element)) {
0611 asis = false;
0612 break;
0613 }
0614 }
0615 if (asis)
0616 return cast(set);
0617 coll = set;
0618 } else if (x instanceof Object[]) {
0619 coll = Arrays.asList((Object[]) x);
0620 } else {
0621 final String msg = "Descriptor value for " + name
0622 + " must be a Set or " + "an array: "
0623 + x.getClass().getName();
0624 throw new IllegalArgumentException(msg);
0625 }
0626
0627 Set<T> result = new HashSet<T>();
0628 for (Object element : coll)
0629 result.add(convertFrom(element, openType));
0630 return result;
0631 }
0632
0633 static <T> Comparable comparableValueFrom(Descriptor d,
0634 String name, OpenType<T> openType) {
0635 T t = valueFrom(d, name, openType);
0636 if (t == null || t instanceof Comparable<?>)
0637 return (Comparable) t;
0638 final String msg = "Descriptor field " + name + " with value "
0639 + t + " is not Comparable";
0640 throw new IllegalArgumentException(msg);
0641 }
0642
0643 private static <T> T convertFrom(Object x, OpenType<T> openType) {
0644 if (openType.isValue(x)) {
0645 T t = OpenMBeanAttributeInfoSupport.<T> cast(x);
0646 return t;
0647 }
0648 return convertFromStrings(x, openType);
0649 }
0650
0651 private static <T> T convertFromStrings(Object x,
0652 OpenType<T> openType) {
0653 if (openType instanceof ArrayType<?>)
0654 return convertFromStringArray(x, openType);
0655 else if (x instanceof String)
0656 return convertFromString((String) x, openType);
0657 final String msg = "Cannot convert value " + x + " of type "
0658 + x.getClass().getName() + " to type "
0659 + openType.getTypeName();
0660 throw new IllegalArgumentException(msg);
0661 }
0662
0663 private static <T> T convertFromString(String s,
0664 OpenType<T> openType) {
0665 Class<T> c;
0666 try {
0667 c = cast(Class.forName(openType.getClassName()));
0668 } catch (ClassNotFoundException e) {
0669 throw new NoClassDefFoundError(e.toString()); // can't happen
0670 }
0671
0672 // Look for: public static T valueOf(String)
0673 Method valueOf;
0674 try {
0675 valueOf = c.getMethod("valueOf", String.class);
0676 if (!Modifier.isStatic(valueOf.getModifiers())
0677 || valueOf.getReturnType() != c)
0678 valueOf = null;
0679 } catch (NoSuchMethodException e) {
0680 valueOf = null;
0681 }
0682 if (valueOf != null) {
0683 try {
0684 return c.cast(valueOf.invoke(null, s));
0685 } catch (Exception e) {
0686 final String msg = "Could not convert \"" + s
0687 + "\" using method: " + valueOf;
0688 throw new IllegalArgumentException(msg);
0689 }
0690 }
0691
0692 // Look for: public T(String)
0693 Constructor<T> con;
0694 try {
0695 con = c.getConstructor(String.class);
0696 } catch (NoSuchMethodException e) {
0697 con = null;
0698 }
0699 if (con != null) {
0700 try {
0701 return con.newInstance(s);
0702 } catch (Exception e) {
0703 final String msg = "Could not convert \"" + s
0704 + "\" using constructor: " + con;
0705 throw new IllegalArgumentException(msg);
0706 }
0707 }
0708
0709 throw new IllegalArgumentException("Don't know how to convert "
0710 + "string to " + openType.getTypeName());
0711 }
0712
0713 /* A Descriptor contained an array value encoded as Strings. The
0714 Strings must be organized in an array corresponding to the desired
0715 array. If the desired array has n dimensions, so must the String
0716 array. We will convert element by element from String to desired
0717 component type. */
0718 private static <T> T convertFromStringArray(Object x,
0719 OpenType<T> openType) {
0720 ArrayType<?> arrayType = (ArrayType<?>) openType;
0721 OpenType<?> baseType = arrayType.getElementOpenType();
0722 int dim = arrayType.getDimension();
0723 String squareBrackets = "[";
0724 for (int i = 1; i < dim; i++)
0725 squareBrackets += "[";
0726 Class<?> stringArrayClass;
0727 Class<?> targetArrayClass;
0728 try {
0729 stringArrayClass = Class.forName(squareBrackets
0730 + "Ljava.lang.String;");
0731 targetArrayClass = Class.forName(squareBrackets + "L"
0732 + baseType.getClassName() + ";");
0733 } catch (ClassNotFoundException e) {
0734 throw new NoClassDefFoundError(e.toString()); // can't happen
0735 }
0736 if (!stringArrayClass.isInstance(x)) {
0737 final String msg = "Value for " + dim
0738 + "-dimensional array of " + baseType.getTypeName()
0739 + " must be same type or a String "
0740 + "array with same dimensions";
0741 throw new IllegalArgumentException(msg);
0742 }
0743 Class<?> targetComponentClass = targetArrayClass
0744 .getComponentType();
0745 OpenType<?> componentOpenType;
0746 if (dim == 1)
0747 componentOpenType = baseType;
0748 else {
0749 try {
0750 componentOpenType = new ArrayType<T>(dim - 1, baseType);
0751 } catch (OpenDataException e) {
0752 throw new IllegalArgumentException(e.getMessage(), e);
0753 // can't happen
0754 }
0755 }
0756 int n = Array.getLength(x);
0757 Object[] targetArray = (Object[]) Array.newInstance(
0758 targetArrayClass.getComponentType(), n);
0759 for (int i = 0; i < n; i++) {
0760 Object stringish = Array.get(x, i); // String or String[] etc
0761 Object converted = convertFromStrings(stringish,
0762 componentOpenType);
0763 Array.set(targetArray, i, converted);
0764 }
0765 return OpenMBeanAttributeInfoSupport.<T> cast(targetArray);
0766 }
0767
0768 @SuppressWarnings("unchecked")
0769 static <T> T cast(Object x) {
0770 return (T) x;
0771 }
0772
0773 /**
0774 * Returns the open type for the values of the attribute described
0775 * by this {@code OpenMBeanAttributeInfoSupport} instance.
0776 */
0777 public OpenType<?> getOpenType() {
0778 return openType;
0779 }
0780
0781 /**
0782 * Returns the default value for the attribute described by this
0783 * {@code OpenMBeanAttributeInfoSupport} instance, if specified,
0784 * or {@code null} otherwise.
0785 */
0786 public Object getDefaultValue() {
0787
0788 // Special case for ArrayType and TabularType
0789 // [JF] TODO: clone it so that it cannot be altered,
0790 // [JF] TODO: if we decide to support defaultValue as an array itself.
0791 // [JF] As of today (oct 2000) it is not supported so
0792 // defaultValue is null for arrays. Nothing to do.
0793
0794 return defaultValue;
0795 }
0796
0797 /**
0798 * Returns an unmodifiable Set of legal values for the attribute
0799 * described by this {@code OpenMBeanAttributeInfoSupport}
0800 * instance, if specified, or {@code null} otherwise.
0801 */
0802 public Set<?> getLegalValues() {
0803
0804 // Special case for ArrayType and TabularType
0805 // [JF] TODO: clone values so that they cannot be altered,
0806 // [JF] TODO: if we decide to support LegalValues as an array itself.
0807 // [JF] As of today (oct 2000) it is not supported so
0808 // legalValues is null for arrays. Nothing to do.
0809
0810 // Returns our legalValues Set (set was constructed unmodifiable)
0811 return (legalValues);
0812 }
0813
0814 /**
0815 * Returns the minimal value for the attribute described by this
0816 * {@code OpenMBeanAttributeInfoSupport} instance, if specified,
0817 * or {@code null} otherwise.
0818 */
0819 public Comparable<?> getMinValue() {
0820
0821 // Note: only comparable values have a minValue,
0822 // so that's not the case of arrays and tabulars (always null).
0823
0824 return minValue;
0825 }
0826
0827 /**
0828 * Returns the maximal value for the attribute described by this
0829 * {@code OpenMBeanAttributeInfoSupport} instance, if specified,
0830 * or {@code null} otherwise.
0831 */
0832 public Comparable<?> getMaxValue() {
0833
0834 // Note: only comparable values have a maxValue,
0835 // so that's not the case of arrays and tabulars (always null).
0836
0837 return maxValue;
0838 }
0839
0840 /**
0841 * Returns {@code true} if this {@code
0842 * OpenMBeanAttributeInfoSupport} instance specifies a non-null
0843 * default value for the described attribute, {@code false}
0844 * otherwise.
0845 */
0846 public boolean hasDefaultValue() {
0847
0848 return (defaultValue != null);
0849 }
0850
0851 /**
0852 * Returns {@code true} if this {@code
0853 * OpenMBeanAttributeInfoSupport} instance specifies a non-null
0854 * set of legal values for the described attribute, {@code false}
0855 * otherwise.
0856 */
0857 public boolean hasLegalValues() {
0858
0859 return (legalValues != null);
0860 }
0861
0862 /**
0863 * Returns {@code true} if this {@code
0864 * OpenMBeanAttributeInfoSupport} instance specifies a non-null
0865 * minimal value for the described attribute, {@code false}
0866 * otherwise.
0867 */
0868 public boolean hasMinValue() {
0869
0870 return (minValue != null);
0871 }
0872
0873 /**
0874 * Returns {@code true} if this {@code
0875 * OpenMBeanAttributeInfoSupport} instance specifies a non-null
0876 * maximal value for the described attribute, {@code false}
0877 * otherwise.
0878 */
0879 public boolean hasMaxValue() {
0880
0881 return (maxValue != null);
0882 }
0883
0884 /**
0885 * Tests whether {@code obj} is a valid value for the attribute
0886 * described by this {@code OpenMBeanAttributeInfoSupport}
0887 * instance.
0888 *
0889 * @param obj the object to be tested.
0890 *
0891 * @return {@code true} if {@code obj} is a valid value for
0892 * the parameter described by this {@code
0893 * OpenMBeanAttributeInfoSupport} instance, {@code false}
0894 * otherwise.
0895 */
0896 public boolean isValue(Object obj) {
0897 return isValue(this , obj);
0898 }
0899
0900 @SuppressWarnings("unchecked")
0901 // cast to Comparable
0902 static boolean isValue(OpenMBeanParameterInfo info, Object obj) {
0903 if (info.hasDefaultValue() && obj == null)
0904 return true;
0905 return info.getOpenType().isValue(obj)
0906 && (!info.hasLegalValues() || info.getLegalValues()
0907 .contains(obj))
0908 && (!info.hasMinValue() || ((Comparable) info
0909 .getMinValue()).compareTo(obj) <= 0)
0910 && (!info.hasMaxValue() || ((Comparable) info
0911 .getMaxValue()).compareTo(obj) >= 0);
0912 }
0913
0914 /* *** Commodity methods from java.lang.Object *** */
0915
0916 /**
0917 * Compares the specified {@code obj} parameter with this {@code
0918 * OpenMBeanAttributeInfoSupport} instance for equality.
0919 * <p>
0920 * Returns {@code true} if and only if all of the following statements are true:
0921 * <ul>
0922 * <li>{@code obj} is non null,</li>
0923 * <li>{@code obj} also implements the {@code OpenMBeanAttributeInfo} interface,</li>
0924 * <li>their names are equal</li>
0925 * <li>their open types are equal</li>
0926 * <li>their access properties (isReadable, isWritable and isIs) are equal</li>
0927 * <li>their default, min, max and legal values are equal.</li>
0928 * </ul>
0929 * This ensures that this {@code equals} method works properly for
0930 * {@code obj} parameters which are different implementations of
0931 * the {@code OpenMBeanAttributeInfo} interface.
0932 *
0933 * <p>If {@code obj} also implements {@link DescriptorRead}, then its
0934 * {@link DescriptorRead#getDescriptor() getDescriptor()} method must
0935 * also return the same value as for this object.</p>
0936 *
0937 * @param obj the object to be compared for equality with this
0938 * {@code OpenMBeanAttributeInfoSupport} instance.
0939 *
0940 * @return {@code true} if the specified object is equal to this
0941 * {@code OpenMBeanAttributeInfoSupport} instance.
0942 */
0943 public boolean equals(Object obj) {
0944 if (!(obj instanceof OpenMBeanAttributeInfo))
0945 return false;
0946
0947 OpenMBeanAttributeInfo other = (OpenMBeanAttributeInfo) obj;
0948
0949 return this .isReadable() == other.isReadable()
0950 && this .isWritable() == other.isWritable()
0951 && this .isIs() == other.isIs() && equal(this , other);
0952 }
0953
0954 static boolean equal(OpenMBeanParameterInfo x1,
0955 OpenMBeanParameterInfo x2) {
0956 if (x1 instanceof DescriptorRead) {
0957 if (!(x2 instanceof DescriptorRead))
0958 return false;
0959 Descriptor d1 = ((DescriptorRead) x1).getDescriptor();
0960 Descriptor d2 = ((DescriptorRead) x2).getDescriptor();
0961 if (!d1.equals(d2))
0962 return false;
0963 } else if (x2 instanceof DescriptorRead)
0964 return false;
0965
0966 return x1.getName().equals(x2.getName())
0967 && x1.getOpenType().equals(x2.getOpenType())
0968 && (x1.hasDefaultValue() ? x1.getDefaultValue().equals(
0969 x2.getDefaultValue()) : !x2.hasDefaultValue())
0970 && (x1.hasMinValue() ? x1.getMinValue().equals(
0971 x2.getMinValue()) : !x2.hasMinValue())
0972 && (x1.hasMaxValue() ? x1.getMaxValue().equals(
0973 x2.getMaxValue()) : !x2.hasMaxValue())
0974 && (x1.hasLegalValues() ? x1.getLegalValues().equals(
0975 x2.getLegalValues()) : !x2.hasLegalValues());
0976 }
0977
0978 /**
0979 * <p>Returns the hash code value for this {@code
0980 * OpenMBeanAttributeInfoSupport} instance.</p>
0981 *
0982 * <p>The hash code of an {@code OpenMBeanAttributeInfoSupport}
0983 * instance is the sum of the hash codes of all elements of
0984 * information used in {@code equals} comparisons (ie: its name,
0985 * its <i>open type</i>, its default, min, max and legal
0986 * values, and its Descriptor).
0987 *
0988 * <p>This ensures that {@code t1.equals(t2)} implies that {@code
0989 * t1.hashCode()==t2.hashCode()} for any two {@code
0990 * OpenMBeanAttributeInfoSupport} instances {@code t1} and {@code
0991 * t2}, as required by the general contract of the method {@link
0992 * Object#hashCode() Object.hashCode()}.
0993 *
0994 * <p>However, note that another instance of a class implementing
0995 * the {@code OpenMBeanAttributeInfo} interface may be equal to
0996 * this {@code OpenMBeanAttributeInfoSupport} instance as defined
0997 * by {@link #equals(java.lang.Object)}, but may have a different
0998 * hash code if it is calculated differently.
0999 *
1000 * <p>As {@code OpenMBeanAttributeInfoSupport} instances are
1001 * immutable, the hash code for this instance is calculated once,
1002 * on the first call to {@code hashCode}, and then the same value
1003 * is returned for subsequent calls.
1004 *
1005 * @return the hash code value for this {@code
1006 * OpenMBeanAttributeInfoSupport} instance
1007 */
1008 public int hashCode() {
1009
1010 // Calculate the hash code value if it has not yet been done
1011 // (ie 1st call to hashCode())
1012 //
1013 if (myHashCode == null)
1014 myHashCode = hashCode(this );
1015
1016 // return always the same hash code for this instance (immutable)
1017 //
1018 return myHashCode.intValue();
1019 }
1020
1021 static int hashCode(OpenMBeanParameterInfo info) {
1022 int value = 0;
1023 value += info.getName().hashCode();
1024 value += info.getOpenType().hashCode();
1025 if (info.hasDefaultValue())
1026 value += info.getDefaultValue().hashCode();
1027 if (info.hasMinValue())
1028 value += info.getMinValue().hashCode();
1029 if (info.hasMaxValue())
1030 value += info.getMaxValue().hashCode();
1031 if (info.hasLegalValues())
1032 value += info.getLegalValues().hashCode();
1033 if (info instanceof DescriptorRead)
1034 value += ((DescriptorRead) info).getDescriptor().hashCode();
1035 return value;
1036 }
1037
1038 /**
1039 * Returns a string representation of this
1040 * {@code OpenMBeanAttributeInfoSupport} instance.
1041 * <p>
1042 * The string representation consists of the name of this class (i.e.
1043 * {@code javax.management.openmbean.OpenMBeanAttributeInfoSupport}),
1044 * the string representation of the name and open type of the
1045 * described parameter, the string representation of its
1046 * default, min, max and legal values and the string
1047 * representation of its descriptor.
1048 *
1049 * <p>As {@code OpenMBeanAttributeInfoSupport} instances are
1050 * immutable, the string representation for this instance is
1051 * calculated once, on the first call to {@code toString}, and
1052 * then the same value is returned for subsequent calls.
1053 *
1054 * @return a string representation of this
1055 * {@code OpenMBeanAttributeInfoSupport} instance.
1056 */
1057 public String toString() {
1058
1059 // Calculate the string value if it has not yet been done
1060 // (ie 1st call to toString())
1061 //
1062 if (myToString == null)
1063 myToString = toString(this );
1064
1065 // return always the same string representation for this
1066 // instance (immutable)
1067 //
1068 return myToString;
1069 }
1070
1071 static String toString(OpenMBeanParameterInfo info) {
1072 Descriptor d = (info instanceof DescriptorRead) ? ((DescriptorRead) info)
1073 .getDescriptor()
1074 : null;
1075 return info.getClass().getName() + "(name=" + info.getName()
1076 + ",openType=" + info.getOpenType() + ",default="
1077 + info.getDefaultValue() + ",minValue="
1078 + info.getMinValue() + ",maxValue="
1079 + info.getMaxValue() + ",legalValues="
1080 + info.getLegalValues()
1081 + ((d == null) ? "" : ",descriptor=" + d) + ")";
1082 }
1083 }
|