1/*
2 * Copyright (c) 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 8041565
27 * @summary Tests the limits imposed on the domain name part of an
28 *          ObjectName instance
29 * @author Jaroslav Bachorik
30 * @modules java.management/javax.management:open
31 * @run main CompressedStorageTest
32 */
33
34import java.lang.reflect.Field;
35import java.lang.reflect.InvocationTargetException;
36import java.lang.reflect.Method;
37import java.util.function.Consumer;
38import javax.management.MalformedObjectNameException;
39import javax.management.ObjectName;
40
41public class CompressedStorageTest {
42    private static Method setDomainLengthM;
43    private static Field compressedStorageFld;
44
45    private static int DOMAIN_PATTERN;
46    private static int PROPLIST_PATTERN;
47    private static int PROPVAL_PATTERN;
48
49    private static Method setDomainPattern;
50    private static Method setPropertyListPattern;
51    private static Method setPropertyValuePattern;
52
53
54    static {
55        try {
56            Class<?> clz = ObjectName.class;
57            setDomainLengthM = clz.getDeclaredMethod("setDomainLength", int.class);
58            setDomainLengthM.setAccessible(true);
59
60            compressedStorageFld = clz.getDeclaredField("_compressed_storage");
61            compressedStorageFld.setAccessible(true);
62
63            setDomainPattern = clz.getDeclaredMethod("setDomainPattern", boolean.class);
64            setDomainPattern.setAccessible(true);
65            setPropertyListPattern = clz.getDeclaredMethod("setPropertyListPattern", boolean.class);
66            setPropertyListPattern.setAccessible(true);
67            setPropertyValuePattern = clz.getDeclaredMethod("setPropertyValuePattern", boolean.class);
68            setPropertyValuePattern.setAccessible(true);
69
70            DOMAIN_PATTERN = getStaticIntFld("DOMAIN_PATTERN");
71            PROPLIST_PATTERN = getStaticIntFld("PROPLIST_PATTERN");
72            PROPVAL_PATTERN = getStaticIntFld("PROPVAL_PATTERN");
73
74        } catch (Exception e) {
75            throw new Error(e);
76        }
77    }
78
79    public static void main(String[] args) throws Exception {
80        testZeroLength();
81        testNegativeLength();
82        testMaxLength();
83
84        testSetDomainPattern();
85        testSetPropertyListPattern();
86        testSetPropertyValuePattern();
87    }
88
89    private static ObjectName getObjectName()
90    throws MalformedObjectNameException {
91        return new ObjectName("domain", "key", "value");
92    }
93
94    /**
95     * Test for accepting 0 being passed as argument to
96     * {@linkplain ObjectName#setDomainLength(int)}.
97     *
98     */
99    private static void testZeroLength() throws Exception {
100        setDomainNameLength(0);
101    }
102
103    /**
104     * Test for rejecting negative value being passed as argument to
105     * {@linkplain ObjectName#setDomainLength(int)}.
106     */
107    private static void testNegativeLength() throws Exception {
108        try {
109            setDomainNameLength(-1);
110        } catch (MalformedObjectNameException e) {
111            return;
112        }
113        fail("Allowing negative domain name length");
114    }
115
116    /**
117     * Test for rejecting value exceeding the maximum allowed length
118     * being passed as argument to {@linkplain ObjectName#setDomainLength(int)}.
119     */
120    private static void testMaxLength() throws Exception {
121        try {
122            setDomainNameLength(Integer.MAX_VALUE / 4 + 1);
123        } catch (MalformedObjectNameException e) {
124            return;
125        }
126        fail("Maximum domain name length is not respected");
127    }
128
129    /**
130     * Tests that calling {@linkplain ObjectName#setDomainPattern(boolean)}
131     * results in setting correct bits in {@linkplain ObjectName#_compressed_storage}.
132     */
133    private static void testSetDomainPattern() throws Exception {
134        ObjectName on = getObjectName();
135
136        checkMask(DOMAIN_PATTERN, setDomainPattern, on);
137    }
138
139    /**
140     * Tests that calling {@linkplain ObjectName#setPropertyListPattern(boolean)}
141     * results in setting correct bits in {@linkplain ObjectName#_compressed_storage}.
142     */
143    private static void testSetPropertyListPattern() throws Exception {
144        ObjectName on = getObjectName();
145
146        checkMask(PROPLIST_PATTERN, setPropertyListPattern, on);
147    }
148
149    /**
150     * Tests that calling {@linkplain ObjectName#setPropertyValuePattern(boolean)}
151     * results in setting correct bits in {@linkplain ObjectName#_compressed_storage}.
152     */
153    private static void testSetPropertyValuePattern() throws Exception {
154        ObjectName on = getObjectName();
155
156        checkMask(PROPVAL_PATTERN, setPropertyValuePattern, on);
157    }
158
159    /**
160     * Helper method to call {@linkplain ObjectName#setDomainLength(int)}
161     * method via reflection.
162     * @param len The domain name length
163     * @throws MalformedObjectNameException Propagated from
164     *           {@linkplain ObjectName#setDomainLength(int)} invocation.
165     */
166    private static void setDomainNameLength(int len)
167    throws MalformedObjectNameException {
168        try {
169            setDomainLengthM.invoke(getObjectName(), len);
170        } catch (InvocationTargetException e) {
171            Throwable cause = e.getCause();
172            if (cause instanceof MalformedObjectNameException) {
173                throw (MalformedObjectNameException)cause;
174            }
175            throw new Error(cause);
176        } catch (IllegalAccessException | IllegalArgumentException e) {
177            throw new Error(e);
178        }
179    }
180
181    /**
182     * Helper method to assert that a particular boolean setter affects only
183     * a particular bit in the {@linkplain ObjectName#_compressed_storage} field.
184     * @param mask bitmask for storing the boolean value
185     * @param setter setter method reference
186     * @param on {@linkplain ObjectName} instance
187     */
188    private static void checkMask(int mask, Method setter, ObjectName on)
189    throws Exception {
190        int valBefore = compressedStorageFld.getInt(on);
191        setter.invoke(on, true);
192        int valAfter = compressedStorageFld.getInt(on);
193
194        checkMask(mask, valAfter ^ valBefore);
195
196        valBefore = valAfter;
197        setter.invoke(on, false);
198        valAfter = compressedStorageFld.getInt(on);
199
200        checkMask(mask, valAfter ^ valBefore);
201    }
202
203    /**
204     * Compare the changed bits with the given mask.
205     * @param mask bitmask
206     * @param val the changed bits; may be 0 if there was no change
207     */
208    private static void checkMask(int mask, int val) {
209        if (val != 0 && val != mask) {
210            fail("Invalid mask: expecting '" +
211                    Integer.toBinaryString(mask) + "' , received '" +
212                    Integer.toBinaryString(val) + "'");
213        }
214    }
215
216    /**
217     * Helper method to obtain the value of a static field via reflection.
218     * @param name static field name
219     * @return static field value
220     */
221    private static int getStaticIntFld(String name) throws Exception {
222        Field fld = ObjectName.class.getDeclaredField(name);
223        fld.setAccessible(true);
224
225        return fld.getInt(null);
226    }
227
228    private static void fail(String msg) {
229        throw new Error(msg);
230    }
231}
232