1 /* 2 * Copyright (c) 2005, 2015, 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 check constraints 28 * @author Eamonn McManus 29 * 30 * @run clean ConstraintTest 31 * @run build ConstraintTest 32 * @run main ConstraintTest 33 */ 34 35 import java.util.*; 36 import javax.management.*; 37 import javax.management.openmbean.*; 38 39 public class ConstraintTest { 40 private static String failure; 41 42 public static void main(String[] args) throws Exception { 43 for (Object[][] test : tests) { 44 if (test.length != 4) { 45 throw new Exception("Test element has wrong length: " + 46 Arrays.deepToString(test)); 47 } 48 49 if (test[0].length != 4) { 50 throw new Exception("Test constraints should have size 4: " + 51 Arrays.deepToString(test[0])); 52 } 53 Object defaultValue = test[0][0]; 54 Comparable<?> minValue = (Comparable<?>) test[0][1]; 55 Comparable<?> maxValue = (Comparable<?>) test[0][2]; 56 Object[] legalValues = (Object[]) test[0][3]; 57 System.out.println("test: defaultValue=" + defaultValue + 58 "; minValue=" + minValue + 59 "; maxValue=" + maxValue + 60 "; legalValues=" + 61 Arrays.deepToString(legalValues)); 62 63 if (test[1].length != 1) { 64 throw new Exception("OpenType list should have size 1: " + 65 Arrays.deepToString(test[1])); 66 } 67 OpenType<?> openType = (OpenType<?>) test[1][0]; 68 69 Object[] valid = test[2]; 70 Object[] invalid = test[3]; 71 72 System.out.println("...valid=" + Arrays.deepToString(valid)); 73 System.out.println("...invalid=" + Arrays.deepToString(invalid)); 74 75 test(openType, defaultValue, minValue, maxValue, legalValues, 76 valid, invalid); 77 } 78 79 if (failure == null) 80 System.out.println("Test passed"); 81 else 82 throw new Exception("TEST FAILED: " + failure); 83 } 84 85 private static <T> void test(OpenType<T> openType, Object defaultValue, 86 Comparable<?> minValue, 87 Comparable<?> maxValue, Object[] legalValues, 88 Object[] valid, Object[] invalid) 89 throws Exception { 90 /* This hack is needed to avoid grief from the parameter checking 91 in the OpenMBean*InfoSupport constructors. Since they are defined 92 to check that the defaultValue etc are of the same type as the 93 OpenType<T>, there is no way to pass a defaultValue etc when 94 the type is OpenType<?>. So either you have to write plain 95 OpenType, and get unchecked warnings for every constructor 96 invocation, or you do this, and get the unchecked warnings just 97 here. */ 98 test1(openType, (T) defaultValue, (Comparable<T>) minValue, 99 (Comparable<T>) maxValue, (T[]) legalValues, valid, invalid); 100 } 101 102 private static <T> void test1(OpenType<T> openType, T defaultValue, 103 Comparable<T> minValue, 104 Comparable<T> maxValue, T[] legalValues, 105 Object[] valid, Object[] invalid) 106 throws Exception { 107 108 if (legalValues != null && (minValue != null || maxValue != null)) 109 throw new Exception("Test case has both legals and min/max"); 110 111 if (defaultValue == null && minValue == null && maxValue == null && 112 legalValues == null) { 113 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 114 true, true, false), 115 valid, invalid); 116 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 117 true, true, false, nullD), 118 valid, invalid); 119 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 120 true, true, false, emptyD), 121 valid, invalid); 122 test(new OpenMBeanParameterInfoSupport("name", "descr", openType), 123 valid, invalid); 124 test(new OpenMBeanParameterInfoSupport("name", "descr", openType, 125 nullD), 126 valid, invalid); 127 test(new OpenMBeanParameterInfoSupport("name", "descr", openType, 128 emptyD), 129 valid, invalid); 130 } 131 132 if (minValue == null && maxValue == null && legalValues == null) { 133 Descriptor d = descriptor("defaultValue", defaultValue); 134 test(new OpenMBeanAttributeInfoSupport("blah", "descr", openType, 135 true, true, false, d), 136 valid, invalid); 137 test(new OpenMBeanAttributeInfoSupport("blah", "descr", 138 openType, true, true, 139 false, defaultValue), 140 valid, invalid); 141 test(new OpenMBeanParameterInfoSupport("blah", "descr", openType, 142 d), 143 valid, invalid); 144 test(new OpenMBeanParameterInfoSupport("blah", "descr", openType, 145 defaultValue), 146 valid, invalid); 147 } 148 149 if (legalValues == null) { 150 Descriptor d = descriptor("defaultValue", defaultValue, 151 "minValue", minValue, 152 "maxValue", maxValue); 153 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 154 true, true, false, d), 155 valid, invalid); 156 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 157 true, true, false, 158 defaultValue, 159 minValue, maxValue), 160 valid, invalid); 161 test(new OpenMBeanParameterInfoSupport("name", "descr", openType, 162 d), 163 valid, invalid); 164 test(new OpenMBeanParameterInfoSupport("name", "descr", openType, 165 defaultValue, 166 minValue, maxValue), 167 valid, invalid); 168 } 169 170 if (minValue == null && maxValue == null) { 171 // Legal values in descriptor can be either an array or a set 172 Descriptor d1 = descriptor("defaultValue", defaultValue, 173 "legalValues", legalValues); 174 Descriptor d2; 175 if (legalValues == null) 176 d2 = d1; 177 else { 178 d2 = descriptor("defaultValue", defaultValue, 179 "legalValues", arraySet(legalValues)); 180 } 181 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 182 true, true, false, d1), 183 valid, invalid); 184 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 185 true, true, false, d2), 186 valid, invalid); 187 test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, 188 true, true, false, 189 defaultValue, legalValues), 190 valid, invalid); 191 test(new OpenMBeanParameterInfoSupport("name", "descr", openType, 192 d1), 193 valid, invalid); 194 test(new OpenMBeanParameterInfoSupport("name", "descr", openType, 195 d2), 196 valid, invalid); 197 test(new OpenMBeanParameterInfoSupport("name", "descr", openType, 198 defaultValue, legalValues), 199 valid, invalid); 200 } 201 } 202 203 /* Test one of the objects. Note that OpenMBeanAttributeInfo 204 extends OpenMBeanParameterInfo, so OpenMBeanAttributeInfoSupport 205 is-an OpenMBeanParameterInfo. */ 206 private static void test(OpenMBeanParameterInfo info, 207 Object[] valid, Object[] invalid) { 208 test1(info, valid, invalid); 209 210 // Check that the constraints can be specified as strings 211 // rather than objects 212 if (info.getOpenType() instanceof SimpleType<?>) { 213 Descriptor d = ((DescriptorRead) info).getDescriptor(); 214 String[] names = d.getFieldNames(); 215 Object[] values = d.getFieldValues(names); 216 for (int i = 0; i < values.length; i++) { 217 if (values[i] == null) 218 continue; 219 if (names[i].equals("legalValues")) { 220 Collection<?> legals; 221 if (values[i] instanceof Collection<?>) 222 legals = (Collection<?>) values[i]; 223 else 224 legals = Arrays.asList((Object[]) values[i]); 225 List<String> strings = new ArrayList<String>(); 226 for (Object legal : legals) 227 strings.add(legal.toString()); 228 values[i] = strings.toArray(new String[0]); 229 } else if (!(values[i] instanceof OpenType<?>)) 230 values[i] = values[i].toString(); 231 } 232 d = new ImmutableDescriptor(names, values); 233 OpenType<?> ot = info.getOpenType(); 234 if (info instanceof OpenMBeanAttributeInfo) { 235 OpenMBeanAttributeInfo ai = (OpenMBeanAttributeInfo) info; 236 info = new OpenMBeanAttributeInfoSupport(info.getName(), 237 info.getDescription(), 238 info.getOpenType(), 239 ai.isReadable(), 240 ai.isWritable(), 241 ai.isIs(), 242 d); 243 } else { 244 info = new OpenMBeanParameterInfoSupport(info.getName(), 245 info.getDescription(), 246 info.getOpenType(), 247 d); 248 } 249 test1(info, valid, invalid); 250 } 251 } 252 253 private static void test1(OpenMBeanParameterInfo info, 254 Object[] valid, Object[] invalid) { 255 256 for (Object x : valid) { 257 if (!info.isValue(x)) { 258 fail("Object should be valid but is not: " + x + " for: " + 259 info); 260 } 261 } 262 263 for (Object x : invalid) { 264 if (info.isValue(x)) { 265 fail("Object should not be valid but is: " + x + " for: " + 266 info); 267 } 268 } 269 270 /* If you specify e.g. minValue in a descriptor, then we arrange 271 for getMinValue() to return the same value, and if you specify 272 a minValue as a constructor parameter then we arrange for the 273 descriptor to have a minValue entry. Check that these values 274 do in fact match. */ 275 276 Descriptor d = ((DescriptorRead) info).getDescriptor(); 277 278 checkSameValue("defaultValue", info.getDefaultValue(), 279 d.getFieldValue("defaultValue")); 280 checkSameValue("minValue", info.getMinValue(), 281 d.getFieldValue("minValue")); 282 checkSameValue("maxValue", info.getMaxValue(), 283 d.getFieldValue("maxValue")); 284 checkSameValues("legalValues", info.getLegalValues(), 285 d.getFieldValue("legalValues")); 286 } 287 288 private static void checkSameValue(String what, Object getterValue, 289 Object descriptorValue) { 290 if (getterValue == null) { 291 if (descriptorValue != null) { 292 fail("Getter returned null but descriptor has entry for " + 293 what + ": " + descriptorValue); 294 } 295 } else if (descriptorValue == null) { 296 fail("Getter returned value but descriptor has no entry for " + 297 what + ": " + getterValue); 298 } else if (!getterValue.equals(descriptorValue) && 299 !getterValue.toString().equals(descriptorValue)) { 300 fail("For " + what + " getter returned " + getterValue + 301 " but descriptor entry is " + descriptorValue); 302 } 303 } 304 305 private static void checkSameValues(String what, Set<?> getterValues, 306 Object descriptorValues) { 307 if (getterValues == null) { 308 if (descriptorValues != null) { 309 fail("Getter returned null but descriptor has entry for " + 310 what + ": " + descriptorValues); 311 } 312 } else if (descriptorValues == null) { 313 fail("Getter returned value but descriptor has no entry for " + 314 what + ": " + getterValues); 315 } else { 316 Set<?> descriptorValueSet; 317 if (descriptorValues instanceof Set<?>) 318 descriptorValueSet = (Set<?>) descriptorValues; 319 else 320 descriptorValueSet = arraySet((Object[]) descriptorValues); 321 boolean same = true; 322 if (getterValues.size() != descriptorValueSet.size()) 323 same = false; 324 else { 325 for (Object x : getterValues) { 326 if (!descriptorValueSet.contains(x) 327 && !descriptorValueSet.contains(x.toString())) { 328 same = false; 329 break; 330 } 331 } 332 } 333 if (!same) { 334 fail("For " + what + " getter returned " + getterValues + 335 " but descriptor entry is " + descriptorValueSet); 336 } 337 } 338 } 339 340 private static void fail(String why) { 341 System.out.println("FAILED: " + why); 342 failure = why; 343 } 344 345 private static Descriptor descriptor(Object... entries) { 346 if (entries.length % 2 != 0) 347 throw new RuntimeException("Odd length descriptor entries"); 348 String[] names = new String[entries.length / 2]; 349 Object[] values = new Object[entries.length / 2]; 350 for (int i = 0; i < entries.length; i += 2) { 351 names[i / 2] = (String) entries[i]; 352 values[i / 2] = entries[i + 1]; 353 } 354 return new ImmutableDescriptor(names, values); 355 } 356 357 private static <T> Set<T> arraySet(T[] array) { 358 return new HashSet<T>(Arrays.asList(array)); 359 } 360 361 private static final OpenType<?> 362 ostring = SimpleType.STRING, 363 oint = SimpleType.INTEGER, 364 obool = SimpleType.BOOLEAN, 365 olong = SimpleType.LONG, 366 obyte = SimpleType.BYTE, 367 ofloat = SimpleType.FLOAT, 368 odouble = SimpleType.DOUBLE, 369 ostringarray, ostringarray2; 370 private static final CompositeType ocomposite; 371 private static final CompositeData compositeData, compositeData2; 372 static { 373 try { 374 ostringarray = new ArrayType<String[]>(1, ostring); 375 ostringarray2 = new ArrayType<String[][]>(2, ostring); 376 ocomposite = 377 new CompositeType("name", "descr", 378 new String[] {"s", "i"}, 379 new String[] {"sdesc", "idesc"}, 380 new OpenType[] {ostring, oint}); 381 compositeData = 382 new CompositeDataSupport(ocomposite, 383 new String[] {"s", "i"}, 384 new Object[] {"foo", 23}); 385 compositeData2 = 386 new CompositeDataSupport(ocomposite, 387 new String[] {"s", "i"}, 388 new Object[] {"bar", -23}); 389 } catch (OpenDataException e) { // damn checked exceptions... 390 throw new IllegalArgumentException(e.toString(), e); 391 } 392 } 393 394 private static final Descriptor 395 nullD = null, 396 emptyD = ImmutableDescriptor.EMPTY_DESCRIPTOR; 397 398 /* The elements of this array are grouped as follows. Each 399 element contains four Object[]s. The first one is a set of 400 four values: default value, min value, max value, legal values 401 (an Object[]), some of which can be null. These will be used 402 to derive the OpenMBean*Info values to be tested. The second 403 is an array with one element that is the OpenType that will be 404 given to the constructors of the OpenMBean*Infos. The third 405 element is a set of values that should be valid according to 406 the constraints in the OpenMBean*Info. The fourth is a set of 407 values that should be invalid according to those 408 constraints. */ 409 private static final Object[][][] tests = { 410 411 // Test cases when there are no constraints 412 // Validity checking is limited to type of object 413 414 {{null, null, null, null}, 415 {oint}, 416 {-1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}, 417 {null, "noddy", 1.3, false, 3L, Long.MAX_VALUE, emptyD, 418 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 419 420 {{null, null, null, null}, 421 {obool}, 422 {true, false}, 423 {null, "noddy", 1.3, 3, 3L, Long.MAX_VALUE, emptyD, 424 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 425 426 {{null, null, null, null}, 427 {ostring}, 428 {"", "yes!"}, 429 {null, 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 430 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 431 432 {{null, null, null, null}, 433 {obyte}, 434 {Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0}, 435 {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 436 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 437 438 {{null, null, null, null}, 439 {ostringarray}, 440 {new String[0], new String[] {"hello", "world"}}, 441 {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 442 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 443 444 {{null, null, null, null}, 445 {ostringarray2}, 446 {new String[0][0], new String[][] {{"hello", "world"}, 447 {"goodbye", "cruel", "world"}}}, 448 {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 449 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 450 451 {{null, null, null, null}, 452 {ocomposite}, 453 {compositeData, compositeData2}, 454 {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 455 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 456 457 // Test cases where there is a default value, so null is allowed 458 459 {{23, null, null, null}, 460 {oint}, 461 {null, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}, 462 {"noddy", 1.3, false, 3L, Long.MAX_VALUE, emptyD, 463 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 464 465 {{true, null, null, null}, 466 {obool}, 467 {null, true, false}, 468 {"noddy", 1.3, 3, 3L, Long.MAX_VALUE, emptyD, 469 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 470 471 {{"foo", null, null, null}, 472 {ostring}, 473 {null, "", "yes!"}, 474 {1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 475 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 476 477 {{(byte) 23, null, null, null}, 478 {obyte}, 479 {null, Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0}, 480 {"noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 481 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 482 483 {{compositeData, null, null, null}, 484 {ocomposite}, 485 {null, compositeData, compositeData2}, 486 {"noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, 487 new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, 488 489 // Test cases where there is a min and/or max, with or without default 490 491 {{23, 0, 50, null}, 492 {oint}, 493 {null, 0, 25, 50}, 494 {"noddy", -1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}}, 495 496 {{null, 0, 50, null}, 497 {oint}, 498 {0, 25, 50}, 499 {null, "noddy", -1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}}, 500 501 {{null, 0, null, null}, 502 {oint}, 503 {0, 25, 50, Integer.MAX_VALUE}, 504 {null, "noddy", -1, Integer.MIN_VALUE, 25L}}, 505 506 {{null, null, 50, null}, 507 {oint}, 508 {Integer.MIN_VALUE, -1, 0, 25, 50}, 509 {null, "noddy", 51, Integer.MAX_VALUE, 25L}}, 510 511 {{"go", "a", "z~", null}, 512 {ostring}, 513 {null, "a", "z~", "zzzz", "z!"}, 514 {"A", "~", "", -1}}, 515 516 // Test cases where there is a set of legal values 517 518 {{23, null, null, new Integer[] {2, 3, 5, 7, 11, 13, 17, 23}}, 519 {oint}, 520 {null, 2, 11, 23}, 521 {"noddy", -1, 1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}}, 522 523 {{null, null, null, new CompositeData[] {compositeData}}, 524 {ocomposite}, 525 {compositeData}, 526 {null, compositeData2, "noddy"}}, 527 528 {{null, null, null, new Long[0]}, 529 {olong}, 530 {}, // constraint is impossible to satisfy! 531 {null, 23L, "x", 23}}, 532 }; 533 }