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 Ensures basic behavior of cycles from record components 28 * @run testng CycleTest 29 * @run testng/othervm/java.security.policy=empty_security.policy CycleTest 30 */ 31 package test.java.io.Serializable.records; 32 33 import java.io.ByteArrayInputStream; 34 import java.io.ByteArrayOutputStream; 35 import java.io.IOException; 36 import java.io.ObjectInputStream; 37 import java.io.ObjectOutputStream; 38 import java.io.Serializable; 39 import org.testng.annotations.Test; 40 import static java.lang.System.out; 41 import static org.testng.Assert.assertEquals; 42 import static org.testng.Assert.assertTrue; 43 44 public class CycleTest { 45 46 record R (int x, int y, C c) implements Serializable { } 47 48 static class C implements Serializable { 49 Object obj; // mutable 50 } 51 52 /** 53 * Deserialization of a record object, r, does not support references to r, 54 * from any of r's components. All references will be null. 55 */ 56 @Test testCycle1()57 public void testCycle1() throws Exception { 58 out.println("\n---"); 59 C c = new C(); 60 R r = new R(1, 2, c); 61 c.obj = r; // cycle, targeting record r 62 63 out.println("serializing : " + r); 64 R deserializedObj = serializeDeserialize(r); 65 out.println("deserialized: " + deserializedObj); 66 assertEquals(deserializedObj.x(), 1); // sanity 67 assertEquals(deserializedObj.y(), 2); // sanity 68 assertTrue(deserializedObj.c() instanceof C); // sanity 69 assertEquals(deserializedObj.c().obj, null); // cycle, expect null 70 } 71 72 /** 73 * An object, c, reconstructed before the record, r, can be referenced from 74 * a record component. It's a cycle, from within the record, but the record, 75 * r, is not the target. 76 */ 77 @Test testCycle2()78 public void testCycle2() throws Exception { 79 out.println("\n---"); 80 C c = new C(); 81 R r = new R(3, 4, c); 82 c.obj = r; // cycle, serializing c first should preserve the cycle 83 84 out.println("serializing : " + c); 85 C deserializedObj = serializeDeserialize(c); 86 out.println("deserialized: " + deserializedObj); 87 assertTrue(deserializedObj instanceof C); // sanity 88 assertTrue(deserializedObj.obj != null); // expect non-null, r 89 assertEquals(((R)deserializedObj.obj).x(), 3); // sanity 90 assertEquals(((R)deserializedObj.obj).y(), 4); // sanity 91 } 92 93 record R2 (int x, int y, C c1, C c2) implements Serializable { } 94 95 /** 96 * Cycles, of non-record objects, within record components should be fine. 97 */ 98 @Test testCycle3()99 public void testCycle3() throws Exception { 100 out.println("\n---"); 101 C c1 = new C(); 102 C c2 = new C(); 103 c1.obj = c2; // --\-- cycle 104 c2.obj = c1; // \- cycle 105 R2 r = new R2(5, 6, c1, c2); 106 107 out.println("serializing : " + r); 108 R2 deserializedObj = serializeDeserialize(r); 109 out.println("deserialized: " + deserializedObj); 110 assertEquals(deserializedObj.x(), 5); // sanity 111 assertEquals(deserializedObj.y(), 6); // sanity 112 113 c1 = deserializedObj.c1(); 114 c2 = deserializedObj.c2(); 115 assertTrue(c1.obj == c2); 116 assertTrue(c2.obj == c1); 117 } 118 119 record R3 (long l, R r) implements Serializable { } 120 121 /** 122 * Deserialization of a record object, r, ( with a cycle ), should still 123 * deserialize from within a record, r3. 124 */ 125 @Test testCycle4()126 public void testCycle4() throws Exception { 127 out.println("\n---"); 128 C c = new C(); 129 R r = new R(7, 8, c); 130 c.obj = r; // cycle, targeting record r 131 R3 r3 = new R3(9, r); // record within a record 132 133 out.println("serializing : " + r3); 134 R3 deserializedObj = serializeDeserialize(r3); 135 out.println("deserialized: " + deserializedObj); 136 assertTrue(deserializedObj.r() != null); 137 assertEquals(deserializedObj.l(), 9); // sanity 138 assertEquals(deserializedObj.r().x(), 7); // sanity 139 assertEquals(deserializedObj.r().y(), 8); // sanity 140 assertTrue(deserializedObj.r().c() instanceof C); // sanity 141 assertEquals(deserializedObj.r().c().obj, null); // cycle, expect null 142 } 143 144 // --- infra 145 serialize(T obj)146 static <T> byte[] serialize(T obj) throws IOException { 147 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 148 ObjectOutputStream oos = new ObjectOutputStream(baos); 149 oos.writeObject(obj); 150 oos.close(); 151 return baos.toByteArray(); 152 } 153 154 @SuppressWarnings("unchecked") deserialize(byte[] streamBytes)155 static <T> T deserialize(byte[] streamBytes) 156 throws IOException, ClassNotFoundException 157 { 158 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 159 ObjectInputStream ois = new ObjectInputStream(bais); 160 return (T) ois.readObject(); 161 } 162 serializeDeserialize(T obj)163 static <T> T serializeDeserialize(T obj) 164 throws IOException, ClassNotFoundException 165 { 166 return deserialize(serialize(obj)); 167 } 168 } 169