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