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 
  34 import java.lang.reflect.Field;
  35 import java.lang.reflect.InvocationTargetException;
  36 import java.lang.reflect.Method;
  37 import java.util.function.Consumer;
  38 import javax.management.MalformedObjectNameException;
  39 import javax.management.ObjectName;
  40 
  41 public 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 }