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