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