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 Tests constructor invocation exceptions are handled appropriately 28 * @run testng ThrowingConstructorTest 29 * @run testng/othervm/java.security.policy=empty_security.policy ThrowingConstructorTest 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.InvalidObjectException; 37 import java.io.ObjectInputStream; 38 import java.io.ObjectOutputStream; 39 import java.io.Serializable; 40 import org.testng.annotations.DataProvider; 41 import org.testng.annotations.Test; 42 import static java.lang.System.out; 43 import static org.testng.Assert.assertEquals; 44 import static org.testng.Assert.assertTrue; 45 import static org.testng.Assert.expectThrows; 46 47 /** 48 * If the constructor invocation throws an exception, an 49 * `InvalidObjectException` is thrown with that exception as its cause. 50 */ 51 public class ThrowingConstructorTest { 52 53 /** "big switch" that can be used to allow/disallow record construction 54 * set to true after the data provider has constructed all record objects */ 55 private static volatile boolean firstDataSetCreated; 56 57 record R1 () implements Serializable { R1()58 public R1() { 59 if (firstDataSetCreated) 60 throw new NullPointerException("thrown from R1"); 61 } 62 } 63 64 record R2 (int x) implements Serializable { R2(int x)65 public R2(int x) { 66 if (firstDataSetCreated) 67 throw new IllegalArgumentException("thrown from R2"); 68 this.x = x; 69 } 70 } 71 72 record R3 (int x, int y) implements Serializable { R3(int x, int y)73 public R3(int x, int y) { 74 if (firstDataSetCreated) 75 throw new NumberFormatException("thrown from R3"); 76 this.x = x; 77 this.y = y; 78 } 79 } 80 81 static class C implements Serializable { 82 final Object obj ; C(Object obj)83 C(Object obj) { this.obj= obj; } toString()84 @Override public String toString() { return "C[" + obj + "]"; } 85 } 86 87 static final Class<InvalidObjectException> IOE = InvalidObjectException.class; 88 89 @DataProvider(name = "exceptionInstances") exceptionInstances()90 public Object[][] exceptionInstances() { 91 Object[][] objs = new Object[][] { 92 new Object[] { new R1(), NullPointerException.class, "thrown from R1" }, 93 new Object[] { new R2(1), IllegalArgumentException.class, "thrown from R2" }, 94 new Object[] { new R3(2, 3), NumberFormatException .class, "thrown from R3" }, 95 new Object[] { new C(new R1()), NullPointerException.class, "thrown from R1" }, 96 new Object[] { new C(new R2(4)), IllegalArgumentException.class, "thrown from R2" }, 97 new Object[] { new C(new R3(5, 6)), NumberFormatException .class, "thrown from R3" }, 98 }; 99 firstDataSetCreated = true; 100 return objs; 101 } 102 103 @Test(dataProvider = "exceptionInstances") testExceptions(Object objectToSerialize, Class<? extends Throwable> expectedExType, String expectedExMessage)104 public void testExceptions(Object objectToSerialize, 105 Class<? extends Throwable> expectedExType, 106 String expectedExMessage) 107 throws Exception 108 { 109 out.println("\n---"); 110 out.println("serializing: " + objectToSerialize); 111 byte[] bytes = serialize(objectToSerialize); 112 InvalidObjectException ioe = expectThrows(IOE, () -> deserialize(bytes)); 113 out.println("caught expected IOE: " + ioe); 114 Throwable t = ioe.getCause(); 115 assertTrue(t.getClass().equals(expectedExType), 116 "Expected:" + expectedExType + ", got:" + t); 117 out.println("expected cause " + expectedExType +" : " + t); 118 assertEquals(t.getMessage(), expectedExMessage); 119 } 120 121 // -- errors ( pass through unwrapped ) 122 123 private static volatile boolean secondDataSetCreated; 124 125 record R4 () implements Serializable { R4()126 public R4() { 127 if (secondDataSetCreated) 128 throw new OutOfMemoryError("thrown from R4"); } 129 } 130 131 record R5 (int x) implements Serializable { R5(int x)132 public R5(int x) { 133 if (secondDataSetCreated) 134 throw new StackOverflowError("thrown from R5"); 135 this.x = x; 136 } 137 } 138 139 record R6 (int x, int y) implements Serializable { R6(int x, int y)140 public R6(int x, int y) { 141 if (secondDataSetCreated) 142 throw new AssertionError("thrown from R6"); 143 this.x = x; 144 this.y = y; 145 } 146 } 147 148 @DataProvider(name = "errorInstances") errorInstances()149 public Object[][] errorInstances() { 150 Object[][] objs = new Object[][] { 151 new Object[] { new R4(), OutOfMemoryError.class, "thrown from R4" }, 152 new Object[] { new R5(11), StackOverflowError.class, "thrown from R5" }, 153 new Object[] { new R6(12, 13), AssertionError .class, "thrown from R6" }, 154 new Object[] { new C(new R4()), OutOfMemoryError.class, "thrown from R4" }, 155 new Object[] { new C(new R5(14)), StackOverflowError.class, "thrown from R5" }, 156 new Object[] { new C(new R6(15, 16)), AssertionError .class, "thrown from R6" }, 157 }; 158 secondDataSetCreated = true; 159 return objs; 160 } 161 162 @Test(dataProvider = "errorInstances") testErrors(Object objectToSerialize, Class<? extends Throwable> expectedExType, String expectedExMessage)163 public void testErrors(Object objectToSerialize, 164 Class<? extends Throwable> expectedExType, 165 String expectedExMessage) 166 throws Exception 167 { 168 out.println("\n---"); 169 out.println("serializing: " + objectToSerialize); 170 byte[] bytes = serialize(objectToSerialize); 171 Throwable t = expectThrows(expectedExType, () -> deserialize(bytes)); 172 assertTrue(t.getClass().equals(expectedExType), 173 "Expected:" + expectedExType + ", got:" + t); 174 out.println("caught expected " + expectedExType +" : " + t); 175 assertEquals(t.getMessage(), expectedExMessage); 176 } 177 178 // --- infra 179 serialize(T obj)180 static <T> byte[] serialize(T obj) throws IOException { 181 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 182 ObjectOutputStream oos = new ObjectOutputStream(baos); 183 oos.writeObject(obj); 184 oos.close(); 185 return baos.toByteArray(); 186 } 187 188 @SuppressWarnings("unchecked") deserialize(byte[] streamBytes)189 static <T> T deserialize(byte[] streamBytes) 190 throws IOException, ClassNotFoundException 191 { 192 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 193 ObjectInputStream ois = new ObjectInputStream(bais); 194 return (T) ois.readObject(); 195 } 196 serializeDeserialize(T obj)197 static <T> T serializeDeserialize(T obj) 198 throws IOException, ClassNotFoundException 199 { 200 return deserialize(serialize(obj)); 201 } 202 } 203