1 /* 2 * Copyright (c) 2015, 2017, 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 8071474 27 * @summary Better failure atomicity for default read object. 28 * @modules jdk.compiler 29 * @library /test/lib 30 * @build jdk.test.lib.Platform 31 * jdk.test.lib.util.FileUtils 32 * @compile FailureAtomicity.java SerialRef.java 33 * @run main failureAtomicity.FailureAtomicity 34 */ 35 36 package failureAtomicity; 37 38 import java.io.ByteArrayInputStream; 39 import java.io.ByteArrayOutputStream; 40 import java.io.File; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.io.ObjectInputStream; 44 import java.io.ObjectOutputStream; 45 import java.io.ObjectStreamClass; 46 import java.io.UncheckedIOException; 47 import java.lang.reflect.Constructor; 48 import java.net.URL; 49 import java.net.URLClassLoader; 50 import java.nio.file.Files; 51 import java.nio.file.Path; 52 import java.nio.file.Paths; 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.List; 56 import java.util.function.BiConsumer; 57 import java.util.stream.Collectors; 58 import javax.tools.JavaCompiler; 59 import javax.tools.JavaFileObject; 60 import javax.tools.StandardJavaFileManager; 61 import javax.tools.StandardLocation; 62 import javax.tools.ToolProvider; 63 import jdk.test.lib.util.FileUtils; 64 65 @SuppressWarnings("unchecked") 66 public class FailureAtomicity { 67 static final Path TEST_SRC = Paths.get(System.getProperty("test.src", ".")); 68 static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", ".")); 69 static final Path fooTemplate = TEST_SRC.resolve("Foo.template"); 70 static final Path barTemplate = TEST_SRC.resolve("Bar.template"); 71 72 static final String[] PKGS = { "a.b.c", "x.y.z" }; 73 main(String[] args)74 public static void main(String[] args) throws Exception { 75 test_Foo(); 76 test_BadFoo(); // 'Bad' => incompatible type; cannot be "fully" deserialized 77 test_FooWithReadObject(); 78 test_BadFooWithReadObject(); 79 80 test_Foo_Bar(); 81 test_Foo_BadBar(); 82 test_BadFoo_Bar(); 83 test_BadFoo_BadBar(); 84 test_Foo_BarWithReadObject(); 85 test_Foo_BadBarWithReadObject(); 86 test_BadFoo_BarWithReadObject(); 87 test_BadFoo_BadBarWithReadObject(); 88 test_FooWithReadObject_Bar(); 89 test_FooWithReadObject_BadBar(); 90 test_BadFooWithReadObject_Bar(); 91 test_BadFooWithReadObject_BadBar(); 92 } 93 94 static final BiConsumer<Object,Object> FOO_FIELDS_EQUAL = (a,b) -> { 95 try { 96 int aPrim = a.getClass().getField("fooPrim").getInt(a); 97 int bPrim = b.getClass().getField("fooPrim").getInt(b); 98 if (aPrim != bPrim) 99 throw new AssertionError("Not equal: (" + aPrim + "!=" + bPrim 100 + "), in [" + a + "] [" + b + "]"); 101 Object aRef = a.getClass().getField("fooRef").get(a); 102 Object bRef = b.getClass().getField("fooRef").get(b); 103 if (!aRef.equals(bRef)) 104 throw new RuntimeException("Not equal: (" + aRef + "!=" + bRef 105 + "), in [" + a + "] [" + b + "]"); 106 } catch (NoSuchFieldException | IllegalAccessException x) { 107 throw new InternalError(x); 108 } 109 }; 110 static final BiConsumer<Object,Object> FOO_FIELDS_DEFAULT = (ignore,b) -> { 111 try { 112 int aPrim = b.getClass().getField("fooPrim").getInt(b); 113 if (aPrim != 0) 114 throw new AssertionError("Expected 0, got:" + aPrim 115 + ", in [" + b + "]"); 116 Object aRef = b.getClass().getField("fooRef").get(b); 117 if (aRef != null) 118 throw new RuntimeException("Expected null, got:" + aRef 119 + ", in [" + b + "]"); 120 } catch (NoSuchFieldException | IllegalAccessException x) { 121 throw new InternalError(x); 122 } 123 }; 124 static final BiConsumer<Object,Object> BAR_FIELDS_EQUAL = (a,b) -> { 125 try { 126 long aPrim = a.getClass().getField("barPrim").getLong(a); 127 long bPrim = b.getClass().getField("barPrim").getLong(b); 128 if (aPrim != bPrim) 129 throw new AssertionError("Not equal: (" + aPrim + "!=" + bPrim 130 + "), in [" + a + "] [" + b + "]"); 131 Object aRef = a.getClass().getField("barRef").get(a); 132 Object bRef = b.getClass().getField("barRef").get(b); 133 if (!aRef.equals(bRef)) 134 throw new RuntimeException("Not equal: (" + aRef + "!=" + bRef 135 + "), in [" + a + "] [" + b + "]"); 136 } catch (NoSuchFieldException | IllegalAccessException x) { 137 throw new InternalError(x); 138 } 139 }; 140 static final BiConsumer<Object,Object> BAR_FIELDS_DEFAULT = (ignore,b) -> { 141 try { 142 long aPrim = b.getClass().getField("barPrim").getLong(b); 143 if (aPrim != 0L) 144 throw new AssertionError("Expected 0, got:" + aPrim 145 + ", in [" + b + "]"); 146 Object aRef = b.getClass().getField("barRef").get(b); 147 if (aRef != null) 148 throw new RuntimeException("Expected null, got:" + aRef 149 + ", in [" + b + "]"); 150 } catch (NoSuchFieldException | IllegalAccessException x) { 151 throw new InternalError(x); 152 } 153 }; 154 test_Foo()155 static void test_Foo() { 156 testFoo("Foo", "String", false, false, FOO_FIELDS_EQUAL); } test_BadFoo()157 static void test_BadFoo() { 158 testFoo("BadFoo", "byte[]", true, false, FOO_FIELDS_DEFAULT); } test_FooWithReadObject()159 static void test_FooWithReadObject() { 160 testFoo("FooWithReadObject", "String", false, true, FOO_FIELDS_EQUAL); } test_BadFooWithReadObject()161 static void test_BadFooWithReadObject() { 162 testFoo("BadFooWithReadObject", "byte[]", true, true, FOO_FIELDS_DEFAULT); } 163 testFoo(String testName, String xyzZebraType, boolean expectCCE, boolean withReadObject, BiConsumer<Object,Object>... resultCheckers)164 static void testFoo(String testName, String xyzZebraType, 165 boolean expectCCE, boolean withReadObject, 166 BiConsumer<Object,Object>... resultCheckers) { 167 System.out.println("\nTesting " + testName); 168 try { 169 Path testRoot = testDir(testName); 170 Path srcRoot = Files.createDirectory(testRoot.resolve("src")); 171 List<Path> srcFiles = new ArrayList<>(); 172 srcFiles.add(createSrc(PKGS[0], fooTemplate, srcRoot, "String", withReadObject)); 173 srcFiles.add(createSrc(PKGS[1], fooTemplate, srcRoot, xyzZebraType, withReadObject)); 174 175 Path build = Files.createDirectory(testRoot.resolve("build")); 176 javac(build, srcFiles); 177 178 URLClassLoader loader = new URLClassLoader(new URL[]{ build.toUri().toURL() }, 179 FailureAtomicity.class.getClassLoader()); 180 Class<?> fooClass = Class.forName(PKGS[0] + ".Foo", true, loader); 181 Constructor<?> ctr = fooClass.getConstructor( 182 new Class<?>[]{int.class, String.class, String.class}); 183 Object abcFoo = ctr.newInstance(5, "chegar", "zebra"); 184 185 try { 186 toOtherPkgInstance(abcFoo, loader); 187 if (expectCCE) 188 throw new AssertionError("Expected CCE not thrown"); 189 } catch (ClassCastException e) { 190 if (!expectCCE) 191 throw new AssertionError("UnExpected CCE: " + e); 192 } 193 194 Object deserialInstance = failureAtomicity.SerialRef.obj; 195 196 System.out.println("abcFoo: " + abcFoo); 197 System.out.println("deserialInstance: " + deserialInstance); 198 199 for (BiConsumer<Object, Object> rc : resultCheckers) 200 rc.accept(abcFoo, deserialInstance); 201 } catch (IOException x) { 202 throw new UncheckedIOException(x); 203 } catch (ReflectiveOperationException x) { 204 throw new InternalError(x); 205 } 206 } 207 test_Foo_Bar()208 static void test_Foo_Bar() { 209 testFooBar("Foo_Bar", "String", "String", false, false, false, 210 FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL); 211 } test_Foo_BadBar()212 static void test_Foo_BadBar() { 213 testFooBar("Foo_BadBar", "String", "byte[]", true, false, false, 214 FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); 215 } test_BadFoo_Bar()216 static void test_BadFoo_Bar() { 217 testFooBar("BadFoo_Bar", "byte[]", "String", true, false, false, 218 FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); 219 } test_BadFoo_BadBar()220 static void test_BadFoo_BadBar() { 221 testFooBar("BadFoo_BadBar", "byte[]", "byte[]", true, false, false, 222 FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); 223 } test_Foo_BarWithReadObject()224 static void test_Foo_BarWithReadObject() { 225 testFooBar("Foo_BarWithReadObject", "String", "String", false, false, true, 226 FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL); 227 } test_Foo_BadBarWithReadObject()228 static void test_Foo_BadBarWithReadObject() { 229 testFooBar("Foo_BadBarWithReadObject", "String", "byte[]", true, false, true, 230 FOO_FIELDS_EQUAL, BAR_FIELDS_DEFAULT); 231 } test_BadFoo_BarWithReadObject()232 static void test_BadFoo_BarWithReadObject() { 233 testFooBar("BadFoo_BarWithReadObject", "byte[]", "String", true, false, true, 234 FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); 235 } test_BadFoo_BadBarWithReadObject()236 static void test_BadFoo_BadBarWithReadObject() { 237 testFooBar("BadFoo_BadBarWithReadObject", "byte[]", "byte[]", true, false, true, 238 FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); 239 } 240 test_FooWithReadObject_Bar()241 static void test_FooWithReadObject_Bar() { 242 testFooBar("FooWithReadObject_Bar", "String", "String", false, true, false, 243 FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL); 244 } test_FooWithReadObject_BadBar()245 static void test_FooWithReadObject_BadBar() { 246 testFooBar("FooWithReadObject_BadBar", "String", "byte[]", true, true, false, 247 FOO_FIELDS_EQUAL, BAR_FIELDS_DEFAULT); 248 } test_BadFooWithReadObject_Bar()249 static void test_BadFooWithReadObject_Bar() { 250 testFooBar("BadFooWithReadObject_Bar", "byte[]", "String", true, true, false, 251 FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); 252 } test_BadFooWithReadObject_BadBar()253 static void test_BadFooWithReadObject_BadBar() { 254 testFooBar("BadFooWithReadObject_BadBar", "byte[]", "byte[]", true, true, false, 255 FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); 256 } 257 testFooBar(String testName, String xyzFooZebraType, String xyzBarZebraType, boolean expectCCE, boolean fooWithReadObject, boolean barWithReadObject, BiConsumer<Object,Object>... resultCheckers)258 static void testFooBar(String testName, String xyzFooZebraType, 259 String xyzBarZebraType, boolean expectCCE, 260 boolean fooWithReadObject, boolean barWithReadObject, 261 BiConsumer<Object,Object>... resultCheckers) { 262 System.out.println("\nTesting " + testName); 263 try { 264 Path testRoot = testDir(testName); 265 Path srcRoot = Files.createDirectory(testRoot.resolve("src")); 266 List<Path> srcFiles = new ArrayList<>(); 267 srcFiles.add(createSrc(PKGS[0], fooTemplate, srcRoot, "String", 268 fooWithReadObject, "String")); 269 srcFiles.add(createSrc(PKGS[1], fooTemplate, srcRoot, xyzFooZebraType, 270 fooWithReadObject, xyzFooZebraType)); 271 srcFiles.add(createSrc(PKGS[0], barTemplate, srcRoot, "String", 272 barWithReadObject, "String")); 273 srcFiles.add(createSrc(PKGS[1], barTemplate, srcRoot, xyzBarZebraType, 274 barWithReadObject, xyzFooZebraType)); 275 276 Path build = Files.createDirectory(testRoot.resolve("build")); 277 javac(build, srcFiles); 278 279 URLClassLoader loader = new URLClassLoader(new URL[]{ build.toUri().toURL() }, 280 FailureAtomicity.class.getClassLoader()); 281 Class<?> fooClass = Class.forName(PKGS[0] + ".Bar", true, loader); 282 Constructor<?> ctr = fooClass.getConstructor( 283 new Class<?>[]{int.class, String.class, String.class, 284 long.class, String.class, String.class}); 285 Object abcBar = ctr.newInstance( 5, "chegar", "zebraFoo", 111L, "aBar", "zebraBar"); 286 287 try { 288 toOtherPkgInstance(abcBar, loader); 289 if (expectCCE) 290 throw new AssertionError("Expected CCE not thrown"); 291 } catch (ClassCastException e) { 292 if (!expectCCE) 293 throw new AssertionError("UnExpected CCE: " + e); 294 } 295 296 Object deserialInstance = failureAtomicity.SerialRef.obj; 297 298 System.out.println("abcBar: " + abcBar); 299 System.out.println("deserialInstance: " + deserialInstance); 300 301 for (BiConsumer<Object, Object> rc : resultCheckers) 302 rc.accept(abcBar, deserialInstance); 303 } catch (IOException x) { 304 throw new UncheckedIOException(x); 305 } catch (ReflectiveOperationException x) { 306 throw new InternalError(x); 307 } 308 } 309 testDir(String name)310 static Path testDir(String name) throws IOException { 311 Path testRoot = Paths.get("FailureAtomicity-" + name); 312 if (Files.exists(testRoot)) 313 FileUtils.deleteFileTreeWithRetry(testRoot); 314 Files.createDirectory(testRoot); 315 return testRoot; 316 } 317 platformPath(String p)318 static String platformPath(String p) { return p.replace("/", File.separator); } binaryName(String name)319 static String binaryName(String name) { return name.replace(".", "/"); } condRemove(String line, String pattern, boolean hasReadObject)320 static String condRemove(String line, String pattern, boolean hasReadObject) { 321 if (hasReadObject) { return line.replaceAll(pattern, ""); } 322 else { return line; } 323 } condReplace(String line, String... zebraFooType)324 static String condReplace(String line, String... zebraFooType) { 325 if (zebraFooType.length == 1) { 326 return line.replaceAll("\\$foo_zebra_type", zebraFooType[0]); 327 } else { return line; } 328 } nameFromTemplate(Path template)329 static String nameFromTemplate(Path template) { 330 return template.getFileName().toString().replaceAll(".template", ""); 331 } 332 createSrc(String pkg, Path srcTemplate, Path srcRoot, String zebraType, boolean hasReadObject, String... zebraFooType)333 static Path createSrc(String pkg, Path srcTemplate, Path srcRoot, 334 String zebraType, boolean hasReadObject, 335 String... zebraFooType) 336 throws IOException 337 { 338 Path srcDst = srcRoot.resolve(platformPath(binaryName(pkg))); 339 Files.createDirectories(srcDst); 340 Path srcFile = srcDst.resolve(nameFromTemplate(srcTemplate) + ".java"); 341 342 List<String> lines = Files.lines(srcTemplate) 343 .map(s -> s.replaceAll("\\$package", pkg)) 344 .map(s -> s.replaceAll("\\$zebra_type", zebraType)) 345 .map(s -> condReplace(s, zebraFooType)) 346 .map(s -> condRemove(s, "//\\$has_readObject", hasReadObject)) 347 .collect(Collectors.toList()); 348 Files.write(srcFile, lines); 349 return srcFile; 350 } 351 javac(Path dest, List<Path> sourceFiles)352 static void javac(Path dest, List<Path> sourceFiles) throws IOException { 353 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 354 try (StandardJavaFileManager fileManager = 355 compiler.getStandardFileManager(null, null, null)) { 356 List<File> files = sourceFiles.stream() 357 .map(p -> p.toFile()) 358 .collect(Collectors.toList()); 359 Iterable<? extends JavaFileObject> compilationUnits = 360 fileManager.getJavaFileObjectsFromFiles(files); 361 fileManager.setLocation(StandardLocation.CLASS_OUTPUT, 362 Arrays.asList(dest.toFile())); 363 fileManager.setLocation(StandardLocation.CLASS_PATH, 364 Arrays.asList(TEST_CLASSES.toFile())); 365 JavaCompiler.CompilationTask task = compiler 366 .getTask(null, fileManager, null, null, null, compilationUnits); 367 boolean passed = task.call(); 368 if (!passed) 369 throw new RuntimeException("Error compiling " + files); 370 } 371 } 372 toOtherPkgInstance(Object obj, ClassLoader loader)373 static Object toOtherPkgInstance(Object obj, ClassLoader loader) 374 throws IOException, ClassNotFoundException 375 { 376 byte[] bytes = serialize(obj); 377 bytes = replacePkg(bytes); 378 return deserialize(bytes, loader); 379 } 380 381 @SuppressWarnings("deprecation") replacePkg(byte[] bytes)382 static byte[] replacePkg(byte[] bytes) { 383 String str = new String(bytes, 0); 384 str = str.replaceAll(PKGS[0], PKGS[1]); 385 str.getBytes(0, bytes.length, bytes, 0); 386 return bytes; 387 } 388 serialize(Object obj)389 static byte[] serialize(Object obj) throws IOException { 390 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 391 ObjectOutputStream out = new ObjectOutputStream(baos);) { 392 out.writeObject(obj); 393 return baos.toByteArray(); 394 } 395 } 396 deserialize(byte[] data, ClassLoader l)397 static Object deserialize(byte[] data, ClassLoader l) 398 throws IOException, ClassNotFoundException 399 { 400 return new WithLoaderObjectInputStream(new ByteArrayInputStream(data), l) 401 .readObject(); 402 } 403 404 static class WithLoaderObjectInputStream extends ObjectInputStream { 405 final ClassLoader loader; WithLoaderObjectInputStream(InputStream is, ClassLoader loader)406 WithLoaderObjectInputStream(InputStream is, ClassLoader loader) 407 throws IOException 408 { 409 super(is); 410 this.loader = loader; 411 } 412 @Override resolveClass(ObjectStreamClass desc)413 protected Class<?> resolveClass(ObjectStreamClass desc) 414 throws IOException, ClassNotFoundException { 415 try { 416 return super.resolveClass(desc); 417 } catch (ClassNotFoundException x) { 418 String name = desc.getName(); 419 return Class.forName(name, false, loader); 420 } 421 } 422 } 423 } 424