changeset 12596:f3d7734ad739

8164908: ReflectionFactory support for IIOP and custom serialization Reviewed-by: chegar, psandoz
author rriggs
date Thu, 08 Dec 2016 14:06:39 -0500
parents 5fec8e5050b1
children 49f05b4e886d
files src/share/classes/java/lang/invoke/MethodHandles.java src/share/classes/sun/reflect/ReflectionFactory.java test/sun/reflect/ReflectionFactory/ReflectionFactoryTest.java test/sun/reflect/ReflectionFactory/security.policy
diffstat 4 files changed, 641 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/MethodHandles.java	Thu Dec 08 05:21:54 2016 -0800
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java	Thu Dec 08 14:06:39 2016 -0500
@@ -680,7 +680,9 @@
             // disallow lookup more restricted packages
             if (allowedModes == ALL_MODES && lookupClass.getClassLoader() == null) {
                 if (name.startsWith("java.") ||
-                        (name.startsWith("sun.") && !name.startsWith("sun.invoke."))) {
+                        (name.startsWith("sun.")
+                                && !name.startsWith("sun.invoke.")
+                                && !name.equals("sun.reflect.ReflectionFactory"))) {
                     throw newIllegalArgumentException("illegal lookupClass: " + lookupClass);
                 }
             }
--- a/src/share/classes/sun/reflect/ReflectionFactory.java	Thu Dec 08 05:21:54 2016 -0800
+++ b/src/share/classes/sun/reflect/ReflectionFactory.java	Thu Dec 08 14:06:39 2016 -0500
@@ -25,16 +25,28 @@
 
 package sun.reflect;
 
+import java.io.Externalizable;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OptionalDataException;
+import java.io.Serializable;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Field;
 import java.lang.reflect.Executable;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.Permission;
 import java.security.PrivilegedAction;
+import java.util.Objects;
+
 import sun.reflect.misc.ReflectUtil;
 
+
 /** <P> The master factory for all reflective objects, both those in
     java.lang.reflect (Fields, Methods, Constructors) as well as their
     delegates (FieldAccessors, MethodAccessors, ConstructorAccessors).
@@ -56,6 +68,9 @@
     // Provides access to package-private mechanisms in java.lang.reflect
     private static volatile LangReflectAccess langReflectAccess;
 
+    /* Method for static class initializer <clinit>, or null */
+    private static volatile Method hasStaticInitializerMethod;
+
     //
     // "Inflation" mechanism. Loading bytecodes to implement
     // Method.invoke() and Constructor.newInstance() currently costs
@@ -73,8 +88,7 @@
     private static boolean noInflation        = false;
     private static int     inflationThreshold = 15;
 
-    private ReflectionFactory() {
-    }
+    private ReflectionFactory() {}
 
     /**
      * A convenience class for acquiring the capability to instantiate
@@ -328,6 +342,14 @@
     //
     //
 
+    /**
+     * Returns an accessible constructor capable of creating instances
+     * of the given class, initialized by the given constructor.
+     *
+     * @param classToInstantiate the class to instantiate
+     * @param constructorToCall the constructor to call
+     * @return an accessible constructor
+     */
     public Constructor<?> newConstructorForSerialization
         (Class<?> classToInstantiate, Constructor<?> constructorToCall)
     {
@@ -335,6 +357,42 @@
         if (constructorToCall.getDeclaringClass() == classToInstantiate) {
             return constructorToCall;
         }
+        return generateConstructor(classToInstantiate, constructorToCall);
+    }
+
+    /**
+     * Returns an accessible no-arg constructor for a class.
+     * The no-arg constructor is found searching the class and its supertypes.
+     *
+     * @param cl the class to instantiate
+     * @return a no-arg constructor for the class or {@code null} if
+     *     the class or supertypes do not have a suitable no-arg constructor
+     */
+    public final Constructor<?> newConstructorForSerialization(Class<?> cl) {
+        Class<?> initCl = cl;
+        while (Serializable.class.isAssignableFrom(initCl)) {
+            if ((initCl = initCl.getSuperclass()) == null) {
+                return null;
+            }
+        }
+        Constructor<?> constructorToCall;
+        try {
+            constructorToCall = initCl.getDeclaredConstructor();
+            int mods = constructorToCall.getModifiers();
+            if ((mods & Modifier.PRIVATE) != 0 ||
+                    ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
+                            !packageEquals(cl, initCl))) {
+                return null;
+            }
+        } catch (NoSuchMethodException ex) {
+            return null;
+        }
+        return generateConstructor(cl, constructorToCall);
+    }
+
+    private final Constructor<?> generateConstructor(Class<?> classToInstantiate,
+                                                     Constructor<?> constructorToCall) {
+
 
         ConstructorAccessor acc = new MethodAccessorGenerator().
             generateSerializationConstructor(classToInstantiate,
@@ -355,9 +413,222 @@
                                           langReflectAccess().
                                           getConstructorParameterAnnotations(constructorToCall));
         setConstructorAccessor(c, acc);
+        c.setAccessible(true);
         return c;
     }
 
