1 /*
   2  * Copyright (c) 1999, 2017, 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      * @throws IllegalArgumentException if {@code impact} is not one of {@code
 150      * ACTION}, {@code ACTION_INFO}, {@code INFO}, or {@code UNKNOWN}.
 151      *
 152      * @since 1.6
 153      */
 154     public MBeanOperationInfo(String name,
 155                               String description,
 156                               MBeanParameterInfo[] signature,
 157                               String type,
 158                               int impact,
 159                               Descriptor descriptor) {
 160 
 161         super(name, description, descriptor);
 162 
 163         if (impact != ACTION && impact != ACTION_INFO && impact != INFO
 164                 && impact != UNKNOWN) {
 165             throw new IllegalArgumentException("Argument impact can only be "
 166                     + "one of ACTION, ACTION_INFO, "
 167                     + "INFO, or UNKNOWN: " + impact);
 168         }
 169 
 170         if (signature == null || signature.length == 0)
 171             signature = MBeanParameterInfo.NO_PARAMS;
 172         else
 173             signature = signature.clone();
 174         this.signature = signature;
 175         this.type = type;
 176         this.impact = impact;
 177         this.arrayGettersSafe =
 178             MBeanInfo.arrayGettersSafe(this.getClass(),
 179                                        MBeanOperationInfo.class);
 180     }
 181 
 182     /**
 183      * <p>Returns a shallow clone of this instance.
 184      * The clone is obtained by simply calling {@code super.clone()},
 185      * thus calling the default native shallow cloning mechanism
 186      * implemented by {@code Object.clone()}.
 187      * No deeper cloning of any internal field is made.</p>
 188      *
 189      * <p>Since this class is immutable, cloning is chiefly of interest
 190      * to subclasses.</p>
 191      */
 192      @Override
 193      public Object clone () {
 194          try {
 195              return super.clone() ;
 196          } catch (CloneNotSupportedException e) {
 197              // should not happen as this class is cloneable
 198              return null;
 199          }
 200      }
 201 
 202     /**
 203      * Returns the type of the method's return value.
 204      *
 205      * @return the return type.
 206      */
 207     public String getReturnType() {
 208         return type;
 209     }
 210 
 211     /**
 212      * <p>Returns the list of parameters for this operation.  Each
 213      * parameter is described by an {@code MBeanParameterInfo}
 214      * object.</p>
 215      *
 216      * <p>The returned array is a shallow copy of the internal array,
 217      * which means that it is a copy of the internal array of
 218      * references to the {@code MBeanParameterInfo} objects but
 219      * that each referenced {@code MBeanParameterInfo} object is
 220      * not copied.</p>
 221      *
 222      * @return  An array of {@code MBeanParameterInfo} objects.
 223      */
 224     public MBeanParameterInfo[] getSignature() {
 225         // If MBeanOperationInfo was created in our implementation,
 226         // signature cannot be null - because our constructors replace
 227         // null with MBeanParameterInfo.NO_PARAMS;
 228         //
 229         // However, signature could be null if an  MBeanOperationInfo is
 230         // deserialized from a byte array produced by another implementation.
 231         // This is not very likely but possible, since the serial form says
 232         // nothing against it. (see 6373150)
 233         //
 234         if (signature == null)
 235             // if signature is null simply return an empty array .
 236             //
 237             return MBeanParameterInfo.NO_PARAMS;
 238         else if (signature.length == 0)
 239             return signature;
 240         else
 241             return signature.clone();
 242     }
 243 
 244     private MBeanParameterInfo[] fastGetSignature() {
 245         if (arrayGettersSafe) {
 246             // if signature is null simply return an empty array .
 247             // see getSignature() above.
 248             //
 249             if (signature == null)
 250                 return MBeanParameterInfo.NO_PARAMS;
 251             else return signature;
 252         } else return getSignature();
 253     }
 254 
 255     /**
 256      * Returns the impact of the method, one of
 257      * {@code INFO, ACTION, ACTION_INFO, UNKNOWN}.
 258      *
 259      * @return the impact code.
 260      */
 261     public int getImpact() {
 262         return impact;
 263     }
 264 
 265     @Override
 266     public String toString() {
 267         String impactString = "";
 268         switch (getImpact()) {
 269         case ACTION: impactString = "action"; break;
 270         case ACTION_INFO: impactString = "action/info"; break;
 271         case INFO: impactString = "info"; break;
 272         case UNKNOWN: impactString = "unknown"; break;
 273         }
 274         return getClass().getName() + "[" +
 275             "description=" + getDescription() + ", " +
 276             "name=" + getName() + ", " +
 277             "returnType=" + getReturnType() + ", " +
 278             "signature=" + Arrays.asList(fastGetSignature()) + ", " +
 279             "impact=" + impactString + ", " +
 280             "descriptor=" + getDescriptor() +
 281             "]";
 282     }
 283 
 284     /**
 285      * Compare this MBeanOperationInfo to another.
 286      *
 287      * @param o the object to compare to.
 288      *
 289      * @return true if and only if {@code o} is an MBeanOperationInfo such
 290      * that its {@link #getName()}, {@link #getReturnType()}, {@link
 291      * #getDescription()}, {@link #getImpact()}, {@link #getDescriptor()}
 292      * and {@link #getSignature()} values are equal (not necessarily identical)
 293      * to those of this MBeanConstructorInfo.  Two signature arrays
 294      * are equal if their elements are pairwise equal.
 295      */
 296     @Override
 297     public boolean equals(Object o) {
 298         if (o == this)
 299             return true;
 300         if (!(o instanceof MBeanOperationInfo))
 301             return false;
 302         MBeanOperationInfo p = (MBeanOperationInfo) o;
 303         return (Objects.equals(p.getName(), getName()) &&
 304                 Objects.equals(p.getReturnType(), getReturnType()) &&
 305                 Objects.equals(p.getDescription(), getDescription()) &&
 306                 p.getImpact() == getImpact() &&
 307                 Arrays.equals(p.fastGetSignature(), fastGetSignature()) &&
 308                 Objects.equals(p.getDescriptor(), getDescriptor()));
 309     }
 310 
 311     /* We do not include everything in the hashcode.  We assume that
 312        if two operations are different they'll probably have different
 313        names or types.  The penalty we pay when this assumption is
 314        wrong should be less than the penalty we would pay if it were
 315        right and we needlessly hashed in the description and the
 316        parameter array.  */
 317     @Override
 318     public int hashCode() {
 319         return Objects.hash(getName(), getReturnType());
 320     }
 321 
 322     private static MBeanParameterInfo[] methodSignature(Method method) {
 323         final Class<?>[] classes = method.getParameterTypes();
 324         final Annotation[][] annots = method.getParameterAnnotations();
 325         return parameters(classes, annots);
 326     }
 327 
 328     static MBeanParameterInfo[] parameters(Class<?>[] classes,
 329                                            Annotation[][] annots) {
 330         final MBeanParameterInfo[] params =
 331             new MBeanParameterInfo[classes.length];
 332         assert(classes.length == annots.length);
 333 
 334         for (int i = 0; i < classes.length; i++) {
 335             Descriptor d = Introspector.descriptorForAnnotations(annots[i]);
 336             final String pn = "p" + (i + 1);
 337             params[i] =
 338                 new MBeanParameterInfo(pn, classes[i].getName(), "", d);
 339         }
 340 
 341         return params;
 342     }
 343 }