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.io.IOException;
29  import java.io.ObjectInputStream;
30  import java.io.ObjectOutputStream;
31  import java.io.Serializable;
32  import java.io.StreamCorruptedException;
33  import java.util.Objects;
34  
35  /**
36   * <p>Provides general information for an MBean descriptor object.
37   * The feature described can be an attribute, an operation, a
38   * parameter, or a notification.  Instances of this class are
39   * immutable.  Subclasses may be mutable but this is not
40   * recommended.</p>
41   *
42   * @since 1.5
43   */
44  public class MBeanFeatureInfo implements Serializable, DescriptorRead {
45  
46  
47      /* Serial version */
48      static final long serialVersionUID = 3952882688968447265L;
49  
50      /**
51       * The name of the feature.  It is recommended that subclasses call
52       * {@link #getName} rather than reading this field, and that they
53       * not change it.
54       *
55       * @serial The name of the feature.
56       */
57      protected String name;
58  
59      /**
60       * The human-readable description of the feature.  It is
61       * recommended that subclasses call {@link #getDescription} rather
62       * than reading this field, and that they not change it.
63       *
64       * @serial The human-readable description of the feature.
65       */
66      protected String description;
67  
68      /**
69       * @serial The Descriptor for this MBeanFeatureInfo.  This field
70       * can be null, which is equivalent to an empty Descriptor.
71       */
72      private transient Descriptor descriptor;
73  
74  
75      /**
76       * Constructs an <CODE>MBeanFeatureInfo</CODE> object.  This
77       * constructor is equivalent to {@code MBeanFeatureInfo(name,
78       * description, (Descriptor) null}.
79       *
80       * @param name The name of the feature.
81       * @param description A human readable description of the feature.
82       */
83      public MBeanFeatureInfo(String name, String description) {
84          this(name, description, null);
85      }
86  
87      /**
88       * Constructs an <CODE>MBeanFeatureInfo</CODE> object.
89       *
90       * @param name The name of the feature.
91       * @param description A human readable description of the feature.
92       * @param descriptor The descriptor for the feature.  This may be null
93       * which is equivalent to an empty descriptor.
94       *
95       * @since 1.6
96       */
97      public MBeanFeatureInfo(String name, String description,
98                              Descriptor descriptor) {
99          this.name = name;
100         this.description = description;
101         this.descriptor = descriptor;
102     }
103 
104     /**
105      * Returns the name of the feature.
106      *
107      * @return the name of the feature.
108      */
109     public String getName() {
110         return name;
111     }
112 
113     /**
114      * Returns the human-readable description of the feature.
115      *
116      * @return the human-readable description of the feature.
117      */
118     public String getDescription() {
119         return description;
120     }
121 
122     /**
123      * Returns the descriptor for the feature.  Changing the returned value
124      * will have no affect on the original descriptor.
125      *
126      * @return a descriptor that is either immutable or a copy of the original.
127      *
128      * @since 1.6
129      */
130     public Descriptor getDescriptor() {
131         return (Descriptor) ImmutableDescriptor.nonNullDescriptor(descriptor).clone();
132     }
133 
134     /**
135      * Compare this MBeanFeatureInfo to another.
136      *
137      * @param o the object to compare to.
138      *
139      * @return true if and only if <code>o</code> is an MBeanFeatureInfo such
140      * that its {@link #getName()}, {@link #getDescription()}, and
141      * {@link #getDescriptor()}
142      * values are equal (not necessarily identical) to those of this
143      * MBeanFeatureInfo.
144      */
145     public boolean equals(Object o) {
146         if (o == this)
147             return true;
148         if (!(o instanceof MBeanFeatureInfo))
149             return false;
150         MBeanFeatureInfo p = (MBeanFeatureInfo) o;
151         return (Objects.equals(p.getName(), getName()) &&
152                 Objects.equals(p.getDescription(), getDescription()) &&
153                 Objects.equals(p.getDescriptor(), getDescriptor()));
154     }
155 
156     public int hashCode() {
157         return getName().hashCode() ^ getDescription().hashCode() ^
158                getDescriptor().hashCode();
159     }
160 
161     /**
162      * Serializes an {@link MBeanFeatureInfo} to an {@link ObjectOutputStream}.
163      * @serialData
164      * For compatibility reasons, an object of this class is serialized as follows.
165      * <p>
166      * The method {@link ObjectOutputStream#defaultWriteObject defaultWriteObject()}
167      * is called first to serialize the object except the field {@code descriptor}
168      * which is declared as transient. The field {@code descriptor} is serialized
169      * as follows:
170      *     <ul>
171      *     <li>If {@code descriptor} is an instance of the class
172      *        {@link ImmutableDescriptor}, the method {@link ObjectOutputStream#write
173      *        write(int val)} is called to write a byte with the value {@code 1},
174      *        then the method {@link ObjectOutputStream#writeObject writeObject(Object obj)}
175      *        is called twice to serialize the field names and the field values of the
176      *        {@code descriptor}, respectively as a {@code String[]} and an
177      *        {@code Object[]};</li>
178      *     <li>Otherwise, the method {@link ObjectOutputStream#write write(int val)}
179      * is called to write a byte with the value {@code 0}, then the method
180      * {@link ObjectOutputStream#writeObject writeObject(Object obj)} is called
181      * to serialize directly the field {@code descriptor}.
182      *     </ul>
183      *
184      * @since 1.6
185      */
186     private void writeObject(ObjectOutputStream out) throws IOException {
187         out.defaultWriteObject();
188 
189         if (descriptor != null &&
190             descriptor.getClass() == ImmutableDescriptor.class) {
191 
192             out.write(1);
193 
194             final String[] names = descriptor.getFieldNames();
195 
196             out.writeObject(names);
197             out.writeObject(descriptor.getFieldValues(names));
198         } else {
199             out.write(0);
200 
201             out.writeObject(descriptor);
202         }
203     }
204 
205     /**
206      * Deserializes an {@link MBeanFeatureInfo} from an {@link ObjectInputStream}.
207      * @serialData
208      * For compatibility reasons, an object of this class is deserialized as follows.
209      * <p>
210      * The method {@link ObjectInputStream#defaultReadObject defaultReadObject()}
211      * is called first to deserialize the object except the field
212      * {@code descriptor}, which is not serialized in the default way. Then the method
213      * {@link ObjectInputStream#read read()} is called to read a byte, the field
214      * {@code descriptor} is deserialized according to the value of the byte value:
215      *    <ul>
216      *    <li>1. The method {@link ObjectInputStream#readObject readObject()}
217      *       is called twice to obtain the field names (a {@code String[]}) and
218      *       the field values (a {@code Object[]}) of the {@code descriptor}.
219      *       The two obtained values then are used to construct
220      *       an {@link ImmutableDescriptor} instance for the field
221      *       {@code descriptor};</li>
222      *    <li>0. The value for the field {@code descriptor} is obtained directly
223      *       by calling the method {@link ObjectInputStream#readObject readObject()}.
224      *       If the obtained value is null, the field {@code descriptor} is set to
225      *       {@link ImmutableDescriptor#EMPTY_DESCRIPTOR EMPTY_DESCRIPTOR};</li>
226      *    <li>-1. This means that there is no byte to read and that the object is from
227      *       an earlier version of the JMX API. The field {@code descriptor} is set
228      *       to {@link ImmutableDescriptor#EMPTY_DESCRIPTOR EMPTY_DESCRIPTOR}</li>
229      *    <li>Any other value. A {@link StreamCorruptedException} is thrown.</li>
230      *    </ul>
231      *
232      * @since 1.6
233      */
234     private void readObject(ObjectInputStream in)
235         throws IOException, ClassNotFoundException {
236 
237         in.defaultReadObject();
238 
239         switch (in.read()) {
240         case 1:
241             final String[] names = (String[])in.readObject();
242 
243             final Object[] values = (Object[]) in.readObject();
244             descriptor = (names.length == 0) ?
245                 ImmutableDescriptor.EMPTY_DESCRIPTOR :
246                 new ImmutableDescriptor(names, values);
247 
248             break;
249         case 0:
250             descriptor = (Descriptor)in.readObject();
251 
252             if (descriptor == null) {
253                 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR;
254             }
255 
256             break;
257         case -1: // from an earlier version of the JMX API
258             descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR;
259 
260             break;
261         default:
262             throw new StreamCorruptedException("Got unexpected byte.");
263         }
264     }
265 }