1 /*
   2  * Copyright (c) 2008, 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 6334663
  27  * @summary Test that TabularDataSupport preserves the order elements were added
  28  * @author Eamonn McManus
  29  */
  30 
  31 import java.io.ByteArrayInputStream;
  32 import java.io.ByteArrayOutputStream;
  33 import java.io.ObjectInputStream;
  34 import java.io.ObjectOutputStream;
  35 import java.lang.reflect.Field;
  36 import java.lang.reflect.Modifier;
  37 import java.util.ArrayList;
  38 import java.util.HashMap;
  39 import java.util.LinkedHashMap;
  40 import java.util.List;
  41 import java.util.Map;
  42 import javax.management.JMX;
  43 import javax.management.MBeanServer;
  44 import javax.management.MBeanServerFactory;
  45 import javax.management.ObjectName;
  46 import javax.management.openmbean.CompositeData;
  47 import javax.management.openmbean.CompositeDataSupport;
  48 import javax.management.openmbean.CompositeType;
  49 import javax.management.openmbean.OpenDataException;
  50 import javax.management.openmbean.OpenType;
  51 import javax.management.openmbean.SimpleType;
  52 import javax.management.openmbean.TabularData;
  53 import javax.management.openmbean.TabularDataSupport;
  54 import javax.management.openmbean.TabularType;
  55 
  56 public class TabularDataOrderTest {
  57     private static String failure;
  58 
  59     private static final String COMPAT_PROP_NAME = "jmx.tabular.data.hash.map";
  60 
  61     private static final String[] intNames = {
  62         "unus", "duo", "tres", "quatuor", "quinque", "sex", "septem",
  63         "octo", "novem", "decim",
  64     };
  65     private static final Map<String, Integer> stringToValue =
  66             new LinkedHashMap<String, Integer>();
  67     static {
  68         for (int i = 0; i < intNames.length; i++)
  69             stringToValue.put(intNames[i], i + 1);
  70     }
  71 
  72     public static interface TestMXBean {
  73         public Map<String, Integer> getMap();
  74     }
  75 
  76     public static class TestImpl implements TestMXBean {
  77         public Map<String, Integer> getMap() {
  78             return stringToValue;
  79         }
  80     }
  81 
  82     private static final CompositeType ct;
  83     private static final TabularType tt;
  84     static {
  85         try {
  86             ct = new CompositeType(
  87                     "a.b.c", "name and int",
  88                     new String[] {"name", "int"},
  89                     new String[] {"name of integer", "value of integer"},
  90                     new OpenType<?>[] {SimpleType.STRING, SimpleType.INTEGER});
  91             tt = new TabularType(
  92                     "d.e.f", "name and int indexed by name", ct,
  93                     new String[] {"name"});
  94         } catch (OpenDataException e) {
  95             throw new AssertionError(e);
  96         }
  97     }
  98 
  99     private static TabularData makeTable() throws OpenDataException {
 100         TabularData td = new TabularDataSupport(tt);
 101         for (Map.Entry<String, Integer> entry : stringToValue.entrySet()) {
 102             CompositeData cd = new CompositeDataSupport(
 103                     ct,
 104                     new String[] {"name", "int"},
 105                     new Object[] {entry.getKey(), entry.getValue()});
 106             td.put(cd);
 107         }
 108         return td;
 109     }
 110 
 111     public static void main(String[] args) throws Exception {
 112         System.out.println("Testing standard behaviour");
 113         TabularData td = makeTable();
 114         System.out.println(td);
 115 
 116         // Test that a default TabularData has the order keys were added in
 117         int last = 0;
 118         boolean ordered = true;
 119         for (Object x : td.values()) {
 120             CompositeData cd = (CompositeData) x;
 121             String name = (String) cd.get("name");
 122             int value = (Integer) cd.get("int");
 123             System.out.println(name + " = " + value);
 124             if (last + 1 != value)
 125                 ordered = false;
 126             last = value;
 127         }
 128         if (!ordered)
 129             fail("Order not preserved");
 130 
 131         // Now test the undocumented property that causes HashMap to be used
 132         // instead of LinkedHashMap, in case serializing to a 1.3 client.
 133         // We serialize and deserialize in case the implementation handles
 134         // this at serialization time.  Then we look at object fields; that's
 135         // not guaranteed to work but at worst it will fail spuriously and
 136         // we'll have to update the test.
 137         System.out.println("Testing compatible behaviour");
 138         System.setProperty(COMPAT_PROP_NAME, "true");
 139         td = makeTable();
 140         System.out.println(td);
 141         ByteArrayOutputStream bout = new ByteArrayOutputStream();
 142         ObjectOutputStream oout = new ObjectOutputStream(bout);
 143         oout.writeObject(td);
 144         oout.close();
 145         byte[] bytes = bout.toByteArray();
 146         ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
 147         ObjectInputStream oin = new ObjectInputStream(bin);
 148         td = (TabularData) oin.readObject();
 149         boolean found = false;
 150         for (Field f : td.getClass().getDeclaredFields()) {
 151             if (Modifier.isStatic(f.getModifiers()))
 152                 continue;
 153             f.setAccessible(true);
 154             Object x = f.get(td);
 155             if (x != null && x.getClass() == HashMap.class) {
 156                 found = true;
 157                 System.out.println(
 158                         x.getClass().getName() + " TabularDataSupport." +
 159                         f.getName() + " = " + x);
 160                 break;
 161             }
 162         }
 163         if (!found) {
 164             fail("TabularDataSupport does not contain HashMap though " +
 165                     COMPAT_PROP_NAME + "=true");
 166         }
 167         System.clearProperty(COMPAT_PROP_NAME);
 168 
 169         System.out.println("Testing MXBean behaviour");
 170         MBeanServer mbs = MBeanServerFactory.newMBeanServer();
 171         ObjectName name = new ObjectName("a:b=c");
 172         mbs.registerMBean(new TestImpl(), name);
 173         TestMXBean proxy = JMX.newMXBeanProxy(mbs, name, TestMXBean.class);
 174         Map<String, Integer> map = proxy.getMap();
 175         List<String> origNames = new ArrayList<String>(stringToValue.keySet());
 176         List<String> proxyNames = new ArrayList<String>(map.keySet());
 177         if (!origNames.equals(proxyNames))
 178             fail("Order mangled after passage through MXBean: " + proxyNames);
 179 
 180         if (failure == null)
 181             System.out.println("TEST PASSED");
 182         else
 183             throw new Exception("TEST FAILED: " + failure);
 184     }
 185 
 186     private static void fail(String why) {
 187         System.out.println("FAILED: " + why);
 188         failure = why;
 189     }
 190 }