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