View Javadoc
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</CODE> 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</CODE> 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 <tt>super.clone()</tt>,
197      * thus calling the default native shallow cloning mechanism
198      * implemented by <tt>Object.clone()</tt>.
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</code> 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 }