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