+    /**
+     * Returns an accessible no-arg constructor for an externalizable class to be
+     * initialized using a public no-argument constructor.
+     *
+     * @param cl the class to instantiate
+     * @return A no-arg constructor for the class; returns {@code null} if
+     *     the class does not implement {@link java.io.Externalizable}
+     */
+    public final Constructor<?> newConstructorForExternalization(Class<?> cl) {
+        if (!Externalizable.class.isAssignableFrom(cl)) {
+            return null;
+        }
+        try {
+            Constructor<?> cons = cl.getConstructor();
+            cons.setAccessible(true);
+            return cons;
+        } catch (NoSuchMethodException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a direct MethodHandle for the {@code readObject} method on
+     * a Serializable class.
+     * The first argument of {@link MethodHandle#invoke} is the serializable
+     * object and the second argument is the {@code ObjectInputStream} passed to
+     * {@code readObject}.
+     *
+     * @param cl a Serializable class
+     * @return  a direct MethodHandle for the {@code readObject} method of the class or
+     *          {@code null} if the class does not have a {@code readObject} method
+     */
+    public final MethodHandle readObjectForSerialization(Class<?> cl) {
+        return findReadWriteObjectForSerialization(cl, "readObject", ObjectInputStream.class);
+    }
+
+    /**
+     * Returns a direct MethodHandle for the {@code readObjectNoData} method on
+     * a Serializable class.
+     * The first argument of {@link MethodHandle#invoke} is the serializable
+     * object and the second argument is the {@code ObjectInputStream} passed to
+     * {@code readObjectNoData}.
+     *
+     * @param cl a Serializable class
+     * @return  a direct MethodHandle for the {@code readObjectNoData} method
+     *          of the class or {@code null} if the class does not have a
+     *          {@code readObjectNoData} method
+     */
+    public final MethodHandle readObjectNoDataForSerialization(Class<?> cl) {
+        return findReadWriteObjectForSerialization(cl, "readObjectNoData", ObjectInputStream.class);
+    }
+
+    /**
+     * Returns a direct MethodHandle for the {@code writeObject} method on
+     * a Serializable class.
+     * The first argument of {@link MethodHandle#invoke} is the serializable
+     * object and the second argument is the {@code ObjectOutputStream} passed to
+     * {@code writeObject}.
+     *
+     * @param cl a Serializable class
+     * @return  a direct MethodHandle for the {@code writeObject} method of the class or
+     *          {@code null} if the class does not have a {@code writeObject} method
+     */
+    public final MethodHandle writeObjectForSerialization(Class<?> cl) {
+        return findReadWriteObjectForSerialization(cl, "writeObject", ObjectOutputStream.class);
+    }
+
+    private final MethodHandle findReadWriteObjectForSerialization(Class<?> cl,
+                                                                   String methodName,
+                                                                   Class<?> streamClass) {
+        if (!Serializable.class.isAssignableFrom(cl)) {
+            return null;
+        }
+
+        try {
+            Method meth = cl.getDeclaredMethod(methodName, streamClass);
+            int mods = meth.getModifiers();
+            if (meth.getReturnType() != Void.TYPE ||
+                    Modifier.isStatic(mods) ||
+                    !Modifier.isPrivate(mods)) {
+                return null;
+            }
+            meth.setAccessible(true);
+            return MethodHandles.lookup().unreflect(meth);
+        } catch (NoSuchMethodException ex) {
+            return null;
+        } catch (IllegalAccessException ex1) {
+            throw new InternalError("Error", ex1);
+        }
+    }
+
+    /**
+     * Returns a direct MethodHandle for the {@code readResolve} method on
+     * a serializable class.
+     * The single argument of {@link MethodHandle#invoke} is the serializable
+     * object.
+     *
+     * @param cl the Serializable class
+     * @return  a direct MethodHandle for the {@code readResolve} method of the class or
+     *          {@code null} if the class does not have a {@code readResolve} method
+     */
+    public final MethodHandle readResolveForSerialization(Class<?> cl) {
+        return getReplaceResolveForSerialization(cl, "readResolve");
+    }
+
+    /**
+     * Returns a direct MethodHandle for the {@code writeReplace} method on
+     * a serializable class.
+     * The single argument of {@link MethodHandle#invoke} is the serializable
+     * object.
+     *
+     * @param cl the Serializable class
+     * @return  a direct MethodHandle for the {@code writeReplace} method of the class or
+     *          {@code null} if the class does not have a {@code writeReplace} method
+     */
+    public final MethodHandle writeReplaceForSerialization(Class<?> cl) {
+        return getReplaceResolveForSerialization(cl, "writeReplace");
+    }
+
+    /**
+     * Returns a direct MethodHandle for the {@code writeReplace} method on
+     * a serializable class.
+     * The single argument of {@link MethodHandle#invoke} is the serializable
+     * object.
+     *
+     * @param cl the Serializable class
+     * @return  a direct MethodHandle for the {@code writeReplace} method of the class or
+     *          {@code null} if the class does not have a {@code writeReplace} method
+     */
+    private MethodHandle getReplaceResolveForSerialization(Class<?> cl,
+                                                           String methodName) {
+        if (!Serializable.class.isAssignableFrom(cl)) {
+            return null;
+        }
+
+        Class<?> defCl = cl;
+        while (defCl != null) {
+            try {
+                Method m = defCl.getDeclaredMethod(methodName);
+                if (m.getReturnType() != Object.class) {
+                    return null;
+                }
+                int mods = m.getModifiers();
+                if (Modifier.isStatic(mods) | Modifier.isAbstract(mods)) {
+                    return null;
+                } else if (Modifier.isPublic(mods) | Modifier.isProtected(mods)) {
+                    // fall through
+                } else if (Modifier.isPrivate(mods) && (cl != defCl)) {
+                    return null;
+                } else if (!packageEquals(cl, defCl)) {
+                    return null;
+                }
+                try {
+                    // Normal return
+                    m.setAccessible(true);
+                    return MethodHandles.lookup().unreflect(m);
+                } catch (IllegalAccessException ex0) {
+                    // setAccessible should prevent IAE
+                    throw new InternalError("Error", ex0);
+                }
+            } catch (NoSuchMethodException ex) {
+                defCl = defCl.getSuperclass();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if the class has a static initializer.
+     * The presence of a static initializer is used to compute the serialVersionUID.
+     * @param cl a serializable classLook
+     * @return {@code true} if the class has a static initializer,
+     *          otherwise {@code false}
+     */
+    public final boolean hasStaticInitializerForSerialization(Class<?> cl) {
+        Method m = hasStaticInitializerMethod;
+        if (m == null) {
+            try {
+                m = ObjectStreamClass.class.getDeclaredMethod("hasStaticInitializer",
+                        new Class<?>[]{Class.class});
+                m.setAccessible(true);
+                hasStaticInitializerMethod = m;
+            } catch (NoSuchMethodException ex) {
+                throw new InternalError("No such method hasStaticInitializer on "
+                        + ObjectStreamClass.class, ex);
+            }
+        }
+        try {
+            return (Boolean) m.invoke(null, cl);
+        } catch (InvocationTargetException | IllegalAccessException ex) {
+            throw new InternalError("Exception invoking hasStaticInitializer", ex);
+        }
+    }
+
+    /**
+     * Returns a new OptionalDataException with {@code eof} set to {@code true}
+     * or {@code false}.
+     * @param bool the value of {@code eof} in the created OptionalDataException
+     * @return  a new OptionalDataException
+     */
+    public final OptionalDataException newOptionalDataExceptionForSerialization(boolean bool) {
+        try {
+            Constructor<OptionalDataException> boolCtor =
+                    OptionalDataException.class.getDeclaredConstructor(Boolean.TYPE);
+            boolCtor.setAccessible(true);
+            return boolCtor.newInstance(bool);
+        } catch (NoSuchMethodException | InstantiationException|
+                IllegalAccessException|InvocationTargetException ex) {
+            throw new InternalError("unable to create OptionalDataException", ex);
+        }
+    }
+
     //--------------------------------------------------------------------------
     //
     // Internals only below this point
@@ -421,4 +692,17 @@
         }
         return langReflectAccess;
     }
+
+    /**
+     * Returns true if classes are defined in the classloader and same package, false
+     * otherwise.
+     * @param cl1 a class
+     * @param cl2 another class
+     * @returns true if the two classes are in the same classloader and package
+     */
+    private static boolean packageEquals(Class<?> cl1, Class<?> cl2) {
+        return cl1.getClassLoader() == cl2.getClassLoader() &&
+                Objects.equals(cl1.getPackage(), cl2.getPackage());
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/reflect/ReflectionFactory/ReflectionFactoryTest.java	Thu Dec 08 14:06:39 2016 -0500
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OptionalDataException;
+import java.io.Serializable;
+import java.lang.invoke.MethodHandle;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import sun.reflect.ReflectionFactory;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+import org.testng.TestNG;
+
+/*
+ * @test
+ * @bug 8137058 8164908 8168980
+ * @run testng ReflectionFactoryTest
+ * @run testng/othervm/policy=security.policy ReflectionFactoryTest
+ * @summary Basic test for the unsupported ReflectionFactory
+ */
+
+public class ReflectionFactoryTest {
+
+    // Initialized by init()
+    static ReflectionFactory factory;
+
+    @DataProvider(name = "ClassConstructors")
+    static Object[][] classConstructors() {
+        return new Object[][] {
+                {Object.class},
+                {Foo.class},
+                {Bar.class},
+        };
+    }
+
+    @BeforeClass
+    static void init() {
+        factory = ReflectionFactory.getReflectionFactory();
+    }
+
+    /**
+     * Test that the correct Constructor is selected and run.
+     * @param type type of object to create
+     * @throws NoSuchMethodException - error
+     * @throws InstantiationException - error
+     * @throws IllegalAccessException - error
+     * @throws InvocationTargetException - error
+     */
+    @Test(dataProvider="ClassConstructors")
+    static void testConstructor(Class<?> type)
+        throws NoSuchMethodException, InstantiationException,
+            IllegalAccessException, InvocationTargetException
+    {
+        @SuppressWarnings("unchecked")
+        Constructor<?> c = factory.newConstructorForSerialization(type);
+
+        Object o = c.newInstance();
+        Assert.assertEquals(o.getClass(), type, "Instance is wrong type");
+        if (o instanceof Foo) {
+            Foo foo = (Foo)o;
+            foo.check();
+        }
+    }
+
+    @DataProvider(name = "NonSerialConstructors")
+    static Object[][] constructors() throws NoSuchMethodException {
+        return new Object[][] {
+                {Foo.class, Object.class.getDeclaredConstructor()},
+                {Foo.class, Foo.class.getDeclaredConstructor()},
+                {Baz.class, Object.class.getDeclaredConstructor()},
+                {Baz.class, Foo.class.getDeclaredConstructor()},
+                {Baz.class, Baz.class.getDeclaredConstructor()}
+        };
+    }
+
+    /**
+     * Tests that the given Constructor, in the hierarchy, is run.
+     */
+    @Test(dataProvider="NonSerialConstructors")
+    static void testNonSerializableConstructor(Class<?> cl,
+                                               Constructor<?> constructorToCall)
+        throws ReflectiveOperationException
+    {
+        @SuppressWarnings("unchecked")
+        Constructor<?> c = factory.newConstructorForSerialization(cl,
+                                                                  constructorToCall);
+
+        Object o = c.newInstance();
+        Assert.assertEquals(o.getClass(), cl, "Instance is wrong type");
+
+        int expectedFoo = 0;
+        int expectedBaz = 0;
+        if (constructorToCall.getName().equals("ReflectionFactoryTest$Foo")) {
+            expectedFoo = 1;
+        } else if (constructorToCall.getName().equals("ReflectionFactoryTest$Baz")) {
+            expectedFoo = 1;
+            expectedBaz = 4;
+        }
+
+        Assert.assertEquals(((Foo)o).foo(), expectedFoo);
+        if (o instanceof Baz) {
+            Assert.assertEquals(((Baz)o).baz(), expectedBaz);
+        }
+    }
+
+    static class Foo {
+        private int foo;
+        public Foo() {
+            this.foo = 1;
+        }
+
+        public String toString() {
+            return "foo: " + foo;
+        }
+
+        public void check() {
+            int expectedFoo = 1;
+            Assert.assertEquals(foo, expectedFoo, "foo() constructor not run");
+        }
+
+        public int foo() { return foo; }
+    }
+
+    static class Bar extends Foo implements Serializable {
+        private static final long serialVersionUID = 3L;
+
+        private int bar;
+        public Bar() {
+            this.bar = 1;
+        }
+
+        public String toString() {
+            return super.toString() + ", bar: " + bar;
+        }
+
+        public void check() {
+            super.check();
+            int expectedBar = 0;
+            Assert.assertEquals(bar, expectedBar, "bar() constructor not run");
+        }
+    }
+
+    static class Baz extends Foo {
+        private static final long serialVersionUID = 4L;
+
+        private final int baz;
+        public Baz() { this.baz = 4; }
+        public int baz() { return baz; }
+    }
+
+    /**
+     * Test newConstructorForExternalization returns the constructor and it can be called.
+     * @throws NoSuchMethodException - error
+     * @throws InstantiationException - error
+     * @throws IllegalAccessException - error
+     * @throws InvocationTargetException - error
+     */
+    @Test
+    static void newConstructorForExternalization()
+            throws NoSuchMethodException, InstantiationException,
+            IllegalAccessException, InvocationTargetException {
+        Constructor<?> cons = factory.newConstructorForExternalization(Ext.class);
+        Ext ext = (Ext)cons.newInstance();
+        Assert.assertEquals(ext.ext, 1, "Constructor not run");
+    }
+
+    static class Ext implements Externalizable {
+        private static final long serialVersionUID = 1L;
+
+        int ext;
+
+        public Ext() {
+            ext = 1;
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput out) throws IOException {}
+
+        @Override
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}
+    }
+
+    @Test
+    static void testReadWriteObjectForSerialization() throws Throwable {
+        MethodHandle readObjectMethod = factory.readObjectForSerialization(Ser.class);
+        Assert.assertNotNull(readObjectMethod, "readObjectMethod not found");
+
+        MethodHandle readObjectNoDataMethod = factory.readObjectNoDataForSerialization(Ser.class);
+        Assert.assertNotNull(readObjectNoDataMethod, "readObjectNoDataMethod not found");
+
+        MethodHandle writeObjectMethod = factory.writeObjectForSerialization(Ser.class);
+        Assert.assertNotNull(writeObjectMethod, "writeObjectMethod not found");
+
+        MethodHandle readResolveMethod = factory.readResolveForSerialization(Ser.class);
+        Assert.assertNotNull(readResolveMethod, "readResolveMethod not found");
+
+        MethodHandle writeReplaceMethod = factory.writeReplaceForSerialization(Ser.class);
+        Assert.assertNotNull(writeReplaceMethod, "writeReplaceMethod not found");
+
+        byte[] data = null;
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+            Ser ser = new Ser();
+
+            writeReplaceMethod.invoke(ser);
+            Assert.assertTrue(ser.writeReplaceCalled, "writeReplace not called");
+            Assert.assertFalse(ser.writeObjectCalled, "writeObject should not have been called");
+
+            writeObjectMethod.invoke(ser, oos);
+            Assert.assertTrue(ser.writeReplaceCalled, "writeReplace should have been called");
+            Assert.assertTrue(ser.writeObjectCalled, "writeObject not called");
+            oos.flush();
+            data = baos.toByteArray();
+        }
+
+        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
+             ObjectInputStream ois = new ObjectInputStream(bais)) {
+            Ser ser2 = new Ser();
+
+            readObjectMethod.invoke(ser2, ois);
+            Assert.assertTrue(ser2.readObjectCalled, "readObject not called");
+            Assert.assertFalse(ser2.readObjectNoDataCalled, "readObjectNoData should not be called");
+            Assert.assertFalse(ser2.readResolveCalled, "readResolve should not be called");
+
+            readObjectNoDataMethod.invoke(ser2, ois);
+            Assert.assertTrue(ser2.readObjectCalled, "readObject should have been called");
+            Assert.assertTrue(ser2.readObjectNoDataCalled, "readObjectNoData not called");
+            Assert.assertFalse(ser2.readResolveCalled, "readResolve should not be called");
+
+            readResolveMethod.invoke(ser2);
+            Assert.assertTrue(ser2.readObjectCalled, "readObject should have been called");
+            Assert.assertTrue(ser2.readObjectNoDataCalled, "readObjectNoData not called");
+            Assert.assertTrue(ser2.readResolveCalled, "readResolve not called");
+        }
+    }
+
+    @Test
+    static void hasStaticInitializer() {
+        boolean actual = factory.hasStaticInitializerForSerialization(Ser.class);
+        Assert.assertTrue(actual, "hasStaticInitializerForSerialization is wrong");
+    }
+
+    static class Ser implements Serializable {
+        private static final long serialVersionUID = 2L;
+        static {
+            // Define a static class initialization method
+        }
+
+        boolean readObjectCalled = false;
+        boolean readObjectNoDataCalled = false;
+        boolean writeObjectCalled = false;
+        boolean readResolveCalled = false;
+        boolean writeReplaceCalled = false;
+
+        public Ser() {}
+
+        private void readObject(ObjectInputStream ois) throws IOException {
+            Assert.assertFalse(writeObjectCalled, "readObject called too many times");
+            readObjectCalled = ois.readBoolean();
+        }
+
+        private void readObjectNoData(ObjectInputStream ois) throws IOException {
+            Assert.assertFalse(readObjectNoDataCalled, "readObjectNoData called too many times");
+            readObjectNoDataCalled = true;
+        }
+
+        private void writeObject(ObjectOutputStream oos) throws IOException {
+            Assert.assertFalse(writeObjectCalled, "writeObject called too many times");
+            writeObjectCalled = true;
+            oos.writeBoolean(writeObjectCalled);
+        }
+
+        private Object writeReplace() {
+            Assert.assertFalse(writeReplaceCalled, "writeReplace called too many times");
+            writeReplaceCalled = true;
+            return this;
+        }
+
+        private Object readResolve() {
+            Assert.assertFalse(readResolveCalled, "readResolve called too many times");
+            readResolveCalled = true;
+            return this;
+        }
+    }
+
+    /**
+     * Test the constructor of OptionalDataExceptions.
+     */
+    @Test
+    static void newOptionalDataException() {
+        OptionalDataException ode = factory.newOptionalDataExceptionForSerialization(true);
+        Assert.assertTrue(ode.eof, "eof wrong");
+        ode = factory.newOptionalDataExceptionForSerialization(false);
+        Assert.assertFalse(ode.eof, "eof wrong");
+
+    }
+
+
+
+    // Main can be used to run the tests from the command line with only testng.jar.
+    @SuppressWarnings("raw_types")
+    @Test(enabled = false)
+    public static void main(String[] args) {
+        Class<?>[] testclass = {ReflectionFactoryTest.class};
+        TestNG testng = new TestNG();
+        testng.setTestClasses(testclass);
+        testng.run();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/reflect/ReflectionFactory/security.policy	Thu Dec 08 14:06:39 2016 -0500
@@ -0,0 +1,11 @@
+// Individual Permissions for ReflectionFactoryTest
+grant {
+        // Permissions needed to run the test
+        permission java.util.PropertyPermission "*", "read";
+        permission java.io.FilePermission "<<ALL FILES>>", "read,write,delete,execute";
+
+        permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+        permission java.lang.RuntimePermission "accessDeclaredMembers";
+        permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
+        permission java.lang.RuntimePermission "reflectionFactoryAccess";
+};