8071474: Better failure atomicity for default read object
Reviewed-by: plevart, coffeys
This commit is contained in:
parent
5a582cecb9
commit
e11aec59a2
@ -253,9 +253,6 @@ public class ObjectInputStream
|
||||
/** 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 @@ public class ObjectInputStream
|
||||
Object curObj = ctx.getObj();
|
||||
ObjectStreamClass curDesc = ctx.getDesc();
|
||||
bin.setBlockDataMode(false);
|
||||
defaultReadFields(curObj, curDesc);
|
||||
FieldValues vals = defaultReadFields(curObj, curDesc);
|
||||
if (curObj != null) {
|
||||
defaultCheckFieldValues(curObj, curDesc, vals);
|
||||
defaultSetFieldValues(curObj, curDesc, vals);
|
||||
}
|
||||
bin.setBlockDataMode(true);
|
||||
if (!curDesc.hasWriteObjectData()) {
|
||||
/*
|
||||
@ -1881,6 +1882,26 @@ public class ObjectInputStream
|
||||
throws IOException
|
||||
{
|
||||
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
|
||||
// Best effort Failure Atomicity; slotValues will be non-null if field
|
||||
// values can be set after reading all field data in the hierarchy.
|
||||
// Field values can only be set after reading all data if there are no
|
||||
// user observable methods in the hierarchy, readObject(NoData). The
|
||||
// top most Serializable class in the hierarchy can be skipped.
|
||||
FieldValues[] slotValues = null;
|
||||
|
||||
boolean hasSpecialReadMethod = false;
|
||||
for (int i = 1; i < slots.length; i++) {
|
||||
ObjectStreamClass slotDesc = slots[i].desc;
|
||||
if (slotDesc.hasReadObjectMethod()
|
||||
|| slotDesc.hasReadObjectNoDataMethod()) {
|
||||
hasSpecialReadMethod = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// No special read methods, can store values and defer setting.
|
||||
if (!hasSpecialReadMethod)
|
||||
slotValues = new FieldValues[slots.length];
|
||||
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
ObjectStreamClass slotDesc = slots[i].desc;
|
||||
|
||||
@ -1917,7 +1938,13 @@ public class ObjectInputStream
|
||||
*/
|
||||
defaultDataEnd = false;
|
||||
} else {
|
||||
defaultReadFields(obj, slotDesc);
|
||||
FieldValues vals = defaultReadFields(obj, slotDesc);
|
||||
if (slotValues != null) {
|
||||
slotValues[i] = vals;
|
||||
} else if (obj != null) {
|
||||
defaultCheckFieldValues(obj, slotDesc, vals);
|
||||
defaultSetFieldValues(obj, slotDesc, vals);
|
||||
}
|
||||
}
|
||||
if (slotDesc.hasWriteObjectData()) {
|
||||
skipCustomData();
|
||||
@ -1933,6 +1960,19 @@ public class ObjectInputStream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obj != null && slotValues != null) {
|
||||
// Check that the non-primitive types are assignable for all slots
|
||||
// before assigning.
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
if (slotValues[i] != null)
|
||||
defaultCheckFieldValues(obj, slots[i].desc, slotValues[i]);
|
||||
}
|
||||
for (int i = 0; i < slots.length; i++) {
|
||||
if (slotValues[i] != null)
|
||||
defaultSetFieldValues(obj, slots[i].desc, slotValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1964,12 +2004,22 @@ public class ObjectInputStream
|
||||
}
|
||||
}
|
||||
|
||||
private class FieldValues {
|
||||
final byte[] primValues;
|
||||
final Object[] objValues;
|
||||
|
||||
FieldValues(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.
|
||||
* descriptor. Expects that passHandle is set to obj's handle before this
|
||||
* method is called.
|
||||
*/
|
||||
private void defaultReadFields(Object obj, ObjectStreamClass desc)
|
||||
private FieldValues defaultReadFields(Object obj, ObjectStreamClass desc)
|
||||
throws IOException
|
||||
{
|
||||
Class<?> cl = desc.forClass();
|
||||
@ -1977,22 +2027,19 @@ public class ObjectInputStream
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
byte[] primVals = null;
|
||||
int primDataSize = desc.getPrimDataSize();
|
||||
if (primDataSize > 0) {
|
||||
if (primVals == null || primVals.length < 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 +2048,30 @@ public class ObjectInputStream
|
||||
handles.markDependency(objHandle, passHandle);
|
||||
}
|
||||
}
|
||||
if (obj != null) {
|
||||
desc.setObjFieldValues(obj, objVals);
|
||||
}
|
||||
passHandle = objHandle;
|
||||
}
|
||||
|
||||
return new FieldValues(primVals, objVals);
|
||||
}
|
||||
|
||||
/** Throws ClassCastException if any value is not assignable. */
|
||||
private void defaultCheckFieldValues(Object obj, ObjectStreamClass desc,
|
||||
FieldValues values) {
|
||||
Object[] objectValues = values.objValues;
|
||||
if (objectValues != null)
|
||||
desc.checkObjFieldValueTypes(obj, objectValues);
|
||||
}
|
||||
|
||||
/** Sets field values in obj. */
|
||||
private void defaultSetFieldValues(Object obj, ObjectStreamClass desc,
|
||||
FieldValues values) {
|
||||
byte[] primValues = values.primValues;
|
||||
Object[] objectValues = values.objValues;
|
||||
|
||||
if (primValues != null)
|
||||
desc.setPrimFieldValues(obj, primValues);
|
||||
if (objectValues != null)
|
||||
desc.setObjFieldValues(obj, objectValues);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1252,6 +1252,15 @@ public class ObjectStreamClass implements Serializable {
|
||||
fieldRefl.getObjFieldValues(obj, vals);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -2069,6 +2078,15 @@ public class ObjectStreamClass implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -2077,6 +2095,10 @@ public class ObjectStreamClass implements Serializable {
|
||||
* 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,6 +2123,7 @@ public class ObjectStreamClass implements Serializable {
|
||||
f.getType().getName() + " in instance of " +
|
||||
obj.getClass().getName());
|
||||
}
|
||||
if (!dryRun)
|
||||
unsafe.putObject(obj, key, val);
|
||||
break;
|
||||
|
||||
|
64
jdk/test/java/io/Serializable/failureAtomicity/Bar.template
Normal file
64
jdk/test/java/io/Serializable/failureAtomicity/Bar.template
Normal file
@ -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 }
|
||||
}
|
@ -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<Object,Object> 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<Object,Object> 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<Object,Object> 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<Object,Object> 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<Object,Object>... resultCheckers) {
|
||||
System.out.println("\nTesting " + testName);
|
||||
try {
|
||||
Path testRoot = testDir(testName);
|
||||
Path srcRoot = Files.createDirectory(testRoot.resolve("src"));
|
||||
List<Path> 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<Object, Object> 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<Object,Object>... resultCheckers) {
|
||||
System.out.println("\nTesting " + testName);
|
||||
try {
|
||||
Path testRoot = testDir(testName);
|
||||
Path srcRoot = Files.createDirectory(testRoot.resolve("src"));
|
||||
List<Path> 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<Object, Object> 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<String> 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<Path> sourceFiles) throws IOException {
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
try (StandardJavaFileManager fileManager =
|
||||
compiler.getStandardFileManager(null, null, null)) {
|
||||
List<File> files = sourceFiles.stream()
|
||||
.map(p -> p.toFile())
|
||||
.collect(Collectors.toList());
|
||||
Iterable<? extends JavaFileObject> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
jdk/test/java/io/Serializable/failureAtomicity/Foo.template
Normal file
62
jdk/test/java/io/Serializable/failureAtomicity/Foo.template
Normal file
@ -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 }
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user