1 /*
   2  * Copyright (c) 2005, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 6204469
  27  * @summary Test that Open MBean attributes and parameters cannot have
  28  * illegal constraints like min greater than max
  29  * @author Eamonn McManus
  30  * @run clean BadConstraintTest
  31  * @run build BadConstraintTest
  32  * @run main BadConstraintTest
  33  */
  34 
  35 import java.io.*;
  36 import java.lang.reflect.*;
  37 import java.util.*;
  38 import javax.management.*;
  39 import javax.management.openmbean.*;
  40 
  41 public class BadConstraintTest {
  42     private static String failure;
  43 
  44     public static void main(String[] args) throws Exception {
  45         genericTests();
  46         descriptorTests();
  47 
  48         if (failure == null)
  49             System.out.println("Test passed");
  50         else
  51             throw new Exception("TEST FAILED: " + failure);
  52     }
  53 
  54     private static void genericTests() throws Exception {
  55         for (Object[] test : tests) {
  56             if (test.length != 5) {
  57                 throw new Exception("Test element has wrong length: " +
  58                                     toString(test));
  59             }
  60 
  61             OpenType<?> openType = (OpenType<?>) test[0];
  62             Object defaultValue = test[1];
  63             Comparable<?> minValue = (Comparable<?>) test[2];
  64             Comparable<?> maxValue = (Comparable<?>) test[3];
  65             Object[] legalValues = (Object[]) test[4];
  66 
  67             System.out.println("test: openType=" + openType +
  68                                "; defaultValue=" + defaultValue +
  69                                "; minValue=" + minValue +
  70                                "; maxValue=" + maxValue +
  71                                "; legalValues=" + toString(legalValues));
  72 
  73             genericTest(openType, defaultValue, minValue, maxValue,
  74                         legalValues);
  75         }
  76     }
  77 
  78     private static void descriptorTests() throws Exception {
  79         for (Object[][] test : descriptorTests) {
  80             if (test.length != 2) {
  81                 throw new Exception("Test element has wrong length: " +
  82                                     toString(test));
  83             }
  84 
  85             if (test[0].length != 1) {
  86                 throw new Exception("Test element should have one OpenType: " +
  87                                     toString(test[0]));
  88             }
  89 
  90             OpenType<?> openType = (OpenType<?>) test[0][0];
  91             Descriptor d = descriptor(test[1]);
  92 
  93             System.out.println("test: openType=" + openType +
  94                                "; descriptor=" + d);
  95 
  96             descriptorTest(openType, d);
  97         }
  98     }
  99 
 100     /* Tests that apply to both the Descriptor and the non-Descriptor
 101        constructors.  We invoke the non-Descriptor constructors by
 102        reflection, then we make the corresponding Descriptor and call
 103        the descriptorTest with it.  */
 104     private static void genericTest(OpenType<?> openType,
 105                                     Object defaultValue,
 106                                     Comparable<?> minValue,
 107                                     Comparable<?> maxValue,
 108                                     Object[] legalValues)
 109             throws Exception {
 110 
 111         if (minValue == null && maxValue == null && legalValues == null) {
 112             if (defaultValue == null)
 113                 throw new Exception("What am I testing?");
 114             Class[] params1 = new Class[] {
 115                 String.class, String.class, OpenType.class,
 116                 boolean.class, boolean.class, boolean.class,
 117                 Object.class
 118             };
 119             Constructor<OpenMBeanAttributeInfoSupport> c1 =
 120                 OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
 121             Class[] params2 = new Class[] {
 122                 String.class, String.class, OpenType.class,
 123                 Object.class
 124             };
 125             Constructor<OpenMBeanParameterInfoSupport> c2 =
 126                 OpenMBeanParameterInfoSupport.class.getConstructor(params2);
 127             ode(c1, "name", "descr", openType, true, true, false, defaultValue);
 128             ode(c2, "name", "descr", openType, defaultValue);
 129             descriptorTest(openType,
 130                            descriptor("defaultValue", defaultValue));
 131             descriptorTest(openType,
 132                            descriptor("defaultValue", string(defaultValue)));
 133         }
 134 
 135         if (legalValues == null) {
 136             Class[] params1 = new Class[] {
 137                 String.class, String.class, OpenType.class,
 138                 boolean.class, boolean.class, boolean.class,
 139                 Object.class, Comparable.class, Comparable.class
 140             };
 141             Constructor<OpenMBeanAttributeInfoSupport> c1 =
 142                 OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
 143             Class[] params2 = new Class[] {
 144                 String.class, String.class, OpenType.class,
 145                 Object.class, Comparable.class, Comparable.class
 146             };
 147             Constructor<OpenMBeanParameterInfoSupport> c2 =
 148                 OpenMBeanParameterInfoSupport.class.getConstructor(params2);
 149             ode(c1, "name", "descr", openType, true, true, false, defaultValue,
 150                 minValue, maxValue);
 151             ode(c2, "name", "descr", openType, defaultValue,
 152                 minValue, maxValue);
 153             descriptorTest(openType,
 154                            descriptor("defaultValue", defaultValue,
 155                                       "minValue", minValue,
 156                                       "maxValue", maxValue));
 157             descriptorTest(openType,
 158                            descriptor("defaultValue", string(defaultValue),
 159                                       "minValue", string(minValue),
 160                                       "maxValue", string(maxValue)));
 161         }
 162 
 163         if (legalValues != null) {
 164             Class[] params1 = new Class[] {
 165                 String.class, String.class, OpenType.class,
 166                 boolean.class, boolean.class, boolean.class,
 167                 Object.class, Object[].class
 168             };
 169             Constructor<OpenMBeanAttributeInfoSupport> c1 =
 170                 OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
 171             Class[] params2 = new Class[] {
 172                 String.class, String.class, OpenType.class,
 173                 Object.class, Object[].class
 174             };
 175             Constructor<OpenMBeanParameterInfoSupport> c2 =
 176                 OpenMBeanParameterInfoSupport.class.getConstructor(params2);
 177             ode(c1, "name", "descr", openType, true, true, false, defaultValue,
 178                 legalValues);
 179             ode(c2, "name", "descr", openType, defaultValue,
 180                 legalValues);
 181             descriptorTest(openType,
 182                            descriptor("defaultValue", defaultValue,
 183                                       "legalValues", legalValues));
 184             descriptorTest(openType,
 185                            descriptor("defaultValue", defaultValue,
 186                                       "legalValues", arraySet(legalValues)));
 187             Set<String> strings = new HashSet<String>();
 188             for (Object x : legalValues)
 189                 strings.add(x.toString());
 190             descriptorTest(openType,
 191                            descriptor("defaultValue", defaultValue,
 192                                       "legalValues", strings));
 193             descriptorTest(openType,
 194                            descriptor("defaultValue", defaultValue,
 195                                       "legalValues",
 196                                       strings.toArray(new String[0])));
 197         }
 198     }
 199 
 200     private static void descriptorTest(OpenType<?> openType, Descriptor d)
 201             throws Exception {
 202         Class[] params1 = new Class[] {
 203             String.class, String.class, OpenType.class,
 204             boolean.class, boolean.class, boolean.class,
 205             Descriptor.class
 206         };
 207         Constructor<OpenMBeanAttributeInfoSupport> c1 =
 208             OpenMBeanAttributeInfoSupport.class.getConstructor(params1);
 209         Class[] params2 = new Class[] {
 210             String.class, String.class, OpenType.class,
 211             Descriptor.class
 212         };
 213         Constructor<OpenMBeanParameterInfoSupport> c2 =
 214             OpenMBeanParameterInfoSupport.class.getConstructor(params2);
 215         iae(c1, "name", "descr", openType, true, true, false, d);
 216         iae(c2, "name", "descr", openType, d);
 217     }
 218 
 219     /* Check that the given constructor invocation gets an
 220        IllegalArgumentException. */
 221     private static void iae(Constructor<?> con, Object... params) {
 222         checkException(IllegalArgumentException.class, con, params);
 223     }
 224 
 225     /* Check that the given constructor invocation gets an
 226        OpenDataException.  */
 227     private static void ode(Constructor<?> con, Object... params) {
 228         checkException(OpenDataException.class, con, params);
 229     }
 230 
 231     private static void checkException(Class<? extends Exception> exc,
 232                                        Constructor<?> con, Object[] params) {
 233         try {
 234             con.newInstance(params);
 235             fail("Constructor succeeded but should have got " + exc.getName() +
 236                  " with params " + Arrays.deepToString(params));
 237         } catch (InvocationTargetException e) {
 238             Throwable cause = e.getCause();
 239             if (exc.isInstance(cause))
 240                 return;
 241             StringWriter sw = new StringWriter();
 242             PrintWriter pw = new PrintWriter(sw);
 243             cause.printStackTrace(pw);
 244             pw.close();
 245             fail("Constructor should have got " + exc.getName() +
 246                  " with params " + Arrays.deepToString(params) + ": " + sw);
 247         } catch (Exception e) {
 248             throw new IllegalArgumentException("Reflection failed", e);
 249         }
 250     }
 251 
 252     private static void fail(String why) {
 253         System.out.println("FAILED: " + why);
 254         failure = why;
 255     }
 256 
 257     private static Descriptor descriptor(Object... entries) {
 258         if (entries.length % 2 != 0)
 259             throw new RuntimeException("Odd length descriptor entries");
 260         String[] names = new String[entries.length / 2];
 261         Object[] values = new Object[entries.length / 2];
 262         for (int i = 0; i < entries.length; i += 2) {
 263             names[i / 2] = (String) entries[i];
 264             values[i / 2] = entries[i + 1];
 265         }
 266         return new ImmutableDescriptor(names, values);
 267     }
 268 
 269     private static <T> Set<T> arraySet(T[] array) {
 270         return new HashSet<T>(Arrays.asList(array));
 271     }
 272 
 273     private static String toString(Object x) {
 274         if (x == null)
 275             return "null";
 276         else if (x.getClass().isArray()) {
 277             StringBuilder sb = new StringBuilder("[");
 278             int len = Array.getLength(x);
 279             for (int i = 0; i < len; i++) {
 280                 if (i > 0)
 281                     sb.append(", ");
 282                 sb.append(toString(Array.get(x, i)));
 283             }
 284             sb.append("]");
 285             return sb.toString();
 286         } else
 287             return x.toString();
 288     }
 289 
 290     private static String string(Object x) {
 291         if (x == null)
 292             return null;
 293         return toString(x);
 294     }
 295 
 296     private static final OpenType<?>
 297         ostring = SimpleType.STRING,
 298         oint = SimpleType.INTEGER,
 299         obool = SimpleType.BOOLEAN,
 300         olong = SimpleType.LONG,
 301         obyte = SimpleType.BYTE,
 302         ofloat = SimpleType.FLOAT,
 303         odouble = SimpleType.DOUBLE,
 304         ostringarray, ostringarray2;
 305     private static final CompositeType ocomposite;
 306     private static final CompositeData compositeData, compositeData2;
 307     static {
 308         try {
 309             ostringarray = new ArrayType<String[]>(1, ostring);
 310             ostringarray2 = new ArrayType<String[][]>(2, ostring);
 311             ocomposite =
 312                 new CompositeType("name", "descr",
 313                                   new String[] {"s", "i"},
 314                                   new String[] {"sdesc", "idesc"},
 315                                   new OpenType[] {ostring, oint});
 316             compositeData =
 317                 new CompositeDataSupport(ocomposite,
 318                                          new String[] {"s", "i"},
 319                                          new Object[] {"foo", 23});
 320             compositeData2 =
 321                 new CompositeDataSupport(ocomposite,
 322                                          new String[] {"s", "i"},
 323                                          new Object[] {"bar", -23});
 324         } catch (OpenDataException e) { // damn checked exceptions...
 325             throw new IllegalArgumentException(e.toString(), e);
 326         }
 327     }
 328 
 329     private static final Descriptor
 330         nullD = null,
 331         emptyD = ImmutableDescriptor.EMPTY_DESCRIPTOR;
 332 
 333     /* Each element of this array contains five Objects:
 334        - OpenType;
 335        - defaultValue;
 336        - minValue;
 337        - maxValue;
 338        - legalValues.  The objects are used to construct tests cases
 339        where all possible constructors that make sense for that
 340        combination of values are invoked, and all are checked to ensure
 341        that they throw the right exception.  */
 342     private static final Object[][] tests = {
 343 
 344         // Values must be of appropriate type
 345 
 346         {oint, "oops", null, null, null},
 347         {oint, Long.MAX_VALUE, null, null, null},
 348         {oint, null, "oops", null, null},
 349         {oint, "oops", 3, null, null},
 350         {oint, 3, "oops", null, null},
 351         {oint, null, null, "oops", null},
 352         {oint, null, 3, "oops", null},
 353         {oint, null, 3, false, null},
 354         {oint, null, null, null, new String[] {"x"}},
 355         {oint, null, null, null, new Object[] {"x"}},
 356         {oint, null, null, null, new Object[] {3, "x"}},
 357 
 358         // If defaultValue is present then it must satisfy the constraints
 359         // defined by legalValues, minValue, or maxValue when any of
 360         // these is also present
 361 
 362         {oint, 3, 4, null, null},
 363         {oint, 3, null, 2, null},
 364         {oint, 3, null, null, new Integer[] {2, 4}},
 365 
 366         // If minValue and maxValue are both present then minValue must
 367         // not be greater than maxValue
 368 
 369         {ostring, null, "z", "a", null},
 370         {oint, null, 3, 2, null},
 371 
 372         // We don't support default values or legal sets for arrays (yet)
 373 
 374         {ostringarray, new String[] {"x"}, null, null, null},
 375         {ostringarray, null, null, null, new String[][]{new String[] {"x"}}},
 376     };
 377 
 378     /* The tests here can only be expressed via Descriptors because an
 379        attempt to invoke one of the non-Descriptor constructors with
 380        the implied parameters would not compile (or would fail at the
 381        reflection stage when reflection is being used).
 382 
 383        Each element of this array contains two subarrays.  The first
 384        is an array of OpenTypes that must contain exactly one element.
 385        The second is an array of alternating field names and field
 386        values that will be used to construct a Descriptor that is supposed
 387        to fail when given to an OpenMBean*Info constructor with the given
 388        OpenType.  */
 389     private static final Object[][][] descriptorTests = {
 390 
 391         // Values must be of appropriate type
 392 
 393         {{oint},
 394          {"minValue", 25L}},
 395 
 396         {{oint},
 397          {"minValue", new Object()}}, // not even Comparable
 398         {{oint},
 399          {"maxValue", new Object()}}, // not even Comparable
 400         {{oint},
 401          {"defaultValue", 3,
 402           "minValue", new Object()}},
 403         {{oint},
 404          {"defaultValue", 3,
 405           "maxValue", new Object()}},
 406 
 407         {{oint},
 408          {"legalValues", new int[] {3}}}, // should be new Integer[] to work
 409         {{oint},
 410          {"legalValues", 3}},
 411 
 412         // If legalValues is present then neither minValue nor maxValue
 413         // must be present
 414 
 415         {{oint},
 416          {"minValue", 3, "legalValues", new Integer[] {3, 4}}},
 417         {{oint},
 418          {"maxValue", 3, "legalValues", new Integer[] {2, 3}}},
 419         {{oint},
 420          {"defaultValue", 3, "minValue", 3, "legalValues", new Integer[] {3}}},
 421 
 422         // We don't support min or max arrays (what would they mean?)
 423 
 424         {{ostringarray},
 425          {"minValue", new String[] {"x"}}},
 426         {{ostringarray},
 427          {"maxValue", new String[] {"x"}}},
 428 
 429     };
 430 }