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