--- old/src/java.base/share/classes/java/io/ObjectInputStream.java 2015-03-19 21:15:25.084194176 +0000 +++ new/src/java.base/share/classes/java/io/ObjectInputStream.java 2015-03-19 21:15:24.900194182 +0000 @@ -253,9 +253,6 @@ /** flag set when at end of field value block with no TC_ENDBLOCKDATA */ private boolean defaultDataEnd = false; - /** buffer for reading primitive field values */ - private byte[] primVals; - /** if true, invoke readObjectOverride() instead of readObject() */ private final boolean enableOverride; /** if true, invoke resolveObject() */ @@ -500,7 +497,11 @@ Object curObj = ctx.getObj(); ObjectStreamClass curDesc = ctx.getDesc(); bin.setBlockDataMode(false); - defaultReadFields(curObj, curDesc); + StreamFieldValues vals = defaultReadFields(curObj, curDesc); + if (curObj != null) { + defaultCheckFieldValues(curObj, curDesc, vals); + defaultSetFieldValues(curObj, curDesc, vals); + } bin.setBlockDataMode(true); if (!curDesc.hasWriteObjectData()) { /* @@ -1881,6 +1882,24 @@ throws IOException { ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); + + // Best effort Failure Atomicity; Each element in 'slotFieldValues' + // contains the stream field values for the same element in 'slots', + // up to the first slot with a readObject(NoData) method ( a user + // visible effect ). + int index = 1; + for (; index < slots.length; index++) { + ObjectStreamClass slotDesc = slots[index].desc; + if (slotDesc.hasReadObjectMethod() + || slotDesc.hasReadObjectNoDataMethod()) { + break; + } + } + // Store, and defer setting, values for index slots, ignore if just one. + StreamFieldValues[] slotFieldValues = null; + if (index > 1 && obj != null) + slotFieldValues = new StreamFieldValues[index]; + for (int i = 0; i < slots.length; i++) { ObjectStreamClass slotDesc = slots[i].desc; @@ -1917,7 +1936,14 @@ */ defaultDataEnd = false; } else { - defaultReadFields(obj, slotDesc); + StreamFieldValues vals = defaultReadFields(obj, slotDesc); + if (obj != null) + defaultCheckFieldValues(obj, slotDesc, vals); + if (slotFieldValues != null) { + slotFieldValues[i] = vals; + } else if (obj != null) { + defaultSetFieldValues(obj, slotDesc, vals); + } } if (slotDesc.hasWriteObjectData()) { skipCustomData(); @@ -1932,6 +1958,24 @@ slotDesc.invokeReadObjectNoData(obj); } } + + if (slotFieldValues != null && i == (index - 1) && obj != null) { + assert slotFieldValues.length == index; + assert slots.length >= index; + setSlotFieldValues(obj, slots, slotFieldValues); + slotFieldValues = null; // remaining slots will be set when read + } + } + } + + /** Sets slot field values in the given obj. */ + private void setSlotFieldValues(Object obj, + ObjectStreamClass.ClassDataSlot[] slots, + StreamFieldValues[] slotFieldValues) { + int length = slotFieldValues.length; + for (int i = 0; i < length; i++) { + if (slotFieldValues[i] != null) + defaultSetFieldValues(obj, slots[i].desc, slotFieldValues[i]); } } @@ -1964,12 +2008,22 @@ } } + /** A holder for field values read from the stream. */ + private class StreamFieldValues { + final byte[] primValues; + final Object[] objValues; + StreamFieldValues(byte[] primValues, Object[] objValues) { + this.primValues = primValues; + this.objValues = objValues; + } + } + /** - * Reads in values of serializable fields declared by given class - * descriptor. If obj is non-null, sets field values in obj. Expects that - * passHandle is set to obj's handle before this method is called. + * Reads in and returns the values of serializable fields declared by the + * given class descriptor. Expects that passHandle is set to obj's handle + * before this method is called. */ - private void defaultReadFields(Object obj, ObjectStreamClass desc) + private StreamFieldValues defaultReadFields(Object obj, ObjectStreamClass desc) throws IOException { Class cl = desc.forClass(); @@ -1977,22 +2031,19 @@ throw new ClassCastException(); } + byte[] primVals = null; int primDataSize = desc.getPrimDataSize(); if (primDataSize > 0) { - if (primVals == null || primVals.length < primDataSize) { - primVals = new byte[primDataSize]; - } + primVals = new byte[primDataSize]; bin.readFully(primVals, 0, primDataSize, false); - if (obj != null) { - desc.setPrimFieldValues(obj, primVals); - } - } + } + Object[] objVals = null; int numObjFields = desc.getNumObjFields(); if (numObjFields > 0) { int objHandle = passHandle; ObjectStreamField[] fields = desc.getFields(false); - Object[] objVals = new Object[numObjFields]; + objVals = new Object[numObjFields]; int numPrimFields = fields.length - objVals.length; for (int i = 0; i < objVals.length; i++) { ObjectStreamField f = fields[numPrimFields + i]; @@ -2001,11 +2052,30 @@ handles.markDependency(objHandle, passHandle); } } - if (obj != null) { - desc.setObjFieldValues(obj, objVals); - } passHandle = objHandle; } + + return new StreamFieldValues(primVals, objVals); + } + + /** Throws ClassCastException if any value is not assignable. */ + private void defaultCheckFieldValues(Object obj, ObjectStreamClass desc, + StreamFieldValues values) { + Object[] objectValues = values.objValues; + if (objectValues != null) + desc.checkObjFieldValueTypes(obj, objectValues); + } + + /** Sets field values in obj. */ + private void defaultSetFieldValues(Object obj, ObjectStreamClass desc, + StreamFieldValues values) { + byte[] primValues = values.primValues; + Object[] objectValues = values.objValues; + + if (primValues != null) + desc.setPrimFieldValues(obj, primValues); + if (objectValues != null) + desc.setObjFieldValues(obj, objectValues); } /** --- old/src/java.base/share/classes/java/io/ObjectStreamClass.java 2015-03-19 21:15:25.612194157 +0000 +++ new/src/java.base/share/classes/java/io/ObjectStreamClass.java 2015-03-19 21:15:25.424194164 +0000 @@ -1253,6 +1253,15 @@ } /** + * Checks that the given values, from array vals starting at offset 0, + * are assignable to the given serializable object fields. + * @throws ClassCastException if any value is not assignable + */ + void checkObjFieldValueTypes(Object obj, Object[] vals) { + fieldRefl.checkObjectFieldValueTypes(obj, vals); + } + + /** * Sets the serializable object fields of object obj using values from * array vals starting at offset 0. It is the responsibility of the caller * to ensure that obj is of the proper type if non-null. @@ -2070,6 +2079,15 @@ } /** + * Checks that the given values, from array vals starting at offset 0, + * are assignable to the given serializable object fields. + * @throws ClassCastException if any value is not assignable + */ + void checkObjectFieldValueTypes(Object obj, Object[] vals) { + setObjFieldValues(obj, vals, true); + } + + /** * Sets the serializable object fields of object obj using values from * array vals starting at offset 0. The caller is responsible for * ensuring that obj is of the proper type; however, attempts to set a @@ -2077,6 +2095,10 @@ * ClassCastException. */ void setObjFieldValues(Object obj, Object[] vals) { + setObjFieldValues(obj, vals, false); + } + + private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) { if (obj == null) { throw new NullPointerException(); } @@ -2101,7 +2123,8 @@ f.getType().getName() + " in instance of " + obj.getClass().getName()); } - unsafe.putObject(obj, key, val); + if (!dryRun) + unsafe.putObject(obj, key, val); break; default: --- /dev/null 2014-12-08 16:24:59.450933980 +0000 +++ new/test/java/io/Serializable/failureAtomicity/Bar.template 2015-03-19 21:15:25.920194147 +0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package $package; + +import java.io.IOException; +import java.io.Serializable; +import failureAtomicity.SerialRef; + +public class Bar extends Foo implements Serializable { + static final long serialVersionUID = -0L; + + public final long barPrim; + public final String barRef; + + public final SerialRef ref; // So we can retrieve a reference to check + public $zebra_type zebraBar; // ordered alphabetically, must be last + + public Bar(int fooPrim, String fooRef, $foo_zebra_type fooZebra, + long barPrim, String barRef, $zebra_type zebra) { + super(fooPrim, fooRef, fooZebra); + this.barPrim = barPrim; + this.barRef = barRef; + this.zebraBar = zebra; + this.ref = new SerialRef(this); + } + + @Override + public String toString() { + return new StringBuilder() + .append("$package.Bar[") + .append("barPrim:").append(barPrim) + .append(", barRef:").append(barRef) + .append(", zebraBar:").append(zebraBar) + .append(", " + super.toString()) + .toString(); + } + +//$has_readObject private void readObject(java.io.ObjectInputStream in) +//$has_readObject throws IOException, ClassNotFoundException +//$has_readObject { +//$has_readObject in.defaultReadObject(); +//$has_readObject } +} --- /dev/null 2014-12-08 16:24:59.450933980 +0000 +++ new/test/java/io/Serializable/failureAtomicity/Foo.template 2015-03-19 21:15:26.372194131 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package $package; + +import java.io.IOException; +import java.io.Serializable; +import failureAtomicity.SerialRef; + +public class Foo implements Serializable { + static final long serialVersionUID = -0L; + + public final int fooPrim; + public final String fooRef; + + public final SerialRef ref; // So we can retrieve a reference to check + public $zebra_type zebraFoo; // ordered alphabetically, must be last + + public Foo(int fooPrim, String fooRef, $zebra_type zebra) { + this.fooPrim = fooPrim; + this.fooRef = fooRef; + this.zebraFoo = zebra; + this.ref = new SerialRef(this); + } + + @Override + public String toString() { + return new StringBuilder() + .append("$package.Foo[") + .append("fooPrim:").append(fooPrim) + .append(", fooRef:").append(fooRef) + .append(", zebraFoo:").append(zebraFoo).append("]") + .toString(); + } + +//$has_readObject private void readObject(java.io.ObjectInputStream in) +//$has_readObject throws IOException, ClassNotFoundException +//$has_readObject { +//$has_readObject in.defaultReadObject(); +//$has_readObject } + +} --- /dev/null 2014-12-08 16:24:59.450933980 +0000 +++ new/test/java/io/Serializable/failureAtomicity/FailureAtomicity.java 2015-03-19 21:15:26.840194115 +0000 @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8071474 + * @summary Better failure atomicity for default read object. + * @library /lib/testlibrary + * @build jdk.testlibrary.FileUtils + * @compile FailureAtomicity.java SerialRef.java + * @run main failureAtomicity.FailureAtomicity + */ + +package failureAtomicity; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.UncheckedIOException; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; +import jdk.testlibrary.FileUtils; + +@SuppressWarnings("unchecked") +public class FailureAtomicity { + static final Path TEST_SRC = Paths.get(System.getProperty("test.src", ".")); + static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", ".")); + static final Path fooTemplate = TEST_SRC.resolve("Foo.template"); + static final Path barTemplate = TEST_SRC.resolve("Bar.template"); + + static final String[] PKGS = { "a.b.c", "x.y.z" }; + + public static void main(String[] args) throws Exception { + test_Foo(); + test_BadFoo(); // 'Bad' => incompatible type; cannot be "fully" deserialized + test_FooWithReadObject(); + test_BadFooWithReadObject(); + + test_Foo_Bar(); + test_Foo_BadBar(); + test_BadFoo_Bar(); + test_BadFoo_BadBar(); + test_Foo_BarWithReadObject(); + test_Foo_BadBarWithReadObject(); + test_BadFoo_BarWithReadObject(); + test_BadFoo_BadBarWithReadObject(); + test_FooWithReadObject_Bar(); + test_FooWithReadObject_BadBar(); + test_BadFooWithReadObject_Bar(); + test_BadFooWithReadObject_BadBar(); + } + + static final BiConsumer FOO_FIELDS_EQUAL = (a,b) -> { + try { + int aPrim = a.getClass().getField("fooPrim").getInt(a); + int bPrim = b.getClass().getField("fooPrim").getInt(b); + if (aPrim != bPrim) + throw new AssertionError("Not equal: (" + aPrim + "!=" + bPrim + + "), in [" + a + "] [" + b + "]"); + Object aRef = a.getClass().getField("fooRef").get(a); + Object bRef = b.getClass().getField("fooRef").get(b); + if (!aRef.equals(bRef)) + throw new RuntimeException("Not equal: (" + aRef + "!=" + bRef + + "), in [" + a + "] [" + b + "]"); + } catch (NoSuchFieldException | IllegalAccessException x) { + throw new InternalError(x); + } + }; + static final BiConsumer FOO_FIELDS_DEFAULT = (ignore,b) -> { + try { + int aPrim = b.getClass().getField("fooPrim").getInt(b); + if (aPrim != 0) + throw new AssertionError("Expected 0, got:" + aPrim + + ", in [" + b + "]"); + Object aRef = b.getClass().getField("fooRef").get(b); + if (aRef != null) + throw new RuntimeException("Expected null, got:" + aRef + + ", in [" + b + "]"); + } catch (NoSuchFieldException | IllegalAccessException x) { + throw new InternalError(x); + } + }; + static final BiConsumer BAR_FIELDS_EQUAL = (a,b) -> { + try { + long aPrim = a.getClass().getField("barPrim").getLong(a); + long bPrim = b.getClass().getField("barPrim").getLong(b); + if (aPrim != bPrim) + throw new AssertionError("Not equal: (" + aPrim + "!=" + bPrim + + "), in [" + a + "] [" + b + "]"); + Object aRef = a.getClass().getField("barRef").get(a); + Object bRef = b.getClass().getField("barRef").get(b); + if (!aRef.equals(bRef)) + throw new RuntimeException("Not equal: (" + aRef + "!=" + bRef + + "), in [" + a + "] [" + b + "]"); + } catch (NoSuchFieldException | IllegalAccessException x) { + throw new InternalError(x); + } + }; + static final BiConsumer BAR_FIELDS_DEFAULT = (ignore,b) -> { + try { + long aPrim = b.getClass().getField("barPrim").getLong(b); + if (aPrim != 0L) + throw new AssertionError("Expected 0, got:" + aPrim + + ", in [" + b + "]"); + Object aRef = b.getClass().getField("barRef").get(b); + if (aRef != null) + throw new RuntimeException("Expected null, got:" + aRef + + ", in [" + b + "]"); + } catch (NoSuchFieldException | IllegalAccessException x) { + throw new InternalError(x); + } + }; + + static void test_Foo() { + testFoo("Foo", "String", false, false, FOO_FIELDS_EQUAL); } + static void test_BadFoo() { + testFoo("BadFoo", "byte[]", true, false, FOO_FIELDS_DEFAULT); } + static void test_FooWithReadObject() { + testFoo("FooWithReadObject", "String", false, true, FOO_FIELDS_EQUAL); } + static void test_BadFooWithReadObject() { + testFoo("BadFooWithReadObject", "byte[]", true, true, FOO_FIELDS_DEFAULT); } + + static void testFoo(String testName, String xyzZebraType, + boolean expectCCE, boolean withReadObject, + BiConsumer... resultCheckers) { + System.out.println("\nTesting " + testName); + try { + Path testRoot = testDir(testName); + Path srcRoot = Files.createDirectory(testRoot.resolve("src")); + List srcFiles = new ArrayList<>(); + srcFiles.add(createSrc(PKGS[0], fooTemplate, srcRoot, "String", withReadObject)); + srcFiles.add(createSrc(PKGS[1], fooTemplate, srcRoot, xyzZebraType, withReadObject)); + + Path build = Files.createDirectory(testRoot.resolve("build")); + javac(build, srcFiles); + + URLClassLoader loader = new URLClassLoader(new URL[]{ build.toUri().toURL() }, + FailureAtomicity.class.getClassLoader()); + Class fooClass = Class.forName(PKGS[0] + ".Foo", true, loader); + Constructor ctr = fooClass.getConstructor( + new Class[]{int.class, String.class, String.class}); + Object abcFoo = ctr.newInstance(5, "chegar", "zebra"); + + try { + toOtherPkgInstance(abcFoo, loader); + if (expectCCE) + throw new AssertionError("Expected CCE not thrown"); + } catch (ClassCastException e) { + if (!expectCCE) + throw new AssertionError("UnExpected CCE: " + e); + } + + Object deserialInstance = failureAtomicity.SerialRef.obj; + + System.out.println("abcFoo: " + abcFoo); + System.out.println("deserialInstance: " + deserialInstance); + + for (BiConsumer rc : resultCheckers) + rc.accept(abcFoo, deserialInstance); + } catch (IOException x) { + throw new UncheckedIOException(x); + } catch (ReflectiveOperationException x) { + throw new InternalError(x); + } + } + + static void test_Foo_Bar() { + testFooBar("Foo_Bar", "String", "String", false, false, false, + FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL); + } + static void test_Foo_BadBar() { + testFooBar("Foo_BadBar", "String", "byte[]", true, false, false, + FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); + } + static void test_BadFoo_Bar() { + testFooBar("BadFoo_Bar", "byte[]", "String", true, false, false, + FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); + } + static void test_BadFoo_BadBar() { + testFooBar("BadFoo_BadBar", "byte[]", "byte[]", true, false, false, + FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); + } + static void test_Foo_BarWithReadObject() { + testFooBar("Foo_BarWithReadObject", "String", "String", false, false, true, + FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL); + } + static void test_Foo_BadBarWithReadObject() { + testFooBar("Foo_BadBarWithReadObject", "String", "byte[]", true, false, true, + FOO_FIELDS_EQUAL, BAR_FIELDS_DEFAULT); + } + static void test_BadFoo_BarWithReadObject() { + testFooBar("BadFoo_BarWithReadObject", "byte[]", "String", true, false, true, + FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); + } + static void test_BadFoo_BadBarWithReadObject() { + testFooBar("BadFoo_BadBarWithReadObject", "byte[]", "byte[]", true, false, true, + FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); + } + + static void test_FooWithReadObject_Bar() { + testFooBar("FooWithReadObject_Bar", "String", "String", false, true, false, + FOO_FIELDS_EQUAL, BAR_FIELDS_EQUAL); + } + static void test_FooWithReadObject_BadBar() { + testFooBar("FooWithReadObject_BadBar", "String", "byte[]", true, true, false, + FOO_FIELDS_EQUAL, BAR_FIELDS_DEFAULT); + } + static void test_BadFooWithReadObject_Bar() { + testFooBar("BadFooWithReadObject_Bar", "byte[]", "String", true, true, false, + FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); + } + static void test_BadFooWithReadObject_BadBar() { + testFooBar("BadFooWithReadObject_BadBar", "byte[]", "byte[]", true, true, false, + FOO_FIELDS_DEFAULT, BAR_FIELDS_DEFAULT); + } + + static void testFooBar(String testName, String xyzFooZebraType, + String xyzBarZebraType, boolean expectCCE, + boolean fooWithReadObject, boolean barWithReadObject, + BiConsumer... resultCheckers) { + System.out.println("\nTesting " + testName); + try { + Path testRoot = testDir(testName); + Path srcRoot = Files.createDirectory(testRoot.resolve("src")); + List srcFiles = new ArrayList<>(); + srcFiles.add(createSrc(PKGS[0], fooTemplate, srcRoot, "String", + fooWithReadObject, "String")); + srcFiles.add(createSrc(PKGS[1], fooTemplate, srcRoot, xyzFooZebraType, + fooWithReadObject, xyzFooZebraType)); + srcFiles.add(createSrc(PKGS[0], barTemplate, srcRoot, "String", + barWithReadObject, "String")); + srcFiles.add(createSrc(PKGS[1], barTemplate, srcRoot, xyzBarZebraType, + barWithReadObject, xyzFooZebraType)); + + Path build = Files.createDirectory(testRoot.resolve("build")); + javac(build, srcFiles); + + URLClassLoader loader = new URLClassLoader(new URL[]{ build.toUri().toURL() }, + FailureAtomicity.class.getClassLoader()); + Class fooClass = Class.forName(PKGS[0] + ".Bar", true, loader); + Constructor ctr = fooClass.getConstructor( + new Class[]{int.class, String.class, String.class, + long.class, String.class, String.class}); + Object abcBar = ctr.newInstance( 5, "chegar", "zebraFoo", 111L, "aBar", "zebraBar"); + + try { + toOtherPkgInstance(abcBar, loader); + if (expectCCE) + throw new AssertionError("Expected CCE not thrown"); + } catch (ClassCastException e) { + if (!expectCCE) + throw new AssertionError("UnExpected CCE: " + e); + } + + Object deserialInstance = failureAtomicity.SerialRef.obj; + + System.out.println("abcBar: " + abcBar); + System.out.println("deserialInstance: " + deserialInstance); + + for (BiConsumer rc : resultCheckers) + rc.accept(abcBar, deserialInstance); + } catch (IOException x) { + throw new UncheckedIOException(x); + } catch (ReflectiveOperationException x) { + throw new InternalError(x); + } + } + + static Path testDir(String name) throws IOException { + Path testRoot = Paths.get("FailureAtomicity-" + name); + if (Files.exists(testRoot)) + FileUtils.deleteFileTreeWithRetry(testRoot); + Files.createDirectory(testRoot); + return testRoot; + } + + static String platformPath(String p) { return p.replace("/", File.separator); } + static String binaryName(String name) { return name.replace(".", "/"); } + static String condRemove(String line, String pattern, boolean hasReadObject) { + if (hasReadObject) { return line.replaceAll(pattern, ""); } + else { return line; } + } + static String condReplace(String line, String... zebraFooType) { + if (zebraFooType.length == 1) { + return line.replaceAll("\\$foo_zebra_type", zebraFooType[0]); + } else { return line; } + } + static String nameFromTemplate(Path template) { + return template.getFileName().toString().replaceAll(".template", ""); + } + + static Path createSrc(String pkg, Path srcTemplate, Path srcRoot, + String zebraType, boolean hasReadObject, + String... zebraFooType) + throws IOException + { + Path srcDst = srcRoot.resolve(platformPath(binaryName(pkg))); + Files.createDirectories(srcDst); + Path srcFile = srcDst.resolve(nameFromTemplate(srcTemplate) + ".java"); + + List lines = Files.lines(srcTemplate) + .map(s -> s.replaceAll("\\$package", pkg)) + .map(s -> s.replaceAll("\\$zebra_type", zebraType)) + .map(s -> condReplace(s, zebraFooType)) + .map(s -> condRemove(s, "//\\$has_readObject", hasReadObject)) + .collect(Collectors.toList()); + Files.write(srcFile, lines); + return srcFile; + } + + static void javac(Path dest, List sourceFiles) throws IOException { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + try (StandardJavaFileManager fileManager = + compiler.getStandardFileManager(null, null, null)) { + List files = sourceFiles.stream() + .map(p -> p.toFile()) + .collect(Collectors.toList()); + Iterable compilationUnits = + fileManager.getJavaFileObjectsFromFiles(files); + fileManager.setLocation(StandardLocation.CLASS_OUTPUT, + Arrays.asList(dest.toFile())); + fileManager.setLocation(StandardLocation.CLASS_PATH, + Arrays.asList(TEST_CLASSES.toFile())); + JavaCompiler.CompilationTask task = compiler + .getTask(null, fileManager, null, null, null, compilationUnits); + boolean passed = task.call(); + if (!passed) + throw new RuntimeException("Error compiling " + files); + } + } + + static Object toOtherPkgInstance(Object obj, ClassLoader loader) + throws IOException, ClassNotFoundException + { + byte[] bytes = serialize(obj); + bytes = replacePkg(bytes); + return deserialize(bytes, loader); + } + + @SuppressWarnings("deprecation") + static byte[] replacePkg(byte[] bytes) { + String str = new String(bytes, 0); + str = str.replaceAll(PKGS[0], PKGS[1]); + str.getBytes(0, bytes.length, bytes, 0); + return bytes; + } + + static byte[] serialize(Object obj) throws IOException { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos);) { + out.writeObject(obj); + return baos.toByteArray(); + } + } + + static Object deserialize(byte[] data, ClassLoader l) + throws IOException, ClassNotFoundException + { + return new WithLoaderObjectInputStream(new ByteArrayInputStream(data), l) + .readObject(); + } + + static class WithLoaderObjectInputStream extends ObjectInputStream { + final ClassLoader loader; + WithLoaderObjectInputStream(InputStream is, ClassLoader loader) + throws IOException + { + super(is); + this.loader = loader; + } + @Override + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + try { + return super.resolveClass(desc); + } catch (ClassNotFoundException x) { + String name = desc.getName(); + return Class.forName(name, false, loader); + } + } + } +} --- /dev/null 2014-12-08 16:24:59.450933980 +0000 +++ new/test/java/io/Serializable/failureAtomicity/SerialRef.java 2015-03-19 21:15:27.304194099 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package failureAtomicity; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +// For verification purposes only. + +public class SerialRef implements Serializable { + static final long serialVersionUID = -0L; + public static Object obj; + + private final Object ref; + + public SerialRef(Object ref) { + this.ref = ref; + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + SerialRef.obj = ref; + } +}