/* * 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 * @modules jdk.unsupported */ 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 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 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(); } }