1 /* 2 * Copyright (c) 1999, 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 package javax.management; 27 28 import java.lang.reflect.Method; 29 import java.security.AccessController; 30 31 import com.sun.jmx.mbeanserver.GetPropertyAction; 32 import com.sun.jmx.mbeanserver.Introspector; 33 import java.util.Objects; 34 35 36 /** 37 * Describes an MBean attribute exposed for management. Instances of 38 * this class are immutable. Subclasses may be mutable but this is 39 * not recommended. 40 * 41 * @since 1.5 42 */ 43 @SuppressWarnings("serial") // serialVersionUID not constant 44 public class MBeanAttributeInfo extends MBeanFeatureInfo implements Cloneable { 45 46 /* Serial version */ 47 private static final long serialVersionUID; 48 static { 49 /* For complicated reasons, the serialVersionUID changed 50 between JMX 1.0 and JMX 1.1, even though JMX 1.1 did not 51 have compatibility code for this class. So the 52 serialization produced by this class with JMX 1.2 and 53 jmx.serial.form=1.0 is not the same as that produced by 54 this class with JMX 1.1 and jmx.serial.form=1.0. However, 55 the serialization without that property is the same, and 56 that is the only form required by JMX 1.2. 57 */ 58 long uid = 8644704819898565848L; 59 try { 60 GetPropertyAction act = new GetPropertyAction("jmx.serial.form"); 61 String form = AccessController.doPrivileged(act); 62 if ("1.0".equals(form)) 63 uid = 7043855487133450673L; 64 } catch (Exception e) { 65 // OK: exception means no compat with 1.0, too bad 66 } 67 serialVersionUID = uid; 68 } 69 70 static final MBeanAttributeInfo[] NO_ATTRIBUTES = 71 new MBeanAttributeInfo[0]; 72 73 /** 74 * @serial The actual attribute type. 75 */ 76 private final String attributeType; 77 78 /** 79 * @serial The attribute write right. 80 */ 81 private final boolean isWrite; 82 83 /** 84 * @serial The attribute read right. 85 */ 86 private final boolean isRead; 87 88 /** 89 * @serial Indicates if this method is a "is" 90 */ 91 private final boolean is; 92 93 94 /** 95 * Constructs an {@code MBeanAttributeInfo} object. 96 * 97 * @param name The name of the attribute. 98 * @param type The type or class name of the attribute. 99 * @param description A human readable description of the attribute. 100 * @param isReadable True if the attribute has a getter method, false otherwise. 101 * @param isWritable True if the attribute has a setter method, false otherwise. 102 * @param isIs True if this attribute has an "is" getter, false otherwise. 103 * 104 * @throws IllegalArgumentException if {@code isIs} is true but 105 * {@code isReadable} is not, or if {@code isIs} is true and 106 * {@code type} is not {@code boolean} or {@code java.lang.Boolean}. 107 * (New code should always use {@code boolean} rather than 108 * {@code java.lang.Boolean}.) 109 */ 110 public MBeanAttributeInfo(String name, 111 String type, 112 String description, 113 boolean isReadable, 114 boolean isWritable, 115 boolean isIs) { 116 this(name, type, description, isReadable, isWritable, isIs, 117 (Descriptor) null); 118 } 119 120 /** 121 * Constructs an {@code MBeanAttributeInfo} object. 122 * 123 * @param name The name of the attribute. 124 * @param type The type or class name of the attribute. 125 * @param description A human readable description of the attribute. 126 * @param isReadable True if the attribute has a getter method, false otherwise. 127 * @param isWritable True if the attribute has a setter method, false otherwise. 128 * @param isIs True if this attribute has an "is" getter, false otherwise. 129 * @param descriptor The descriptor for the attribute. This may be null 130 * which is equivalent to an empty descriptor. 131 * 132 * @throws IllegalArgumentException if {@code isIs} is true but 133 * {@code isReadable} is not, or if {@code isIs} is true and 134 * {@code type} is not {@code boolean} or {@code java.lang.Boolean}. 135 * (New code should always use {@code boolean} rather than 136 * {@code java.lang.Boolean}.) 137 * 138 * @since 1.6 139 */ 140 public MBeanAttributeInfo(String name, 141 String type, 142 String description, 143 boolean isReadable, 144 boolean isWritable, 145 boolean isIs, 146 Descriptor descriptor) { 147 super(name, description, descriptor); 148 149 this.attributeType = type; 150 this.isRead = isReadable; 151 this.isWrite = isWritable; 152 if (isIs && !isReadable) { 153 throw new IllegalArgumentException("Cannot have an \"is\" getter " + 154 "for a non-readable attribute"); 155 } 156 if (isIs && !type.equals("java.lang.Boolean") && 157 !type.equals("boolean")) { 158 throw new IllegalArgumentException("Cannot have an \"is\" getter " + 159 "for a non-boolean attribute"); 160 } 161 this.is = isIs; 162 } 163 164 /** 165 * <p>This constructor takes the name of a simple attribute, and Method 166 * objects for reading and writing the attribute. The {@link Descriptor} 167 * of the constructed object will include fields contributed by any 168 * annotations on the {@code Method} objects that contain the 169 * {@link DescriptorKey} meta-annotation. 170 * 171 * @param name The programmatic name of the attribute. 172 * @param description A human readable description of the attribute. 173 * @param getter The method used for reading the attribute value. 174 * May be null if the property is write-only. 175 * @param setter The method used for writing the attribute value. 176 * May be null if the attribute is read-only. 177 * @exception IntrospectionException There is a consistency 178 * problem in the definition of this attribute. 179 */ 180 public MBeanAttributeInfo(String name, 181 String description, 182 Method getter, 183 Method setter) throws IntrospectionException { 184 this(name, 185 attributeType(getter, setter), 186 description, 187 (getter != null), 188 (setter != null), 189 isIs(getter), 190 ImmutableDescriptor.union(Introspector.descriptorForElement(getter), 191 Introspector.descriptorForElement(setter))); 192 } 193 194 /** 195 * <p>Returns a shallow clone of this instance. 196 * The clone is obtained by simply calling {@code super.clone()}, 197 * thus calling the default native shallow cloning mechanism 198 * implemented by {@code Object.clone()}. 199 * No deeper cloning of any internal field is made.</p> 200 * 201 * <p>Since this class is immutable, cloning is chiefly of 202 * interest to subclasses.</p> 203 */ 204 public Object clone () { 205 try { 206 return super.clone() ; 207 } catch (CloneNotSupportedException e) { 208 // should not happen as this class is cloneable 209 return null; 210 } 211 } 212 213 /** 214 * Returns the class name of the attribute. 215 * 216 * @return the class name. 217 */ 218 public String getType() { 219 return attributeType; 220 } 221 222 /** 223 * Whether the value of the attribute can be read. 224 * 225 * @return True if the attribute can be read, false otherwise. 226 */ 227 public boolean isReadable() { 228 return isRead; 229 } 230 231 /** 232 * Whether new values can be written to the attribute. 233 * 234 * @return True if the attribute can be written to, false otherwise. 235 */ 236 public boolean isWritable() { 237 return isWrite; 238 } 239 240 /** 241 * Indicates if this attribute has an "is" getter. 242 * 243 * @return true if this attribute has an "is" getter. 244 */ 245 public boolean isIs() { 246 return is; 247 } 248 249 public String toString() { 250 String access; 251 if (isReadable()) { 252 if (isWritable()) 253 access = "read/write"; 254 else 255 access = "read-only"; 256 } else if (isWritable()) 257 access = "write-only"; 258 else 259 access = "no-access"; 260 261 return 262 getClass().getName() + "[" + 263 "description=" + getDescription() + ", " + 264 "name=" + getName() + ", " + 265 "type=" + getType() + ", " + 266 access + ", " + 267 (isIs() ? "isIs, " : "") + 268 "descriptor=" + getDescriptor() + 269 "]"; 270 } 271 272 /** 273 * Compare this MBeanAttributeInfo to another. 274 * 275 * @param o the object to compare to. 276 * 277 * @return true if and only if {@code o} is an MBeanAttributeInfo such 278 * that its {@link #getName()}, {@link #getType()}, {@link 279 * #getDescription()}, {@link #isReadable()}, {@link 280 * #isWritable()}, and {@link #isIs()} values are equal (not 281 * necessarily identical) to those of this MBeanAttributeInfo. 282 */ 283 public boolean equals(Object o) { 284 if (o == this) 285 return true; 286 if (!(o instanceof MBeanAttributeInfo)) 287 return false; 288 MBeanAttributeInfo p = (MBeanAttributeInfo) o; 289 return (Objects.equals(p.getName(), getName()) && 290 Objects.equals(p.getType(), getType()) && 291 Objects.equals(p.getDescription(), getDescription()) && 292 Objects.equals(p.getDescriptor(), getDescriptor()) && 293 p.isReadable() == isReadable() && 294 p.isWritable() == isWritable() && 295 p.isIs() == isIs()); 296 } 297 298 /* We do not include everything in the hashcode. We assume that 299 if two operations are different they'll probably have different 300 names or types. The penalty we pay when this assumption is 301 wrong should be less than the penalty we would pay if it were 302 right and we needlessly hashed in the description and parameter 303 array. */ 304 public int hashCode() { 305 return Objects.hash(getName(), getType()); 306 } 307 308 private static boolean isIs(Method getter) { 309 return (getter != null && 310 getter.getName().startsWith("is") && 311 (getter.getReturnType().equals(Boolean.TYPE) || 312 getter.getReturnType().equals(Boolean.class))); 313 } 314 315 /** 316 * Finds the type of the attribute. 317 */ 318 private static String attributeType(Method getter, Method setter) 319 throws IntrospectionException { 320 Class<?> type = null; 321 322 if (getter != null) { 323 if (getter.getParameterTypes().length != 0) { 324 throw new IntrospectionException("bad getter arg count"); 325 } 326 type = getter.getReturnType(); 327 if (type == Void.TYPE) { 328 throw new IntrospectionException("getter " + getter.getName() + 329 " returns void"); 330 } 331 } 332 333 if (setter != null) { 334 Class<?> params[] = setter.getParameterTypes(); 335 if (params.length != 1) { 336 throw new IntrospectionException("bad setter arg count"); 337 } 338 if (type == null) 339 type = params[0]; 340 else if (type != params[0]) { 341 throw new IntrospectionException("type mismatch between " + 342 "getter and setter"); 343 } 344 } 345 346 if (type == null) { 347 throw new IntrospectionException("getter and setter cannot " + 348 "both be null"); 349 } 350 351 return type.getName(); 352 } 353 354 }