blob: b74bada9ed3098eaf778e804cde50e125139a13d [file] [log] [blame]
/*
* 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}},
};
}