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 tests for SUID in the serial stream 28 * @run testng SerialVersionUIDTest 29 * @run testng/othervm/java.security.policy=empty_security.policy SerialVersionUIDTest 30 */ 31 package test.java.io.Serializable.records; 32 33 import java.io.ByteArrayInputStream; 34 import java.io.ByteArrayOutputStream; 35 import java.io.DataInputStream; 36 import java.io.DataOutputStream; 37 import java.io.IOException; 38 import java.io.ObjectInputStream; 39 import java.io.ObjectOutputStream; 40 import java.io.Serializable; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.stream.LongStream; 44 import org.testng.annotations.DataProvider; 45 import org.testng.annotations.Test; 46 import static java.io.ObjectStreamConstants.*; 47 import static java.lang.System.out; 48 import static org.testng.Assert.assertEquals; 49 import static org.testng.Assert.assertTrue; 50 51 public class SerialVersionUIDTest { 52 53 record R1 () implements Serializable { 54 private static final long serialVersionUID = 1L; 55 } 56 57 record R2 (int x, int y) implements Serializable { 58 private static final long serialVersionUID = 0L; 59 } 60 61 record R3 () implements Serializable { } 62 63 record R4 (String s) implements Serializable { } 64 65 record R5 (long l) implements Serializable { 66 private static final long serialVersionUID = 5678L; 67 } 68 69 @DataProvider(name = "recordObjects") recordObjects()70 public Object[][] recordObjects() { 71 return new Object[][] { 72 new Object[] { new R1(), 1L }, 73 new Object[] { new R2(1, 2), 0L }, 74 new Object[] { new R3(), 0L }, 75 new Object[] { new R4("s"), 0L }, 76 new Object[] { new R5(7L), 5678L }, 77 }; 78 } 79 80 /** 81 * Tests that a declared SUID for a record class is inserted into the stream. 82 */ 83 @Test(dataProvider = "recordObjects") testSerialize(Object objectToSerialize, long expectedUID)84 public void testSerialize(Object objectToSerialize, long expectedUID) 85 throws Exception 86 { 87 out.println("\n---"); 88 out.println("serializing : " + objectToSerialize); 89 byte[] bytes = serialize(objectToSerialize); 90 91 ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 92 DataInputStream dis = new DataInputStream(bais); 93 94 // sanity 95 assertEquals(dis.readShort(), STREAM_MAGIC); 96 assertEquals(dis.readShort(), STREAM_VERSION); 97 assertEquals(dis.readByte(), TC_OBJECT); 98 assertEquals(dis.readByte(), TC_CLASSDESC); 99 assertEquals(dis.readUTF(), objectToSerialize.getClass().getName()); 100 101 // verify that the UID is as expected 102 assertEquals(dis.readLong(), expectedUID); 103 } 104 105 @DataProvider(name = "recordClasses") recordClasses()106 public Object[][] recordClasses() { 107 List<Object[]> list = new ArrayList<>(); 108 List<Class<?>> recordClasses = List.of(R1.class, R2.class, R3.class, R4.class, R5.class); 109 LongStream.of(0L, 1L, 100L, 10_000L, 1_000_000L).forEach(suid -> 110 recordClasses.stream() 111 .map(cl -> new Object[] {cl, suid}) 112 .forEach(list::add)); 113 return list.stream().toArray(Object[][]::new); 114 } 115 116 /** 117 * Tests that matching of the serialVersionUID values ( stream value 118 * and runtime class value ) is waived for record classes. 119 */ 120 @Test(dataProvider = "recordClasses") testSerializeFromClass(Class<? extends Record> cl, long suid)121 public void testSerializeFromClass(Class<? extends Record> cl, long suid) 122 throws Exception 123 { 124 out.println("\n---"); 125 byte[] bytes = byteStreamFor(cl.getName(), suid); 126 Object obj = deserialize(bytes); 127 assertEquals(obj.getClass(), cl); 128 assertTrue(obj.getClass().isRecord()); 129 } 130 131 // --- infra 132 133 /** 134 * Returns a stream of bytes for the given class and uid. The 135 * stream will have no stream field values. 136 */ byteStreamFor(String className, long uid)137 static byte[] byteStreamFor(String className, long uid) 138 throws Exception 139 { 140 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 141 DataOutputStream dos = new DataOutputStream(baos); 142 dos.writeShort(STREAM_MAGIC); 143 dos.writeShort(STREAM_VERSION); 144 dos.writeByte(TC_OBJECT); 145 dos.writeByte(TC_CLASSDESC); 146 dos.writeUTF(className); 147 dos.writeLong(uid); 148 dos.writeByte(SC_SERIALIZABLE); 149 dos.writeShort(0); // number of fields 150 dos.writeByte(TC_ENDBLOCKDATA); // no annotations 151 dos.writeByte(TC_NULL); // no superclasses 152 dos.close(); 153 return baos.toByteArray(); 154 } 155 serialize(T obj)156 static <T> byte[] serialize(T obj) throws IOException { 157 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 158 ObjectOutputStream oos = new ObjectOutputStream(baos); 159 oos.writeObject(obj); 160 oos.close(); 161 return baos.toByteArray(); 162 } 163 deserialize(byte[] streamBytes)164 static <T> T deserialize(byte[] streamBytes) 165 throws IOException, ClassNotFoundException 166 { 167 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 168 ObjectInputStream ois = new ObjectInputStream(bais); 169 return (T) ois.readObject(); 170 } 171 } 172