1 /* 2 * Copyright (c) 2012, 2013, 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 8011200 27 * @run testng BasicSerialization 28 * @summary Ensure Maps can be serialized and deserialized. 29 * @author Mike Duigou 30 */ 31 import java.io.ByteArrayOutputStream; 32 import java.io.InputStream; 33 import java.io.IOException; 34 import java.io.ObjectInputStream; 35 import java.io.ObjectOutputStream; 36 import java.io.ByteArrayInputStream; 37 import java.lang.reflect.Constructor; 38 import java.lang.reflect.Method; 39 import java.util.*; 40 import java.util.concurrent.ConcurrentHashMap; 41 import java.util.concurrent.ConcurrentSkipListMap; 42 43 import org.testng.annotations.Test; 44 import org.testng.annotations.DataProvider; 45 import static org.testng.Assert.fail; 46 import static org.testng.Assert.assertEquals; 47 import static org.testng.Assert.assertTrue; 48 import static org.testng.Assert.assertFalse; 49 import static org.testng.Assert.assertSame; 50 51 public class BasicSerialization { 52 53 enum IntegerEnum { 54 55 e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, 56 e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, 57 e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, 58 e30, e31, e32, e33, e34, e35, e36, e37, e38, e39, 59 e40, e41, e42, e43, e44, e45, e46, e47, e48, e49, 60 e50, e51, e52, e53, e54, e55, e56, e57, e58, e59, 61 e60, e61, e62, e63, e64, e65, e66, e67, e68, e69, 62 e70, e71, e72, e73, e74, e75, e76, e77, e78, e79, 63 e80, e81, e82, e83, e84, e85, e86, e87, e88, e89, 64 e90, e91, e92, e93, e94, e95, e96, e97, e98, e99, 65 EXTRA_KEY; 66 public static final int SIZE = values().length; 67 }; 68 private static final int TEST_SIZE = IntegerEnum.SIZE - 1; 69 /** 70 * Realized keys ensure that there is always a hard ref to all test objects. 71 */ 72 private static final IntegerEnum[] KEYS = new IntegerEnum[TEST_SIZE]; 73 /** 74 * Realized values ensure that there is always a hard ref to all test 75 * objects. 76 */ 77 private static final String[] VALUES = new String[TEST_SIZE]; 78 79 static { 80 IntegerEnum[] keys = IntegerEnum.values(); 81 for (int each = 0; each < TEST_SIZE; each++) { 82 KEYS[each] = keys[each]; 83 VALUES[each] = keys[each].name(); 84 } 85 } 86 private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY; 87 private static final String EXTRA_VALUE = IntegerEnum.EXTRA_KEY.name(); 88 mapClone(Map<K, V> map)89 public static <K, V> Map<K, V> mapClone(Map<K, V> map) { 90 Method cloneMethod; 91 92 try { 93 cloneMethod = map.getClass().getMethod("clone", new Class[]{}); 94 } catch (NoSuchMethodException | SecurityException all) { 95 cloneMethod = null; 96 } 97 98 if (null != cloneMethod) { 99 try { 100 Map<K, V> result = (Map<K, V>)cloneMethod.invoke(map, new Object[]{}); 101 return result; 102 } catch (Exception all) { 103 fail("clone() failed " + map.getClass().getSimpleName(), all); 104 return null; 105 } 106 } else { 107 Constructor<? extends Map> copyConstructor; 108 try { 109 copyConstructor = (Constructor<? extends Map>)map.getClass().getConstructor(new Class[]{Map.class}); 110 111 Map<K, V> result = (Map<K, V>)copyConstructor.newInstance(new Object[]{map}); 112 113 return result; 114 } catch (Exception all) { 115 return serialClone(map); 116 } 117 } 118 } 119 120 @Test(dataProvider = "Map<IntegerEnum,String>") testSerialization(String description, Map<IntegerEnum, String> map)121 public void testSerialization(String description, Map<IntegerEnum, String> map) { 122 Object foo = new Object(); 123 124 Map<IntegerEnum, String> clone = mapClone(map); 125 Map<IntegerEnum, String> serialClone = serialClone(map); 126 127 assertEquals(map, map, description + ":should equal self"); 128 assertEquals(clone, map, description + ":should equal clone"); 129 assertEquals(map, clone, description + ": should equal orginal map"); 130 assertEquals(serialClone, map, description + ": should equal deserialized clone"); 131 assertEquals(map, serialClone, description + ": should equal original map"); 132 assertEquals(serialClone, clone, description + ": deserialized clone should equal clone"); 133 assertEquals(clone, serialClone, description + ": clone should equal deserialized clone"); 134 135 assertFalse(map.containsKey(EXTRA_KEY), description + ":unexpected key"); 136 assertFalse(clone.containsKey(EXTRA_KEY), description + ":unexpected key"); 137 assertFalse(serialClone.containsKey(EXTRA_KEY), description + ":unexpected key"); 138 map.put(EXTRA_KEY, EXTRA_VALUE); 139 clone.put(EXTRA_KEY, EXTRA_VALUE); 140 serialClone.put(EXTRA_KEY, EXTRA_VALUE); 141 assertTrue(map.containsKey(EXTRA_KEY), description + ":missing key"); 142 assertTrue(clone.containsKey(EXTRA_KEY), description + ":missing key"); 143 assertTrue(serialClone.containsKey(EXTRA_KEY), description + ":missing key"); 144 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE, description + ":wrong value"); 145 assertSame(clone.get(EXTRA_KEY), EXTRA_VALUE, description + ":wrong value"); 146 assertSame(serialClone.get(EXTRA_KEY), EXTRA_VALUE, description + ":wrong value"); 147 148 assertEquals(map, map, description + ":should equal self"); 149 assertEquals(clone, map, description + ":should equal clone"); 150 assertEquals(map, clone, description + ": should equal orginal map"); 151 assertEquals(serialClone, map, description + ": should equal deserialized clone"); 152 assertEquals(map, serialClone, description + ": should equal original map"); 153 assertEquals(serialClone, clone, description + ": deserialized clone should equal clone"); 154 assertEquals(clone, serialClone, description + ": clone should equal deserialized clone"); 155 } 156 serializedForm(Object obj)157 static byte[] serializedForm(Object obj) { 158 try { 159 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 160 new ObjectOutputStream(baos).writeObject(obj); 161 return baos.toByteArray(); 162 } catch (IOException e) { 163 fail("Unexpected Exception", e); 164 return null; 165 } 166 } 167 readObject(byte[] bytes)168 static Object readObject(byte[] bytes) throws IOException, ClassNotFoundException { 169 InputStream is = new ByteArrayInputStream(bytes); 170 return new ObjectInputStream(is).readObject(); 171 } 172 173 @SuppressWarnings("unchecked") serialClone(T obj)174 static <T> T serialClone(T obj) { 175 try { 176 return (T)readObject(serializedForm(obj)); 177 } catch (IOException | ClassNotFoundException e) { 178 fail("Unexpected Exception", e); 179 return null; 180 } 181 } 182 183 @DataProvider(name = "Map<IntegerEnum,String>", parallel = true) makeMaps()184 private static Iterator<Object[]> makeMaps() { 185 return Arrays.asList( 186 // empty 187 new Object[]{"HashMap", new HashMap()}, 188 new Object[]{"LinkedHashMap", new LinkedHashMap()}, 189 new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(new HashMap(), IntegerEnum.class, String.class)}, 190 new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(new HashMap())}, 191 // null hostile 192 new Object[]{"EnumMap", new EnumMap(IntegerEnum.class)}, 193 new Object[]{"Hashtable", new Hashtable()}, 194 new Object[]{"TreeMap", new TreeMap()}, 195 new Object[]{"ConcurrentHashMap", new ConcurrentHashMap()}, 196 new Object[]{"ConcurrentSkipListMap", new ConcurrentSkipListMap()}, 197 new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(new ConcurrentHashMap(), IntegerEnum.class, String.class)}, 198 new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(new EnumMap(IntegerEnum.class))}, 199 // filled 200 new Object[]{"HashMap", fillMap(new HashMap())}, 201 new Object[]{"LinkedHashMap", fillMap(new LinkedHashMap())}, 202 new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(fillMap(new HashMap()), IntegerEnum.class, String.class)}, 203 new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(fillMap(new HashMap()))}, 204 // null hostile 205 new Object[]{"EnumMap", fillMap(new EnumMap(IntegerEnum.class))}, 206 new Object[]{"Hashtable", fillMap(new Hashtable())}, 207 new Object[]{"TreeMap", fillMap(new TreeMap())}, 208 new Object[]{"ConcurrentHashMap", fillMap(new ConcurrentHashMap())}, 209 new Object[]{"ConcurrentSkipListMap", fillMap(new ConcurrentSkipListMap())}, 210 new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(fillMap(new ConcurrentHashMap()), IntegerEnum.class, String.class)}, 211 new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(fillMap(new EnumMap(IntegerEnum.class)))}).iterator(); 212 } 213 fillMap(Map<IntegerEnum, String> result)214 private static Map<IntegerEnum, String> fillMap(Map<IntegerEnum, String> result) { 215 for (int each = 0; each < TEST_SIZE; each++) { 216 result.put(KEYS[each], VALUES[each]); 217 } 218 219 return result; 220 } 221 } 222