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
27package javax.management.openmbean;
28
29
30// java import
31//
32import java.util.Arrays;
33import java.util.HashSet;
34import java.util.Objects;
35
36import javax.management.Descriptor;
37import javax.management.MBeanAttributeInfo;
38import javax.management.MBeanConstructorInfo;
39import javax.management.MBeanInfo;
40import javax.management.MBeanNotificationInfo;
41import javax.management.MBeanOperationInfo;
42
43/**
44 * The {@code OpenMBeanInfoSupport} class describes the management
45 * information of an <i>open MBean</i>: it is a subclass of {@link
46 * javax.management.MBeanInfo}, and it implements the {@link
47 * OpenMBeanInfo} interface.  Note that an <i>open MBean</i> is
48 * recognized as such if its {@code getMBeanInfo()} method returns an
49 * instance of a class which implements the OpenMBeanInfo interface,
50 * typically {@code OpenMBeanInfoSupport}.
51 *
52 *
53 * @since 1.5
54 */
55public class OpenMBeanInfoSupport
56    extends MBeanInfo
57    implements OpenMBeanInfo {
58
59    /* Serial version */
60    static final long serialVersionUID = 4349395935420511492L;
61
62    // As this instance is immutable, these two values
63    // need only be calculated once.
64    private transient Integer myHashCode = null;
65    private transient String  myToString = null;
66
67
68    /**
69     * <p>Constructs an {@code OpenMBeanInfoSupport} instance, which
70     * describes a class of open MBeans with the specified {@code
71     * className}, {@code description}, {@code openAttributes}, {@code
72     * openConstructors} , {@code openOperations} and {@code
73     * notifications}.</p>
74     *
75     * <p>The {@code openAttributes}, {@code openConstructors},
76     * {@code openOperations} and {@code notifications}
77     * array parameters are internally copied, so that subsequent changes
78     * to the arrays referenced by these parameters have no effect on this
79     * instance.</p>
80     *
81     * @param className The fully qualified Java class name of the
82     * open MBean described by this <CODE>OpenMBeanInfoSupport</CODE>
83     * instance.
84     *
85     * @param description A human readable description of the open
86     * MBean described by this <CODE>OpenMBeanInfoSupport</CODE>
87     * instance.
88     *
89     * @param openAttributes The list of exposed attributes of the
90     * described open MBean; Must be an array of instances of a
91     * subclass of {@code MBeanAttributeInfo}, typically {@code
92     * OpenMBeanAttributeInfoSupport}.
93     *
94     * @param openConstructors The list of exposed public constructors
95     * of the described open MBean; Must be an array of instances of a
96     * subclass of {@code MBeanConstructorInfo}, typically {@code
97     * OpenMBeanConstructorInfoSupport}.
98     *
99     * @param openOperations The list of exposed operations of the
100     * described open MBean.  Must be an array of instances of a
101     * subclass of {@code MBeanOperationInfo}, typically {@code
102     * OpenMBeanOperationInfoSupport}.
103     *
104     * @param notifications The list of notifications emitted by the
105     * described open MBean.
106     *
107     * @throws ArrayStoreException If {@code openAttributes}, {@code
108     * openConstructors} or {@code openOperations} is not an array of
109     * instances of a subclass of {@code MBeanAttributeInfo}, {@code
110     * MBeanConstructorInfo} or {@code MBeanOperationInfo}
111     * respectively.
112     */
113    public OpenMBeanInfoSupport(String className,
114                                String description,
115                                OpenMBeanAttributeInfo[] openAttributes,
116                                OpenMBeanConstructorInfo[] openConstructors,
117                                OpenMBeanOperationInfo[] openOperations,
118                                MBeanNotificationInfo[] notifications) {
119        this(className, description,
120             openAttributes, openConstructors, openOperations, notifications,
121             (Descriptor) null);
122    }
123
124    /**
125     * <p>Constructs an {@code OpenMBeanInfoSupport} instance, which
126     * describes a class of open MBeans with the specified {@code
127     * className}, {@code description}, {@code openAttributes}, {@code
128     * openConstructors} , {@code openOperations}, {@code
129     * notifications}, and {@code descriptor}.</p>
130     *
131     * <p>The {@code openAttributes}, {@code openConstructors}, {@code
132     * openOperations} and {@code notifications} array parameters are
133     * internally copied, so that subsequent changes to the arrays
134     * referenced by these parameters have no effect on this
135     * instance.</p>
136     *
137     * @param className The fully qualified Java class name of the
138     * open MBean described by this <CODE>OpenMBeanInfoSupport</CODE>
139     * instance.
140     *
141     * @param description A human readable description of the open
142     * MBean described by this <CODE>OpenMBeanInfoSupport</CODE>
143     * instance.
144     *
145     * @param openAttributes The list of exposed attributes of the
146     * described open MBean; Must be an array of instances of a
147     * subclass of {@code MBeanAttributeInfo}, typically {@code
148     * OpenMBeanAttributeInfoSupport}.
149     *
150     * @param openConstructors The list of exposed public constructors
151     * of the described open MBean; Must be an array of instances of a
152     * subclass of {@code MBeanConstructorInfo}, typically {@code
153     * OpenMBeanConstructorInfoSupport}.
154     *
155     * @param openOperations The list of exposed operations of the
156     * described open MBean.  Must be an array of instances of a
157     * subclass of {@code MBeanOperationInfo}, typically {@code
158     * OpenMBeanOperationInfoSupport}.
159     *
160     * @param notifications The list of notifications emitted by the
161     * described open MBean.
162     *
163     * @param descriptor The descriptor for the MBean.  This may be null
164     * which is equivalent to an empty descriptor.
165     *
166     * @throws ArrayStoreException If {@code openAttributes}, {@code
167     * openConstructors} or {@code openOperations} is not an array of
168     * instances of a subclass of {@code MBeanAttributeInfo}, {@code
169     * MBeanConstructorInfo} or {@code MBeanOperationInfo}
170     * respectively.
171     *
172     * @since 1.6
173     */
174    public OpenMBeanInfoSupport(String className,
175                                String description,
176                                OpenMBeanAttributeInfo[] openAttributes,
177                                OpenMBeanConstructorInfo[] openConstructors,
178                                OpenMBeanOperationInfo[] openOperations,
179                                MBeanNotificationInfo[] notifications,
180                                Descriptor descriptor) {
181        super(className,
182              description,
183              attributeArray(openAttributes),
184              constructorArray(openConstructors),
185              operationArray(openOperations),
186              (notifications == null) ? null : notifications.clone(),
187              descriptor);
188    }
189
190
191    private static MBeanAttributeInfo[]
192            attributeArray(OpenMBeanAttributeInfo[] src) {
193        if (src == null)
194            return null;
195        MBeanAttributeInfo[] dst = new MBeanAttributeInfo[src.length];
196        System.arraycopy(src, 0, dst, 0, src.length);
197        // may throw an ArrayStoreException
198        return dst;
199    }
200
201    private static MBeanConstructorInfo[]
202            constructorArray(OpenMBeanConstructorInfo[] src) {
203        if (src == null)
204            return null;
205        MBeanConstructorInfo[] dst = new MBeanConstructorInfo[src.length];
206        System.arraycopy(src, 0, dst, 0, src.length);
207        // may throw an ArrayStoreException
208        return dst;
209    }
210
211    private static MBeanOperationInfo[]
212            operationArray(OpenMBeanOperationInfo[] src) {
213        if (src == null)
214            return null;
215        MBeanOperationInfo[] dst = new MBeanOperationInfo[src.length];
216        System.arraycopy(src, 0, dst, 0, src.length);
217        return dst;
218    }
219
220
221
222    /* ***  Commodity methods from java.lang.Object  *** */
223
224
225    /**
226     * <p>Compares the specified {@code obj} parameter with this
227     * {@code OpenMBeanInfoSupport} instance for equality.</p>
228     *
229     * <p>Returns {@code true} if and only if all of the following
230     * statements are true:
231     *
232     * <ul>
233     * <li>{@code obj} is non null,</li>
234     * <li>{@code obj} also implements the {@code OpenMBeanInfo}
235     * interface,</li>
236     * <li>their class names are equal</li>
237     * <li>their infos on attributes, constructors, operations and
238     * notifications are equal</li>
239     * </ul>
240     *
241     * This ensures that this {@code equals} method works properly for
242     * {@code obj} parameters which are different implementations of
243     * the {@code OpenMBeanInfo} interface.
244     *
245     * @param obj the object to be compared for equality with this
246     * {@code OpenMBeanInfoSupport} instance;
247     *
248     * @return {@code true} if the specified object is equal to this
249     * {@code OpenMBeanInfoSupport} instance.
250     */
251    public boolean equals(Object obj) {
252
253        // if obj is null, return false
254        //
255        if (obj == null) {
256            return false;
257        }
258
259        // if obj is not a OpenMBeanInfo, return false
260        //
261        OpenMBeanInfo other;
262        try {
263            other = (OpenMBeanInfo) obj;
264        } catch (ClassCastException e) {
265            return false;
266        }
267
268        // Now, really test for equality between this OpenMBeanInfo
269        // implementation and the other:
270        //
271
272        // their MBean className should be equal
273        if (!Objects.equals(this.getClassName(), other.getClassName())) {
274            return false;
275        }
276
277        // their infos on attributes should be equal (order not
278        // significant => equality between sets, not arrays or lists)
279        if (!sameArrayContents(this.getAttributes(), other.getAttributes()))
280            return false;
281
282        // their infos on constructors should be equal (order not
283        // significant => equality between sets, not arrays or lists)
284        if (!sameArrayContents(this.getConstructors(), other.getConstructors()))
285            return false;
286
287        // their infos on operations should be equal (order not
288        // significant => equality between sets, not arrays or lists)
289        if (!sameArrayContents(this.getOperations(), other.getOperations()))
290
291            return false;
292
293        // their infos on notifications should be equal (order not
294        // significant => equality between sets, not arrays or lists)
295        if (!sameArrayContents(this.getNotifications(), other.getNotifications()))
296            return false;
297
298        // All tests for equality were successful
299        //
300        return true;
301    }
302
303    private static <T> boolean sameArrayContents(T[] a1, T[] a2) {
304        return (new HashSet<T>(Arrays.asList(a1))
305                .equals(new HashSet<T>(Arrays.asList(a2))));
306    }
307
308    /**
309     * <p>Returns the hash code value for this {@code
310     * OpenMBeanInfoSupport} instance.</p>
311     *
312     * <p>The hash code of an {@code OpenMBeanInfoSupport} instance is
313     * the sum of the hash codes of all elements of information used
314     * in {@code equals} comparisons (ie: its class name, and its
315     * infos on attributes, constructors, operations and
316     * notifications, where the hashCode of each of these arrays is
317     * calculated by a call to {@code new
318     * java.util.HashSet(java.util.Arrays.asList(this.getSignature)).hashCode()}).</p>
319     *
320     * <p>This ensures that {@code t1.equals(t2)} implies that {@code
321     * t1.hashCode()==t2.hashCode()} for any two {@code
322     * OpenMBeanInfoSupport} instances {@code t1} and {@code t2}, as
323     * required by the general contract of the method {@link
324     * Object#hashCode() Object.hashCode()}.</p>
325     *
326     * <p>However, note that another instance of a class implementing
327     * the {@code OpenMBeanInfo} interface may be equal to this {@code
328     * OpenMBeanInfoSupport} instance as defined by {@link
329     * #equals(java.lang.Object)}, but may have a different hash code
330     * if it is calculated differently.</p>
331     *
332     * <p>As {@code OpenMBeanInfoSupport} instances are immutable, the
333     * hash code for this instance is calculated once, on the first
334     * call to {@code hashCode}, and then the same value is returned
335     * for subsequent calls.</p>
336     *
337     * @return the hash code value for this {@code
338     * OpenMBeanInfoSupport} instance
339     */
340    public int hashCode() {
341
342        // Calculate the hash code value if it has not yet been done
343        // (ie 1st call to hashCode())
344        //
345        if (myHashCode == null) {
346            int value = 0;
347            if (this.getClassName() != null) {
348                value += this.getClassName().hashCode();
349            }
350            value += arraySetHash(this.getAttributes());
351            value += arraySetHash(this.getConstructors());
352            value += arraySetHash(this.getOperations());
353            value += arraySetHash(this.getNotifications());
354            myHashCode = Integer.valueOf(value);
355        }
356
357        // return always the same hash code for this instance (immutable)
358        //
359        return myHashCode.intValue();
360    }
361
362    private static <T> int arraySetHash(T[] a) {
363        return new HashSet<T>(Arrays.asList(a)).hashCode();
364    }
365
366
367
368    /**
369     * <p>Returns a string representation of this {@code
370     * OpenMBeanInfoSupport} instance.</p>
371     *
372     * <p>The string representation consists of the name of this class
373     * (ie {@code javax.management.openmbean.OpenMBeanInfoSupport}),
374     * the MBean class name, the string representation of infos on
375     * attributes, constructors, operations and notifications of the
376     * described MBean and the string representation of the descriptor.</p>
377     *
378     * <p>As {@code OpenMBeanInfoSupport} instances are immutable, the
379     * string representation for this instance is calculated once, on
380     * the first call to {@code toString}, and then the same value is
381     * returned for subsequent calls.</p>
382     *
383     * @return a string representation of this {@code
384     * OpenMBeanInfoSupport} instance
385     */
386    public String toString() {
387
388        // Calculate the string value if it has not yet been done (ie
389        // 1st call to toString())
390        //
391        if (myToString == null) {
392            myToString = new StringBuilder()
393                .append(this.getClass().getName())
394                .append("(mbean_class_name=")
395                .append(this.getClassName())
396                .append(",attributes=")
397                .append(Arrays.asList(this.getAttributes()).toString())
398                .append(",constructors=")
399                .append(Arrays.asList(this.getConstructors()).toString())
400                .append(",operations=")
401                .append(Arrays.asList(this.getOperations()).toString())
402                .append(",notifications=")
403                .append(Arrays.asList(this.getNotifications()).toString())
404                .append(",descriptor=")
405                .append(this.getDescriptor())
406                .append(")")
407                .toString();
408        }
409
410        // return always the same string representation for this
411        // instance (immutable)
412        //
413        return myToString;
414    }
415
416}
417