3789983e89
Reviewed-by: darcy, ihse
534 lines
23 KiB
Java
534 lines
23 KiB
Java
/*
|
|
* 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
|
|
*
|
|
* @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}},
|
|
};
|
|
}
|