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