1 /* 2 * Copyright (c) 2019, 2020, 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 8246774 27 * @summary Basic test that serializes and deserializes a number of records 28 * @run testng BasicRecordSer 29 * @run testng/othervm/java.security.policy=empty_security.policy BasicRecordSer 30 */ 31 32 import java.io.ByteArrayInputStream; 33 import java.io.ByteArrayOutputStream; 34 import java.io.Externalizable; 35 import java.io.IOException; 36 import java.io.NotSerializableException; 37 import java.io.ObjectInput; 38 import java.io.ObjectInputStream; 39 import java.io.ObjectOutput; 40 import java.io.ObjectOutputStream; 41 import java.io.Serializable; 42 import java.math.BigInteger; 43 import org.testng.annotations.DataProvider; 44 import org.testng.annotations.Test; 45 import static java.lang.String.format; 46 import static java.lang.System.out; 47 import static java.net.InetAddress.getLoopbackAddress; 48 import static org.testng.Assert.assertEquals; 49 import static org.testng.Assert.assertTrue; 50 import static org.testng.Assert.expectThrows; 51 import static org.testng.Assert.fail; 52 53 /** 54 * Basic test that serializes and deserializes a number of simple records. 55 */ 56 public class BasicRecordSer { 57 58 // a mix of a few record and non-record classes 59 60 record Empty () implements Serializable { } 61 62 record Foo (int i) implements Serializable { } 63 64 static class Bar implements Serializable { 65 final Foo foo; 66 final long along; 67 Bar(Foo foo, long along) { this.foo = foo; this.along = along; } 68 @Override 69 public boolean equals(Object obj) { 70 if (!(obj instanceof Bar)) 71 return false; 72 Bar other = (Bar)obj; 73 if (this.foo.equals(other.foo) && this.along == other.along) 74 return true; 75 return false; 76 } 77 @Override 78 public String toString() { 79 return format("Bar[foo=%s, along=%d]", foo, along); 80 } 81 } 82 83 record Baz (Bar bar, float afloat, Foo foo) implements Serializable { } 84 85 record Bat (Empty e1, Foo foo1, Bar bar1, float afloat, Foo foo2, Empty e2, Bar bar2) 86 implements Serializable { } 87 88 record Cheese<A, B>(A a, B b) implements Serializable { } 89 90 interface ThrowingExternalizable extends Externalizable { 91 default void writeExternal(ObjectOutput out) { 92 fail("should not reach here"); 93 } 94 default void readExternal(ObjectInput in) { 95 fail("should not reach here"); 96 } 97 } 98 99 record Wibble () implements ThrowingExternalizable { } 100 101 record Wobble (Foo foo) implements ThrowingExternalizable { } 102 103 record Wubble (Wobble wobble, Wibble wibble, String s) implements ThrowingExternalizable { } 104 105 @DataProvider(name = "serializable") 106 public Object[][] serializable() { 107 Foo foo = new Foo(23); 108 return new Object[][] { 109 new Object[] { new Empty() }, 110 new Object[] { new Foo(22) }, 111 new Object[] { new Foo[] { new Foo(24), new Foo(25) } }, 112 new Object[] { new Foo[] { foo, foo, foo, foo, foo } }, 113 new Object[] { new Bar(new Foo(33), 1_234_567L) }, 114 new Object[] { new Baz(new Bar(new Foo(44), 4_444L), 5.5f, new Foo(55)) }, 115 new Object[] { new Bat(new Empty(), new Foo(57), new Bar(new Foo(44), 4_444L), 116 5.5f, new Foo(55), new Empty(), new Bar(new Foo(23), 1L)) }, 117 new Object[] { new Cheese(getLoopbackAddress(), BigInteger.valueOf(78)) }, 118 new Object[] { new Wibble() }, 119 new Object[] { new Wobble(new Foo(65)) }, 120 new Object[] { new Wubble(new Wobble(new Foo(6)), new Wibble(), "xxzzzyy") }, 121 }; 122 } 123 124 /** Tests serializing and deserializing a number of records. */ 125 @Test(dataProvider = "serializable") 126 public void testSerializable(Object objToSerialize) throws Exception { 127 out.println("\n---"); 128 out.println("serializing : " + objToSerialize); 129 var objDeserialized = serializeDeserialize(objToSerialize); 130 out.println("deserialized: " + objDeserialized); 131 assertEquals(objToSerialize, objDeserialized); 132 assertEquals(objDeserialized, objToSerialize); 133 } 134 135 /** Tests serializing and deserializing of local records. */ 136 @Test 137 public void testLocalRecord() throws Exception { 138 out.println("\n---"); 139 record Point(int x, int y) implements Serializable { } 140 record Rectangle(Point bottomLeft, Point topRight) implements Serializable { } 141 var objToSerialize = new Rectangle(new Point(0, 1), new Point (5, 6)); 142 out.println("serializing : " + objToSerialize); 143 var objDeserialized = serializeDeserialize(objToSerialize); 144 out.println("deserialized: " + objDeserialized); 145 assertEquals(objToSerialize, objDeserialized); 146 assertEquals(objDeserialized, objToSerialize); 147 } 148 149 /** Tests back references of Serializable record objects in the stream. */ 150 @Test 151 public void testSerializableBackRefs() throws Exception { 152 out.println("\n---"); 153 Foo foo = new Foo(32); 154 Foo[] objToSerialize = new Foo[] { foo, foo, foo, foo, foo }; 155 out.println("serializing : " + objToSerialize); 156 Foo[] objDeserialized = (Foo[])serializeDeserialize(objToSerialize); 157 out.println("deserialized: " + objDeserialized); 158 assertEquals(objToSerialize, objDeserialized); 159 assertEquals(objDeserialized, objToSerialize); 160 161 for (Foo f : objDeserialized) 162 assertTrue(objDeserialized[0] == f); 163 } 164 165 /** Tests back references of Externalizable record objects in the stream. */ 166 @Test 167 public void testExternalizableBackRefs() throws Exception { 168 out.println("\n---"); 169 Foo foo = new Foo(33); 170 Wobble wobble = new Wobble(foo); 171 Wobble[] objToSerialize = new Wobble[] { wobble, wobble, wobble, wobble }; 172 out.println("serializing : " + objToSerialize); 173 Wobble[] objDeserialized = (Wobble[])serializeDeserialize(objToSerialize); 174 out.println("deserialized: " + objDeserialized); 175 assertEquals(objToSerialize, objDeserialized); 176 assertEquals(objDeserialized, objToSerialize); 177 178 for (Wobble w : objDeserialized) { 179 assertTrue(objDeserialized[0] == w); 180 assertTrue(objDeserialized[0].foo() == w.foo()); 181 } 182 } 183 184 // --- Not Serializable 185 186 record NotSerEmpty () { } 187 record NotSer (int x) { } 188 record NotSerA (int x, int y) { 189 private static final long serialVersionUID = 5L; 190 } 191 static class A implements Serializable { 192 final int y = -1; 193 final NotSer notSer = new NotSer(7); 194 } 195 196 @DataProvider(name = "notSerializable") 197 public Object[][] notSerializable() { 198 return new Object[][] { 199 new Object[] { new NotSerEmpty() }, 200 new Object[] { new NotSerEmpty[] { new NotSerEmpty() } }, 201 new Object[] { new Object[] { new NotSerEmpty() } }, 202 new Object[] { new NotSer(6) }, 203 new Object[] { new NotSer[] { new NotSer(7) } }, 204 new Object[] { new NotSerA(6, 8) }, 205 new Object[] { new A() }, 206 new Object[] { new A[] { new A() } }, 207 }; 208 } 209 210 static final Class<NotSerializableException> NSE = NotSerializableException.class; 211 212 /** Tests that non-Serializable record objects throw NotSerializableException. */ 213 @Test(dataProvider = "notSerializable") 214 public void testNotSerializable(Object objToSerialize) throws Exception { 215 out.println("\n---"); 216 out.println("serializing : " + objToSerialize); 217 NotSerializableException expected = expectThrows(NSE, () -> serialize(objToSerialize)); 218 out.println("caught expected NSE:" + expected); 219 220 } 221 222 // --- constructor invocation counting 223 224 static volatile int e_ctrInvocationCount; 225 226 record E () implements Serializable { 227 public E() { e_ctrInvocationCount++; } 228 } 229 230 /** Tests that the record's constructor is invoke exactly once per deserialization. */ 231 @Test 232 public void testCtrCalledOnlyOnce() throws Exception { 233 out.println("\n---"); 234 var objToSerialize = new E(); 235 e_ctrInvocationCount = 0; // reset 236 out.println("serializing : " + objToSerialize); 237 var objDeserialized = serializeDeserialize(objToSerialize); 238 out.println("deserialized: " + objDeserialized); 239 assertEquals(objToSerialize, objDeserialized); 240 assertEquals(objDeserialized, objToSerialize); 241 assertEquals(e_ctrInvocationCount, 1); 242 } 243 244 // --- 245 246 static volatile int g_ctrInvocationCount; 247 248 record F (int x){ 249 public F(int x) { this.x = x; g_ctrInvocationCount++; } 250 } 251 static class G implements Serializable { 252 F f = new F(89); 253 } 254 255 /** Tests that the record's constructor is NOT invoke during failed deserialization. */ 256 @Test 257 public void testCtrNotCalled() { 258 out.println("\n---"); 259 var objToSerialize = new G(); 260 g_ctrInvocationCount = 0; // reset 261 out.println("serializing : " + objToSerialize); 262 NotSerializableException expected = expectThrows(NSE, () -> serialize(objToSerialize)); 263 out.println("caught expected NSE:" + expected); 264 assertEquals(g_ctrInvocationCount, 0); 265 } 266 267 // --- infra 268 269 static <T> byte[] serialize(T obj) throws IOException { 270 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 271 ObjectOutputStream oos = new ObjectOutputStream(baos); 272 oos.writeObject(obj); 273 oos.close(); 274 return baos.toByteArray(); 275 } 276 277 @SuppressWarnings("unchecked") 278 static <T> T deserialize(byte[] streamBytes) 279 throws IOException, ClassNotFoundException 280 { 281 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 282 ObjectInputStream ois = new ObjectInputStream(bais); 283 return (T) ois.readObject(); 284 } 285 286 static <T> T serializeDeserialize(T obj) 287 throws IOException, ClassNotFoundException 288 { 289 return deserialize(serialize(obj)); 290 } 291 }