1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26import java.io.Serializable;
27import java.lang.reflect.InvocationTargetException;
28import java.lang.reflect.Method;
29import java.math.BigDecimal;
30import java.math.BigInteger;
31import java.net.InetAddress;
32import java.net.UnknownHostException;
33import java.rmi.Remote;
34import java.rmi.RemoteException;
35import java.time.LocalTime;
36import java.util.ArrayList;
37import java.util.Arrays;
38
39import java.util.EnumSet;
40import java.util.HashSet;
41import java.util.HashMap;
42import java.util.Objects;
43import java.util.PropertyPermission;
44import java.util.Set;
45import java.util.concurrent.atomic.LongAdder;
46
47import javax.rmi.CORBA.Util;
48import javax.rmi.PortableRemoteObject;
49
50import org.omg.CORBA_2_3.ORB;
51import org.omg.CORBA_2_3.portable.OutputStream;
52import org.omg.CORBA_2_3.portable.InputStream;
53
54import org.testng.Assert;
55import org.testng.annotations.AfterClass;
56import org.testng.annotations.BeforeClass;
57import org.testng.annotations.Test;
58import org.testng.annotations.DataProvider;
59import org.testng.TestNG;
60
61/*
62 * @test
63 * @library /test/lib
64 * @build jdk.test.lib.*
65 * @compile ObjectStreamTest.java  ObjectStreamTest$_Echo_Stub.java
66 *          ObjectStreamTest$_Server_Tie.java
67 * @modules java.base/java.io:open
68 *          java.corba/com.sun.corba.se.impl.io:+open
69 *          java.corba/com.sun.corba.se.impl.activation
70 * @summary Tests of ReflectionFactory use in IIOP Serialization
71 * @run testng/othervm ObjectStreamTest
72 * @run testng/othervm/policy=security.policy ObjectStreamTest
73 */
74
75@Test
76public class ObjectStreamTest {
77
78    enum Colors {RED, GREEN, YELLOW}
79
80    static Set<Colors> colorSet = new HashSet<>();
81
82    static {
83        colorSet.add(Colors.RED);
84        colorSet.add(Colors.GREEN);
85    }
86
87    @DataProvider(name = "Objects")
88    static Object[][] patterns() {
89        BigInteger bigInteger = new BigInteger("8943892002309239");
90        InetAddress inetAddr;
91        try {
92            inetAddr = java.net.InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
93        } catch (UnknownHostException ignored) {
94            inetAddr = null;
95            // ignored
96        }
97        HashMap<String, Object> hashMap = new HashMap<>();
98        hashMap.put("BigInteger", bigInteger);
99        hashMap.put("InetAddress", inetAddr);
100        hashMap.put("String", "bString");
101        Object[][] patterns = new Object[][]{
102                {"aString"},
103                {Integer.valueOf(5)},
104                {new SimpleObject(4, 4.0f)},
105                {Arrays.asList("a", "b", "c")},
106                {new String[]{"x", "y", "z"}},
107                {new ArrayList<Object>(1)},     // uses readObject/writeObject
108                {new StringBuffer("abc")},      // Has serialPersistentFields
109                {new StringBuilder("abc")},
110                {Colors.RED},
111                {inetAddr},
112                {LocalTime.MIDNIGHT},           // uses writeReplace/readResolve
113                {new LongAdder()},              // uses writeReplace/readResolve
114                {EnumSet.allOf(Colors.class)},  // used writeReplace/readResolve
115                {bigInteger},
116                {new BigDecimal(bigInteger)},
117                {hashMap},
118                {new PropertyPermission("abc", "read")}, // has serialPersistentFields
119        };
120        return patterns;
121    }
122
123
124    /**
125     * Check ObjectStreamClass facts match between core serialization and CORBA.
126     *
127     * @param value
128     */
129    @Test(dataProvider = "Objects")
130    void factCheck(Serializable value) {
131        Class<?> clazz = value.getClass();
132        java.io.ObjectStreamClass sOSC = java.io.ObjectStreamClass.lookup(clazz);
133        java.io.ObjectStreamField[] sFields = sOSC.getFields();
134        com.sun.corba.se.impl.io.ObjectStreamClass cOSC = corbaLookup(clazz);
135        com.sun.corba.se.impl.io.ObjectStreamField[] cFields = cOSC.getFields();
136
137        Assert.assertEquals(sFields.length, cFields.length, "Different number of fields");
138        for (int i = 0; i < sFields.length; i++) {
139            Assert.assertEquals(sFields[i].getName(), cFields[i].getName(),
140                    "different field names " + cFields[i].getName());
141            Assert.assertEquals(sFields[i].getType(), cFields[i].getType(),
142                    "different field types " + cFields[i].getName());
143            Assert.assertEquals(sFields[i].getTypeString(), cFields[i].getTypeString(),
144                    "different field typestrings " + cFields[i].getName());
145        }
146
147        Assert.assertEquals(baseMethod("hasReadObjectMethod", sOSC, (Class<?>[]) null),
148                corbaMethod("hasReadObject", cOSC, (Class<?>[]) null),
149                "hasReadObject: " + value.getClass());
150
151        Assert.assertEquals(baseMethod("hasWriteObjectMethod", sOSC, (Class<?>[]) null),
152                corbaMethod("hasWriteObject", cOSC, (Class<?>[]) null),
153                "hasWriteObject: " + value.getClass());
154
155        Assert.assertEquals(baseMethod("hasWriteReplaceMethod", sOSC, (Class<?>[]) null),
156                corbaMethod("hasWriteReplaceMethod", cOSC, (Class<?>[]) null),
157                "hasWriteReplace: " + value.getClass());
158
159        Assert.assertEquals(baseMethod("hasReadResolveMethod", sOSC, (Class<?>[]) null),
160                corbaMethod("hasReadResolveMethod", cOSC, (Class<?>[]) null),
161                "hasReadResolve: " + value.getClass());
162
163        Assert.assertEquals(baseMethod("getSerialVersionUID", sOSC, (Class<?>[]) null),
164                corbaMethod("getSerialVersionUID", cOSC, (Class<?>[]) null),
165                "getSerialVersionUID: " + value.getClass());
166
167    }
168
169
170    /**
171     * Test that objects written using Util.writeAny can be serialized
172     * and deserialized using Util.readAny to equivalent objects.
173     */
174    @Test(dataProvider = "Objects", enabled = true, dependsOnMethods = {"factCheck"})
175    void WriteValueObjectStreamTest01(Serializable value) throws Exception {
176        ORB orb = (ORB) ORB.init(new String[0], null);
177
178        OutputStream out = (OutputStream) orb.create_output_stream();
179        Util.writeAny(out, value);
180
181        InputStream in = (InputStream) out.create_input_stream();
182        Object actual = Util.readAny(in);
183
184        checkEquals(actual, value);
185    }
186
187    /**
188     * Test that objects can be echoed to a server and come back equivalent.
189     */
190    @Test(dataProvider = "Objects", enabled = true, dependsOnMethods = {"factCheck"})
191    void echoObjects(Serializable value) throws Exception {
192        Echo echo = getEchoStub();
193        Object actual = echo.echo(value);
194        checkEquals(actual, value);
195    }
196
197
198    /**
199     * Initialize the ORB and the singleton Echo server stub.
200     * @return the stub for the Echo server.
201     * @throws RemoteException if an error occurs
202     */
203    synchronized Echo getEchoStub() throws RemoteException {
204        if (echoStub == null) {
205            ORB orb = (ORB) ORB.init(new String[0], null);
206            Echo server = new Server();
207            echoStub = (javax.rmi.CORBA.Stub) PortableRemoteObject.toStub(server);
208            echoStub.connect(orb);
209        }
210        return (Echo)echoStub;
211    }
212
213    /**
214     * The stub for the Echo Server class. Initialized on first use.
215     */
216    private javax.rmi.CORBA.Stub echoStub;
217
218    /**
219     * After all the tests run shutdown the orb.
220     */
221    @AfterClass
222    void shutdownOrb() {
223        ORB orb = (ORB) ORB.init(new String[0], null);
224        orb.shutdown(true);
225    }
226
227    /**
228     * Check if the value and result are equals, with some tests depending on the type.
229     * @param expected the expected value
230     * @param actual the actual value
231     */
232    static void checkEquals(Object actual, Object expected) {
233        Class<?> cl = expected.getClass();
234        Assert.assertEquals(actual.getClass(), cl,
235                "type of value not equal to class of result");
236        try {
237            if (cl.isArray() || !(cl.getDeclaredMethod("equals", cl) == null)) {
238                Assert.assertEquals(actual, expected, "echo'd object not equal");
239            } else {
240                Assert.assertEquals(toString(actual), toString(expected),
241                        "toString values not equal");
242            }
243        } catch (NoSuchMethodException ex) {
244            Assert.assertEquals(toString(actual), toString(expected),
245                    "toString values not equal");
246        }
247    }
248
249    /**
250     * Convert an object to a String, and correctly for arrays.
251     * @param obj an object
252     * @return the tostring for the object.
253     */
254    static String toString(Object obj) {
255        return obj.getClass().isArray()
256                ? Arrays.toString((Object[]) obj)
257                : Objects.toString(obj);
258    }
259
260    /**
261     * SimpleObject to test round trip.
262     */
263    static class SimpleObject implements Serializable {
264        private static final long serialVersionUID = 5217577841494640354L;
265
266        private int i = 0;
267        private float f = 0.0f;
268
269        SimpleObject(int i, float f) {
270            this.i = i;
271            this.f = f;
272        }
273
274        @Override
275        public boolean equals(Object o) {
276            if (this == o) return true;
277            if (o == null || getClass() != o.getClass()) return false;
278
279            SimpleObject that = (SimpleObject) o;
280
281            if (i != that.i) return false;
282            return Float.compare(that.f, f) == 0;
283
284        }
285
286        @Override
287        public int hashCode() {
288            int result = i;
289            result = 31 * result + (f != +0.0f ? Float.floatToIntBits(f) : 0);
290            return result;
291        }
292
293        @Override
294        public String toString() {
295            return "SimpleObject{" +
296                    "i=" + i +
297                    ", f=" + f +
298                    '}';
299        }
300    }
301
302
303    /**
304     * Lookup the CORBA ObjectStreamClass instance for a class.
305     * @param clazz the class
306     * @return the CORBA ObjectStreamClass instance for the class
307     */
308    static com.sun.corba.se.impl.io.ObjectStreamClass corbaLookup(Class<?> clazz) {
309        Class<?> oscClass = com.sun.corba.se.impl.io.ObjectStreamClass.class;
310
311        try {
312            Method meth = oscClass.getDeclaredMethod("lookup", Class.class);
313            meth.setAccessible(true);
314            return (com.sun.corba.se.impl.io.ObjectStreamClass) meth.invoke(null, clazz);
315        } catch (NoSuchMethodException noMeth) {
316            throw new RuntimeException("missing method", noMeth);
317        } catch (IllegalAccessException | InvocationTargetException rex) {
318            throw new RuntimeException("invocation failed", rex);
319        }
320    }
321
322    /**
323     * Lookup aand invoke method on a serializable object via the CORBA ObjectStreamClass.
324     * @param methodName method name
325     * @param osc CORBA ObjectStreamClass
326     * @param argClasses method arguments
327     * @return the value returned from invoking the method
328     */
329    static Object corbaMethod(String methodName,
330                              com.sun.corba.se.impl.io.ObjectStreamClass osc,
331                              Class<?>... argClasses) {
332        Class<?> oscClass = com.sun.corba.se.impl.io.ObjectStreamClass.class;
333
334        try {
335            Method meth = oscClass.getDeclaredMethod(methodName, argClasses);
336            meth.setAccessible(true);
337            return meth.invoke(osc);
338
339        } catch (NoSuchMethodException noMeth) {
340            throw new RuntimeException("missing method" + osc.getName()
341                    + "::" + methodName, noMeth);
342        } catch (IllegalAccessException | InvocationTargetException rex) {
343            throw new RuntimeException("invocation failed", rex);
344        }
345    }
346
347
348    /**
349     * Lookup aand invoke method on a serializable object via java.io.ObjectStreamClass.
350     * @param methodName method name
351     * @param osc java.io.ObjectStreamClass
352     * @param argClasses method arguments
353     * @return the value returned from invoking the method
354     */
355    static Object baseMethod(String methodName, java.io.ObjectStreamClass osc,
356                             Class<?>... argClasses) {
357        Class<?> oscClass = java.io.ObjectStreamClass.class;
358
359        try {
360            Method meth = oscClass.getDeclaredMethod(methodName, argClasses);
361            meth.setAccessible(true);
362            return meth.invoke(osc);
363
364        } catch (NoSuchMethodException noMeth) {
365            throw new RuntimeException("missing method: " + osc.getName()
366                    + "::" + methodName, noMeth);
367        } catch (IllegalAccessException | InvocationTargetException rex) {
368            throw new RuntimeException("invocation failed", rex);
369        }
370    }
371
372    /**
373     * Simple echo interface to check IIOP serialization/deserialization.
374     */
375    interface Echo extends Remote {
376        Object echo(Object obj) throws RemoteException;
377    }
378
379    static class Server extends PortableRemoteObject implements Echo {
380
381        public Server() throws RemoteException {
382            super();
383        }
384
385        public Object echo(Object obj) {
386            return obj;
387        }
388    }
389
390    // Main can be used to run the tests from the command line with only testng.jar.
391    @SuppressWarnings("raw_types")
392    @Test(enabled = false)
393    public static void main(String[] args) {
394        Class<?>[] testclass = {ObjectStreamTest.class};
395        TestNG testng = new TestNG();
396        testng.setTestClasses(testclass);
397        testng.run();
398    }
399
400}
401