| /* |
| * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @bug 6204469 |
| * @summary Test that Open MBean attributes and parameters check constraints |
| * @author Eamonn McManus |
| * @modules java.management |
| * @run clean ConstraintTest |
| * @run build ConstraintTest |
| * @run main ConstraintTest |
| */ |
| |
| import java.util.*; |
| import javax.management.*; |
| import javax.management.openmbean.*; |
| |
| public class ConstraintTest { |
| private static String failure; |
| |
| public static void main(String[] args) throws Exception { |
| for (Object[][] test : tests) { |
| if (test.length != 4) { |
| throw new Exception("Test element has wrong length: " + |
| Arrays.deepToString(test)); |
| } |
| |
| if (test[0].length != 4) { |
| throw new Exception("Test constraints should have size 4: " + |
| Arrays.deepToString(test[0])); |
| } |
| Object defaultValue = test[0][0]; |
| Comparable<?> minValue = (Comparable<?>) test[0][1]; |
| Comparable<?> maxValue = (Comparable<?>) test[0][2]; |
| Object[] legalValues = (Object[]) test[0][3]; |
| System.out.println("test: defaultValue=" + defaultValue + |
| "; minValue=" + minValue + |
| "; maxValue=" + maxValue + |
| "; legalValues=" + |
| Arrays.deepToString(legalValues)); |
| |
| if (test[1].length != 1) { |
| throw new Exception("OpenType list should have size 1: " + |
| Arrays.deepToString(test[1])); |
| } |
| OpenType<?> openType = (OpenType<?>) test[1][0]; |
| |
| Object[] valid = test[2]; |
| Object[] invalid = test[3]; |
| |
| System.out.println("...valid=" + Arrays.deepToString(valid)); |
| System.out.println("...invalid=" + Arrays.deepToString(invalid)); |
| |
| test(openType, defaultValue, minValue, maxValue, legalValues, |
| valid, invalid); |
| } |
| |
| if (failure == null) |
| System.out.println("Test passed"); |
| else |
| throw new Exception("TEST FAILED: " + failure); |
| } |
| |
| private static <T> void test(OpenType<T> openType, Object defaultValue, |
| Comparable<?> minValue, |
| Comparable<?> maxValue, Object[] legalValues, |
| Object[] valid, Object[] invalid) |
| throws Exception { |
| /* This hack is needed to avoid grief from the parameter checking |
| in the OpenMBean*InfoSupport constructors. Since they are defined |
| to check that the defaultValue etc are of the same type as the |
| OpenType<T>, there is no way to pass a defaultValue etc when |
| the type is OpenType<?>. So either you have to write plain |
| OpenType, and get unchecked warnings for every constructor |
| invocation, or you do this, and get the unchecked warnings just |
| here. */ |
| test1(openType, (T) defaultValue, (Comparable<T>) minValue, |
| (Comparable<T>) maxValue, (T[]) legalValues, valid, invalid); |
| } |
| |
| private static <T> void test1(OpenType<T> openType, T defaultValue, |
| Comparable<T> minValue, |
| Comparable<T> maxValue, T[] legalValues, |
| Object[] valid, Object[] invalid) |
| throws Exception { |
| |
| if (legalValues != null && (minValue != null || maxValue != null)) |
| throw new Exception("Test case has both legals and min/max"); |
| |
| if (defaultValue == null && minValue == null && maxValue == null && |
| legalValues == null) { |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false), |
| valid, invalid); |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false, nullD), |
| valid, invalid); |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false, emptyD), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType, |
| nullD), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType, |
| emptyD), |
| valid, invalid); |
| } |
| |
| if (minValue == null && maxValue == null && legalValues == null) { |
| Descriptor d = descriptor("defaultValue", defaultValue); |
| test(new OpenMBeanAttributeInfoSupport("blah", "descr", openType, |
| true, true, false, d), |
| valid, invalid); |
| test(new OpenMBeanAttributeInfoSupport("blah", "descr", |
| openType, true, true, |
| false, defaultValue), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("blah", "descr", openType, |
| d), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("blah", "descr", openType, |
| defaultValue), |
| valid, invalid); |
| } |
| |
| if (legalValues == null) { |
| Descriptor d = descriptor("defaultValue", defaultValue, |
| "minValue", minValue, |
| "maxValue", maxValue); |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false, d), |
| valid, invalid); |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false, |
| defaultValue, |
| minValue, maxValue), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType, |
| d), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType, |
| defaultValue, |
| minValue, maxValue), |
| valid, invalid); |
| } |
| |
| if (minValue == null && maxValue == null) { |
| // Legal values in descriptor can be either an array or a set |
| Descriptor d1 = descriptor("defaultValue", defaultValue, |
| "legalValues", legalValues); |
| Descriptor d2; |
| if (legalValues == null) |
| d2 = d1; |
| else { |
| d2 = descriptor("defaultValue", defaultValue, |
| "legalValues", arraySet(legalValues)); |
| } |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false, d1), |
| valid, invalid); |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false, d2), |
| valid, invalid); |
| test(new OpenMBeanAttributeInfoSupport("name", "descr", openType, |
| true, true, false, |
| defaultValue, legalValues), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType, |
| d1), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType, |
| d2), |
| valid, invalid); |
| test(new OpenMBeanParameterInfoSupport("name", "descr", openType, |
| defaultValue, legalValues), |
| valid, invalid); |
| } |
| } |
| |
| /* Test one of the objects. Note that OpenMBeanAttributeInfo |
| extends OpenMBeanParameterInfo, so OpenMBeanAttributeInfoSupport |
| is-an OpenMBeanParameterInfo. */ |
| private static void test(OpenMBeanParameterInfo info, |
| Object[] valid, Object[] invalid) { |
| test1(info, valid, invalid); |
| |
| // Check that the constraints can be specified as strings |
| // rather than objects |
| if (info.getOpenType() instanceof SimpleType<?>) { |
| Descriptor d = ((DescriptorRead) info).getDescriptor(); |
| String[] names = d.getFieldNames(); |
| Object[] values = d.getFieldValues(names); |
| for (int i = 0; i < values.length; i++) { |
| if (values[i] == null) |
| continue; |
| if (names[i].equals("legalValues")) { |
| Collection<?> legals; |
| if (values[i] instanceof Collection<?>) |
| legals = (Collection<?>) values[i]; |
| else |
| legals = Arrays.asList((Object[]) values[i]); |
| List<String> strings = new ArrayList<String>(); |
| for (Object legal : legals) |
| strings.add(legal.toString()); |
| values[i] = strings.toArray(new String[0]); |
| } else if (!(values[i] instanceof OpenType<?>)) |
| values[i] = values[i].toString(); |
| } |
| d = new ImmutableDescriptor(names, values); |
| OpenType<?> ot = info.getOpenType(); |
| if (info instanceof OpenMBeanAttributeInfo) { |
| OpenMBeanAttributeInfo ai = (OpenMBeanAttributeInfo) info; |
| info = new OpenMBeanAttributeInfoSupport(info.getName(), |
| info.getDescription(), |
| info.getOpenType(), |
| ai.isReadable(), |
| ai.isWritable(), |
| ai.isIs(), |
| d); |
| } else { |
| info = new OpenMBeanParameterInfoSupport(info.getName(), |
| info.getDescription(), |
| info.getOpenType(), |
| d); |
| } |
| test1(info, valid, invalid); |
| } |
| } |
| |
| private static void test1(OpenMBeanParameterInfo info, |
| Object[] valid, Object[] invalid) { |
| |
| for (Object x : valid) { |
| if (!info.isValue(x)) { |
| fail("Object should be valid but is not: " + x + " for: " + |
| info); |
| } |
| } |
| |
| for (Object x : invalid) { |
| if (info.isValue(x)) { |
| fail("Object should not be valid but is: " + x + " for: " + |
| info); |
| } |
| } |
| |
| /* If you specify e.g. minValue in a descriptor, then we arrange |
| for getMinValue() to return the same value, and if you specify |
| a minValue as a constructor parameter then we arrange for the |
| descriptor to have a minValue entry. Check that these values |
| do in fact match. */ |
| |
| Descriptor d = ((DescriptorRead) info).getDescriptor(); |
| |
| checkSameValue("defaultValue", info.getDefaultValue(), |
| d.getFieldValue("defaultValue")); |
| checkSameValue("minValue", info.getMinValue(), |
| d.getFieldValue("minValue")); |
| checkSameValue("maxValue", info.getMaxValue(), |
| d.getFieldValue("maxValue")); |
| checkSameValues("legalValues", info.getLegalValues(), |
| d.getFieldValue("legalValues")); |
| } |
| |
| private static void checkSameValue(String what, Object getterValue, |
| Object descriptorValue) { |
| if (getterValue == null) { |
| if (descriptorValue != null) { |
| fail("Getter returned null but descriptor has entry for " + |
| what + ": " + descriptorValue); |
| } |
| } else if (descriptorValue == null) { |
| fail("Getter returned value but descriptor has no entry for " + |
| what + ": " + getterValue); |
| } else if (!getterValue.equals(descriptorValue) && |
| !getterValue.toString().equals(descriptorValue)) { |
| fail("For " + what + " getter returned " + getterValue + |
| " but descriptor entry is " + descriptorValue); |
| } |
| } |
| |
| private static void checkSameValues(String what, Set<?> getterValues, |
| Object descriptorValues) { |
| if (getterValues == null) { |
| if (descriptorValues != null) { |
| fail("Getter returned null but descriptor has entry for " + |
| what + ": " + descriptorValues); |
| } |
| } else if (descriptorValues == null) { |
| fail("Getter returned value but descriptor has no entry for " + |
| what + ": " + getterValues); |
| } else { |
| Set<?> descriptorValueSet; |
| if (descriptorValues instanceof Set<?>) |
| descriptorValueSet = (Set<?>) descriptorValues; |
| else |
| descriptorValueSet = arraySet((Object[]) descriptorValues); |
| boolean same = true; |
| if (getterValues.size() != descriptorValueSet.size()) |
| same = false; |
| else { |
| for (Object x : getterValues) { |
| if (!descriptorValueSet.contains(x) |
| && !descriptorValueSet.contains(x.toString())) { |
| same = false; |
| break; |
| } |
| } |
| } |
| if (!same) { |
| fail("For " + what + " getter returned " + getterValues + |
| " but descriptor entry is " + descriptorValueSet); |
| } |
| } |
| } |
| |
| private static void fail(String why) { |
| System.out.println("FAILED: " + why); |
| failure = why; |
| } |
| |
| private static Descriptor descriptor(Object... entries) { |
| if (entries.length % 2 != 0) |
| throw new RuntimeException("Odd length descriptor entries"); |
| String[] names = new String[entries.length / 2]; |
| Object[] values = new Object[entries.length / 2]; |
| for (int i = 0; i < entries.length; i += 2) { |
| names[i / 2] = (String) entries[i]; |
| values[i / 2] = entries[i + 1]; |
| } |
| return new ImmutableDescriptor(names, values); |
| } |
| |
| private static <T> Set<T> arraySet(T[] array) { |
| return new HashSet<T>(Arrays.asList(array)); |
| } |
| |
| private static final OpenType<?> |
| ostring = SimpleType.STRING, |
| oint = SimpleType.INTEGER, |
| obool = SimpleType.BOOLEAN, |
| olong = SimpleType.LONG, |
| obyte = SimpleType.BYTE, |
| ofloat = SimpleType.FLOAT, |
| odouble = SimpleType.DOUBLE, |
| ostringarray, ostringarray2; |
| private static final CompositeType ocomposite; |
| private static final CompositeData compositeData, compositeData2; |
| static { |
| try { |
| ostringarray = new ArrayType<String[]>(1, ostring); |
| ostringarray2 = new ArrayType<String[][]>(2, ostring); |
| ocomposite = |
| new CompositeType("name", "descr", |
| new String[] {"s", "i"}, |
| new String[] {"sdesc", "idesc"}, |
| new OpenType[] {ostring, oint}); |
| compositeData = |
| new CompositeDataSupport(ocomposite, |
| new String[] {"s", "i"}, |
| new Object[] {"foo", 23}); |
| compositeData2 = |
| new CompositeDataSupport(ocomposite, |
| new String[] {"s", "i"}, |
| new Object[] {"bar", -23}); |
| } catch (OpenDataException e) { // damn checked exceptions... |
| throw new IllegalArgumentException(e.toString(), e); |
| } |
| } |
| |
| private static final Descriptor |
| nullD = null, |
| emptyD = ImmutableDescriptor.EMPTY_DESCRIPTOR; |
| |
| /* The elements of this array are grouped as follows. Each |
| element contains four Object[]s. The first one is a set of |
| four values: default value, min value, max value, legal values |
| (an Object[]), some of which can be null. These will be used |
| to derive the OpenMBean*Info values to be tested. The second |
| is an array with one element that is the OpenType that will be |
| given to the constructors of the OpenMBean*Infos. The third |
| element is a set of values that should be valid according to |
| the constraints in the OpenMBean*Info. The fourth is a set of |
| values that should be invalid according to those |
| constraints. */ |
| private static final Object[][][] tests = { |
| |
| // Test cases when there are no constraints |
| // Validity checking is limited to type of object |
| |
| {{null, null, null, null}, |
| {oint}, |
| {-1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}, |
| {null, "noddy", 1.3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{null, null, null, null}, |
| {obool}, |
| {true, false}, |
| {null, "noddy", 1.3, 3, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{null, null, null, null}, |
| {ostring}, |
| {"", "yes!"}, |
| {null, 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{null, null, null, null}, |
| {obyte}, |
| {Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0}, |
| {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{null, null, null, null}, |
| {ostringarray}, |
| {new String[0], new String[] {"hello", "world"}}, |
| {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{null, null, null, null}, |
| {ostringarray2}, |
| {new String[0][0], new String[][] {{"hello", "world"}, |
| {"goodbye", "cruel", "world"}}}, |
| {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{null, null, null, null}, |
| {ocomposite}, |
| {compositeData, compositeData2}, |
| {null, "noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| // Test cases where there is a default value, so null is allowed |
| |
| {{23, null, null, null}, |
| {oint}, |
| {null, -1, 0, 1, Integer.MAX_VALUE, Integer.MIN_VALUE}, |
| {"noddy", 1.3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{true, null, null, null}, |
| {obool}, |
| {null, true, false}, |
| {"noddy", 1.3, 3, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{"foo", null, null, null}, |
| {ostring}, |
| {null, "", "yes!"}, |
| {1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{(byte) 23, null, null, null}, |
| {obyte}, |
| {null, Byte.MIN_VALUE, Byte.MAX_VALUE, (byte) 0}, |
| {"noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| {{compositeData, null, null, null}, |
| {ocomposite}, |
| {null, compositeData, compositeData2}, |
| {"noddy", 1.3, 3, false, 3L, Long.MAX_VALUE, emptyD, |
| new int[2], new Integer[2], new Integer[] {3}, new Integer[0]}}, |
| |
| // Test cases where there is a min and/or max, with or without default |
| |
| {{23, 0, 50, null}, |
| {oint}, |
| {null, 0, 25, 50}, |
| {"noddy", -1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}}, |
| |
| {{null, 0, 50, null}, |
| {oint}, |
| {0, 25, 50}, |
| {null, "noddy", -1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}}, |
| |
| {{null, 0, null, null}, |
| {oint}, |
| {0, 25, 50, Integer.MAX_VALUE}, |
| {null, "noddy", -1, Integer.MIN_VALUE, 25L}}, |
| |
| {{null, null, 50, null}, |
| {oint}, |
| {Integer.MIN_VALUE, -1, 0, 25, 50}, |
| {null, "noddy", 51, Integer.MAX_VALUE, 25L}}, |
| |
| {{"go", "a", "z~", null}, |
| {ostring}, |
| {null, "a", "z~", "zzzz", "z!"}, |
| {"A", "~", "", -1}}, |
| |
| // Test cases where there is a set of legal values |
| |
| {{23, null, null, new Integer[] {2, 3, 5, 7, 11, 13, 17, 23}}, |
| {oint}, |
| {null, 2, 11, 23}, |
| {"noddy", -1, 1, 51, Integer.MIN_VALUE, Integer.MAX_VALUE, 25L}}, |
| |
| {{null, null, null, new CompositeData[] {compositeData}}, |
| {ocomposite}, |
| {compositeData}, |
| {null, compositeData2, "noddy"}}, |
| |
| {{null, null, null, new Long[0]}, |
| {olong}, |
| {}, // constraint is impossible to satisfy! |
| {null, 23L, "x", 23}}, |
| }; |
| } |