1 /* 2 * Copyright (c) 2019, 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 * @summary No Serialization support of primitive classes, without a proxy 27 * @build Point Line NonFlattenValue Serialization 28 * @run testng/othervm Serialization 29 */ 30 31 import java.io.ByteArrayInputStream; 32 import java.io.ByteArrayOutputStream; 33 import java.io.DataOutputStream; 34 import java.io.Externalizable; 35 import java.io.IOException; 36 import java.io.InvalidClassException; 37 import java.io.InvalidObjectException; 38 import java.io.NotSerializableException; 39 import java.io.ObjectInput; 40 import java.io.ObjectInputStream; 41 import java.io.ObjectOutput; 42 import java.io.ObjectOutputStream; 43 import java.io.ObjectStreamClass; 44 import java.io.ObjectStreamException; 45 import java.io.Serializable; 46 import org.testng.annotations.DataProvider; 47 import org.testng.annotations.Test; 48 import static java.io.ObjectStreamConstants.*; 49 import static org.testng.Assert.assertEquals; 50 import static org.testng.Assert.assertThrows; 51 import static org.testng.Assert.expectThrows; 52 53 public class Serialization { 54 55 static final Class<NotSerializableException> NSE = NotSerializableException.class; 56 57 @DataProvider(name = "doesNotImplementSerializable") 58 public Object[][] doesNotImplementSerializable() { 59 return new Object[][] { 60 new Object[] { Point.makePoint(10, 100) }, 61 new Object[] { Line.makeLine(Point.makePoint(99, 99), 62 Point.makePoint(888, 888)) }, 63 new Object[] { NonFlattenValue.make(1001, 10005) }, 64 // an array of Points 65 new Object[] { new Point[] { 66 Point.makePoint(1, 5), 67 Point.makePoint(2, 6) } }, 68 new Object[] { new Object[] { 69 Point.makePoint(3, 7), 70 Point.makePoint(4, 8) } }, 71 }; 72 } 73 74 // primitive class that DOES NOT implement Serializable should throw NSE 75 @Test(dataProvider = "doesNotImplementSerializable") 76 public void doesNotImplementSerializable(Object obj) { 77 assertThrows(NSE, () -> serialize(obj)); 78 } 79 80 /** A Serializable Point */ 81 static primitive class SerializablePoint implements Serializable { 82 public int x; 83 public int y; 84 SerializablePoint(int x, int y) { this.x = x; this.y = y; } 85 static SerializablePoint make(int x, int y) { 86 return new SerializablePoint(x, y); 87 } 88 @Override public String toString() { 89 return "[SerializablePoint x=" + x + " y=" + y + "]"; } 90 } 91 92 /** An Externalizable Point */ 93 static primitive class ExternalizablePoint implements Externalizable { 94 public int x; 95 public int y; 96 ExternalizablePoint(int x, int y) { this.x = x; this.y = y; } 97 static ExternalizablePoint make(int x, int y) { 98 return new ExternalizablePoint(x, y); 99 } 100 @Override public void readExternal(ObjectInput in) { } 101 @Override public void writeExternal(ObjectOutput out) { } 102 @Override public String toString() { 103 return "[ExternalizablePoint x=" + x + " y=" + y + "]"; } 104 } 105 106 @DataProvider(name = "doesImplementSerializable") 107 public Object[][] doesImplementSerializable() { 108 return new Object[][] { 109 new Object[] { SerializablePoint.make(11, 101) }, 110 new Object[] { ExternalizablePoint.make(12, 102) }, 111 new Object[] { new ExternalizablePoint[] { 112 ExternalizablePoint.make(3, 7), 113 ExternalizablePoint.make(2, 8) } }, 114 new Object[] { new SerializablePoint[] { 115 SerializablePoint.make(1, 5), 116 SerializablePoint.make(2, 6) } }, 117 new Object[] { new Object[] { 118 SerializablePoint.make(3, 7), 119 SerializablePoint.make(4, 8) } }, 120 new Object[] { new Object[] { 121 ExternalizablePoint.make(13, 17), 122 ExternalizablePoint.make(14, 18) } }, 123 }; 124 } 125 126 // primitive class that DOES implement Serializable should throw NSE 127 @Test(dataProvider = "doesImplementSerializable") 128 public void doesImplementSerializable(Object obj) { 129 assertThrows(NSE, () -> serialize(obj)); 130 } 131 132 /** A Serializable Foo, with a serial proxy */ 133 static primitive class SerializableFoo implements Serializable { 134 public int x; 135 SerializableFoo(int x) { this.x = x; } 136 static SerializableFoo make(int x) { 137 return new SerializableFoo(x); 138 } 139 Object writeReplace() throws ObjectStreamException { 140 return new SerialFooProxy(x); 141 } 142 private void readObject(ObjectInputStream s) throws InvalidObjectException { 143 throw new InvalidObjectException("Proxy required"); 144 } 145 private static class SerialFooProxy implements Serializable { 146 final int x; 147 SerialFooProxy(int x) { this.x = x; } 148 Object readResolve() throws ObjectStreamException { 149 return SerializableFoo.make(this.x); 150 } 151 } 152 } 153 154 /** An Externalizable Foo, with a serial proxy */ 155 static primitive class ExternalizableFoo implements Externalizable { 156 public String s; 157 ExternalizableFoo(String s) { this.s = s; } 158 static ExternalizableFoo make(String s) { 159 return new ExternalizableFoo(s); 160 } 161 Object writeReplace() throws ObjectStreamException { 162 return new SerialFooProxy(s); 163 } 164 private void readObject(ObjectInputStream s) throws InvalidObjectException { 165 throw new InvalidObjectException("Proxy required"); 166 } 167 private static class SerialFooProxy implements Serializable { 168 final String s; 169 SerialFooProxy(String s) { this.s = s; } 170 Object readResolve() throws ObjectStreamException { 171 return ExternalizableFoo.make(this.s); 172 } 173 } 174 @Override public void readExternal(ObjectInput in) { } 175 @Override public void writeExternal(ObjectOutput out) { } 176 } 177 178 // primitive classes that DO implement Serializable, but have a serial proxy 179 @Test 180 public void serializableFooWithProxy() throws Exception { 181 SerializableFoo foo = SerializableFoo.make(45); 182 SerializableFoo foo1 = serializeDeserialize(foo); 183 assertEquals(foo.x, foo1.x); 184 } 185 @Test 186 public void serializableFooArrayWithProxy() throws Exception { 187 SerializableFoo[] fooArray = new SerializableFoo[]{SerializableFoo.make(46)}; 188 SerializableFoo[] fooArray1 = serializeDeserialize(fooArray); 189 assertEquals(fooArray.length, fooArray1.length); 190 assertEquals(fooArray[0].x, fooArray1[0].x); 191 } 192 @Test 193 public void externalizableFooWithProxy() throws Exception { 194 ExternalizableFoo foo = ExternalizableFoo.make("hello"); 195 ExternalizableFoo foo1 = serializeDeserialize(foo); 196 assertEquals(foo.s, foo1.s); 197 } 198 @Test 199 public void externalizableFooArrayWithProxy() throws Exception { 200 ExternalizableFoo[] fooArray = new ExternalizableFoo[] { ExternalizableFoo.make("there") }; 201 ExternalizableFoo[] fooArray1 = serializeDeserialize(fooArray); 202 assertEquals(fooArray.length, fooArray1.length); 203 assertEquals(fooArray[0].s, fooArray1[0].s); 204 } 205 206 private static byte[] byteStreamFor(String className, long uid, byte flags) 207 throws Exception 208 { 209 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 210 DataOutputStream dos = new DataOutputStream(baos); 211 dos.writeShort(STREAM_MAGIC); 212 dos.writeShort(STREAM_VERSION); 213 dos.writeByte(TC_OBJECT); 214 dos.writeByte(TC_CLASSDESC); 215 dos.writeUTF(className); 216 dos.writeLong(uid); 217 dos.writeByte(flags); 218 dos.writeShort(0); // number of fields 219 dos.writeByte(TC_ENDBLOCKDATA); // no annotations 220 dos.writeByte(TC_NULL); // no superclasses 221 dos.close(); 222 return baos.toByteArray(); 223 } 224 225 private static byte[] serializableByteStreamFor(String className, long uid) 226 throws Exception 227 { 228 return byteStreamFor(className, uid, SC_SERIALIZABLE); 229 } 230 231 private static byte[] externalizableByteStreamFor(String className, long uid) 232 throws Exception 233 { 234 return byteStreamFor(className, uid, SC_EXTERNALIZABLE); 235 } 236 237 @DataProvider(name = "classes") 238 public Object[][] classes() { 239 return new Object[][] { 240 new Object[] { Point.class }, 241 new Object[] { SerializablePoint.class } 242 }; 243 } 244 245 static final Class<InvalidClassException> ICE = InvalidClassException.class; 246 247 // primitive class read directly from a byte stream 248 @Test(dataProvider = "classes") 249 public void deserialize(Class<?> cls) throws Exception { 250 var clsDesc = ObjectStreamClass.lookup(cls); 251 long uid = clsDesc == null ? 0L : clsDesc.getSerialVersionUID(); 252 253 byte[] serialBytes = serializableByteStreamFor(cls.getName(), uid); 254 expectThrows(ICE, () -> deserialize(serialBytes)); 255 256 byte[] extBytes = externalizableByteStreamFor(cls.getName(), uid); 257 expectThrows(ICE, () -> deserialize(extBytes)); 258 } 259 260 static <T> byte[] serialize(T obj) throws IOException { 261 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 262 ObjectOutputStream oos = new ObjectOutputStream(baos); 263 oos.writeObject(obj); 264 oos.close(); 265 return baos.toByteArray(); 266 } 267 268 @SuppressWarnings("unchecked") 269 static <T> T deserialize(byte[] streamBytes) 270 throws IOException, ClassNotFoundException 271 { 272 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 273 ObjectInputStream ois = new ObjectInputStream(bais); 274 return (T) ois.readObject(); 275 } 276 277 @SuppressWarnings("unchecked") 278 static <T> T serializeDeserialize(T obj) 279 throws IOException, ClassNotFoundException 280 { 281 return (T) deserialize(serialize(obj)); 282 } 283 }