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 com.sun.jmx.mbeanserver.Introspector;
  29 import java.lang.annotation.Annotation;
  30 import java.lang.reflect.Method;
  31 import java.util.Arrays;
  32 import java.util.Objects;
  33 
  34 /**
  35  * Describes a management operation exposed by an MBean.  Instances of
  36  * this class are immutable.  Subclasses may be mutable but this is
  37  * not recommended.
  38  *
  39  * @since 1.5
  40  */
  41 public class MBeanOperationInfo extends MBeanFeatureInfo implements Cloneable {
  42 
  43     /* Serial version */
  44     static final long serialVersionUID = -6178860474881375330L;
  45 
  46     static final MBeanOperationInfo[] NO_OPERATIONS =
  47         new MBeanOperationInfo[0];
  48 
  49     /**
  50      * Indicates that the operation is read-like:
  51      * it returns information but does not change any state.
  52      */
  53     public static final int INFO = 0;
  54 
  55     /**
  56      * Indicates that the operation is write-like: it has an effect but does
  57      * not return any information from the MBean.
  58      */
  59     public static final int ACTION = 1;
  60 
  61     /**
  62      * Indicates that the operation is both read-like and write-like:
  63      * it has an effect, and it also returns information from the MBean.
  64      */
  65     public static final int ACTION_INFO = 2;
  66 
  67     /**
  68      * Indicates that the impact of the operation is unknown or cannot be
  69      * expressed using one of the other values.
  70      */
  71     public static final int UNKNOWN = 3;
  72 
  73     /**
  74      * @serial The method's return value.
  75      */
  76     private final String type;
  77 
  78     /**
  79      * @serial The signature of the method, that is, the class names
  80      * of the arguments.
  81      */
  82     private final MBeanParameterInfo[] signature;
  83 
  84     /**
  85      * @serial The impact of the method, one of
  86      *         {@code INFO, ACTION, ACTION_INFO, UNKNOWN}.
  87      */
  88     private final int impact;
  89 
  90     /** @see MBeanInfo#arrayGettersSafe */
  91     private final transient boolean arrayGettersSafe;
  92 
  93 
  94     /**
  95      * Constructs an {@code MBeanOperationInfo} object.  The
  96      * {@link Descriptor} of the constructed object will include
  97      * fields contributed by any annotations on the {@code Method}
  98      * object that contain the {@link DescriptorKey} meta-annotation.
  99      *
 100      * @param method The {@code java.lang.reflect.Method} object
 101      * describing the MBean operation.
 102      * @param description A human readable description of the operation.
 103      */
 104     public MBeanOperationInfo(String description, Method method) {
 105         this(method.getName(),
 106              description,
 107              methodSignature(method),
 108              method.getReturnType().getName(),
 109              UNKNOWN,
 110              Introspector.descriptorForElement(method));
 111     }
 112 
 113     /**
 114      * Constructs an {@code MBeanOperationInfo} object.
 115      *
 116      * @param name The name of the method.
 117      * @param description A human readable description of the operation.
 118      * @param signature {@code MBeanParameterInfo} objects
 119      * describing the parameters(arguments) of the method.  This may be
 120      * null with the same effect as a zero-length array.
 121      * @param type The type of the method's return value.
 122      * @param impact The impact of the method, one of
 123      * {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO},
 124      * {@link #UNKNOWN}.
 125      */
 126     public MBeanOperationInfo(String name,
 127                               String description,
 128                               MBeanParameterInfo[] signature,
 129                               String type,
 130                               int impact) {
 131         this(name, description, signature, type, impact, (Descriptor) null);
 132     }
 133 
 134     /**
 135      * Constructs an {@code MBeanOperationInfo} object.
 136      *
 137      * @param name The name of the method.
 138      * @param description A human readable description of the operation.
 139      * @param signature {@code MBeanParameterInfo} objects
 140      * describing the parameters(arguments) of the method.  This may be
 141      * null with the same effect as a zero-length array.
 142      * @param type The type of the method's return value.
 143      * @param impact The impact of the method, one of
 144      * {@link #INFO}, {@link #ACTION}, {@link #ACTION_INFO},
 145      * {@link #UNKNOWN}.
 146      * @param descriptor The descriptor for the operation.  This may be null
 147      * which is equivalent to an empty descriptor.
 148      *
 149      * @since 1.6
 150      */
 151     public MBeanOperationInfo(String name,
 152                               String description,
 153                               MBeanParameterInfo[] signature,
 154                               String type,
 155                               int impact,
 156                               Descriptor descriptor) {
 157 
 158         super(name, description, descriptor);
 159 
 160         if (signature == null || signature.length == 0)
 161             signature = MBeanParameterInfo.NO_PARAMS;
 162         else
 163             signature = signature.clone();
 164         this.signature = signature;
 165         this.type = type;
 166         this.impact = impact;
 167         this.arrayGettersSafe =
 168             MBeanInfo.arrayGettersSafe(this.getClass(),
 169                                        MBeanOperationInfo.class);
 170     }
 171 
 172     /**
 173      * <p>Returns a shallow clone of this instance.
 174      * The clone is obtained by simply calling {@code super.clone()},
 175      * thus calling the default native shallow cloning mechanism
 176      * implemented by {@code Object.clone()}.
 177      * No deeper cloning of any internal field is made.</p>
 178      *
 179      * <p>Since this class is immutable, cloning is chiefly of interest
 180      * to subclasses.</p>
 181      */
 182      @Override
 183      public Object clone () {
 184          try {
 185              return super.clone() ;
 186          } catch (CloneNotSupportedException e) {
 187              // should not happen as this class is cloneable
 188              return null;
 189          }
 190      }
 191 
 192     /**
 193      * Returns the type of the method's return value.
 194      *
 195      * @return the return type.
 196      */
 197     public String getReturnType() {
 198         return type;
 199     }
 200 
 201     /**
 202      * <p>Returns the list of parameters for this operation.  Each
 203      * parameter is described by an {@code MBeanParameterInfo}
 204      * object.</p>
 205      *
 206      * <p>The returned array is a shallow copy of the internal array,
 207      * which means that it is a copy of the internal array of
 208      * references to the {@code MBeanParameterInfo} objects but
 209      * that each referenced {@code MBeanParameterInfo} object is
 210      * not copied.</p>
 211      *
 212      * @return  An array of {@code MBeanParameterInfo} objects.
 213      */
 214     public MBeanParameterInfo[] getSignature() {
 215         // If MBeanOperationInfo was created in our implementation,
 216         // signature cannot be null - because our constructors replace
 217         // null with MBeanParameterInfo.NO_PARAMS;
 218         //
 219         // However, signature could be null if an  MBeanOperationInfo is
 220         // deserialized from a byte array produced by another implementation.
 221         // This is not very likely but possible, since the serial form says
 222         // nothing against it. (see 6373150)
 223         //
 224         if (signature == null)
 225             // if signature is null simply return an empty array .
 226             //
 227             return MBeanParameterInfo.NO_PARAMS;
 228         else if (signature.length == 0)
 229             return signature;
 230         else
 231             return signature.clone();
 232     }
 233 
 234     private MBeanParameterInfo[] fastGetSignature() {
 235         if (arrayGettersSafe) {
 236             // if signature is null simply return an empty array .
 237             // see getSignature() above.
 238             //
 239             if (signature == null)
 240                 return MBeanParameterInfo.NO_PARAMS;
 241             else return signature;
 242         } else return getSignature();
 243     }
 244 
 245     /**
 246      * Returns the impact of the method, one of
 247      * {@code INFO, ACTION, ACTION_INFO, UNKNOWN}.
 248      *
 249      * @return the impact code.
 250      */
 251     public int getImpact() {
 252         return impact;
 253     }
 254 
 255     @Override
 256     public String toString() {
 257         String impactString;
 258         switch (getImpact()) {
 259         case ACTION: impactString = "action"; break;
 260         case ACTION_INFO: impactString = "action/info"; break;
 261         case INFO: impactString = "info"; break;
 262         case UNKNOWN: impactString = "unknown"; break;
 263         default: impactString = "(" + getImpact() + ")";
 264         }
 265         return getClass().getName() + "[" +
 266             "description=" + getDescription() + ", " +
 267             "name=" + getName() + ", " +
 268             "returnType=" + getReturnType() + ", " +
 269             "signature=" + Arrays.asList(fastGetSignature()) + ", " +
 270             "impact=" + impactString + ", " +
 271             "descriptor=" + getDescriptor() +
 272             "]";
 273     }
 274 
 275     /**
 276      * Compare this MBeanOperationInfo to another.
 277      *
 278      * @param o the object to compare to.
 279      *
 280      * @return true if and only if {@code o} is an MBeanOperationInfo such
 281      * that its {@link #getName()}, {@link #getReturnType()}, {@link
 282      * #getDescription()}, {@link #getImpact()}, {@link #getDescriptor()}
 283      * and {@link #getSignature()} values are equal (not necessarily identical)
 284      * to those of this MBeanConstructorInfo.  Two signature arrays
 285      * are equal if their elements are pairwise equal.
 286      */
 287     @Override
 288     public boolean equals(Object o) {
 289         if (o == this)
 290             return true;
 291         if (!(o instanceof MBeanOperationInfo))
 292             return false;
 293         MBeanOperationInfo p = (MBeanOperationInfo) o;
 294         return (Objects.equals(p.getName(), getName()) &&
 295                 Objects.equals(p.getReturnType(), getReturnType()) &&
 296                 Objects.equals(p.getDescription(), getDescription()) &&
 297                 p.getImpact() == getImpact() &&
 298                 Arrays.equals(p.fastGetSignature(), fastGetSignature()) &&
 299                 Objects.equals(p.getDescriptor(), getDescriptor()));
 300     }
 301 
 302     /* We do not include everything in the hashcode.  We assume that
 303        if two operations are different they'll probably have different
 304        names or types.  The penalty we pay when this assumption is
 305        wrong should be less than the penalty we would pay if it were
 306        right and we needlessly hashed in the description and the
 307        parameter array.  */
 308     @Override
 309     public int hashCode() {
 310         return Objects.hash(getName(), getReturnType());
 311     }
 312 
 313     private static MBeanParameterInfo[] methodSignature(Method method) {
 314         final Class<?>[] classes = method.getParameterTypes();
 315         final Annotation[][] annots = method.getParameterAnnotations();
 316         return parameters(classes, annots);
 317     }
 318 
 319     static MBeanParameterInfo[] parameters(Class<?>[] classes,
 320                                            Annotation[][] annots) {
 321         final MBeanParameterInfo[] params =
 322             new MBeanParameterInfo[classes.length];
 323         assert(classes.length == annots.length);
 324 
 325         for (int i = 0; i < classes.length; i++) {
 326             Descriptor d = Introspector.descriptorForAnnotations(annots[i]);
 327             final String pn = "p" + (i + 1);
 328             params[i] =
 329                 new MBeanParameterInfo(pn, classes[i].getName(), "", d);
 330         }
 331 
 332         return params;
 333     }
 334 }