ObjectStreamTest.java revision 15894:6ce43dd8e954
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.TimeUnit;
46import java.util.concurrent.atomic.LongAdder;
47
48import javax.naming.CommunicationException;
49import javax.naming.InitialContext;
50import javax.naming.Context;
51import javax.naming.NamingException;
52import javax.rmi.CORBA.Util;
53import javax.rmi.PortableRemoteObject;
54
55import org.omg.CORBA_2_3.ORB;
56import org.omg.CORBA_2_3.portable.OutputStream;
57import org.omg.CORBA_2_3.portable.InputStream;
58
59import jdk.test.lib.JDKToolFinder;
60import jdk.test.lib.JDKToolLauncher;
61
62import org.testng.Assert;
63import org.testng.annotations.AfterSuite;
64import org.testng.annotations.Test;
65import org.testng.annotations.DataProvider;
66import org.testng.TestNG;
67
68/*
69 * @test
70 * @library /test/lib
71 * @build jdk.test.lib.*
72 * @compile  ObjectStreamTest.java  ObjectStreamTest$_Echo_Stub.java  ObjectStreamTest$_Server_Tie.java
73 * @modules java.corba/com.sun.corba.se.impl.io java.base/java.io java.corba/com.sun.corba.se.impl.activation
74 * @summary Tests of ReflectionFactory use in IIOP Serialization
75 * @run testng/othervm
76 *       -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
77 *       -Djava.naming.provider.url=iiop://localhost:1050 ObjectStreamTest
78 * @run testng/othervm/policy=security.policy
79 *       -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory
80 *       -Djava.naming.provider.url=iiop://localhost:1050 ObjectStreamTest
81 */
82
83@Test
84public class ObjectStreamTest {
85
86    enum Colors {RED, GREEN, YELLOW}
87
88    static Set<Colors> colorSet = new HashSet<>();
89
90    static {
91        colorSet.add(Colors.RED);
92        colorSet.add(Colors.GREEN);
93    }
94
95    /**
96     * The process spawned to run orbd.
97     */
98    static Process orbdProcess;
99    static Thread orbThread;
100
101    @DataProvider(name = "Objects")
102    static Object[][] patterns() {
103        BigInteger bigInteger = new BigInteger("8943892002309239");
104        InetAddress inetAddr;
105        try {
106            inetAddr = java.net.InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
107        } catch (UnknownHostException ignored) {
108            inetAddr = null;
109            // ignored
110        }
111        HashMap<String, Object> hashMap = new HashMap<>();
112        hashMap.put("BigInteger", bigInteger);
113        hashMap.put("InetAddress", inetAddr);
114        hashMap.put("String", "bString");
115        Object[][] patterns = new Object[][]{
116                {"aString"},
117                {Integer.valueOf(5)},
118                {new SimpleObject(4, 4.0f)},
119                {Arrays.asList("a", "b", "c")},
120                {new String[]{"x", "y", "z"}},
121                {new ArrayList<Object>(1)},     // uses readObject/writeObject
122                {new StringBuffer("abc")},      // Has serialPersistentFields
123                {new StringBuilder("abc")},
124                {Colors.RED},
125                {inetAddr},
126                {LocalTime.MIDNIGHT},           // uses writeReplace/readResolve
127                {new LongAdder()},              // uses writeReplace/readResolve
128                {EnumSet.allOf(Colors.class)},  // used writeReplace/readResolve
129                {bigInteger},
130                {new BigDecimal(bigInteger)},
131                {hashMap},
132                {new PropertyPermission("abc", "read")}, // has serialPersistentFields
133        };
134        return patterns;
135    }
136
137
138    /**
139     * Check ObjectStreamClass facts match between core serialization and CORBA.
140     *
141     * @param value
142     */
143    @Test(dataProvider = "Objects")
144    static void factCheck(Serializable value) {
145        Class<?> clazz = value.getClass();
146        java.io.ObjectStreamClass sOSC = java.io.ObjectStreamClass.lookup(clazz);
147        java.io.ObjectStreamField[] sFields = sOSC.getFields();
148        com.sun.corba.se.impl.io.ObjectStreamClass cOSC = corbaLookup(clazz);
149        com.sun.corba.se.impl.io.ObjectStreamField[] cFields = cOSC.getFields();
150
151        Assert.assertEquals(sFields.length, cFields.length, "Different number of fields");
152        for (int i = 0; i < sFields.length; i++) {
153            Assert.assertEquals(sFields[i].getName(), cFields[i].getName(), "different field names " + cFields[i].getName());
154            Assert.assertEquals(sFields[i].getType(), cFields[i].getType(), "different field types " + cFields[i].getName());
155            Assert.assertEquals(sFields[i].getTypeString(), cFields[i].getTypeString(), "different field typestrings " + cFields[i].getName());
156        }
157
158        Assert.assertEquals(baseMethod("hasReadObjectMethod", sOSC, (Class<?>[]) null),
159                corbaMethod("hasReadObject", cOSC, (Class<?>[]) null), "hasReadObject: " + value.getClass());
160
161        Assert.assertEquals(baseMethod("hasWriteObjectMethod", sOSC, (Class<?>[]) null),
162                corbaMethod("hasWriteObject", cOSC, (Class<?>[]) null), "hasWriteObject: " + value.getClass());
163
164        Assert.assertEquals(baseMethod("hasWriteReplaceMethod", sOSC, (Class<?>[]) null),
165                corbaMethod("hasWriteReplaceMethod", cOSC, (Class<?>[]) null), "hasWriteReplace: " + value.getClass());
166
167        Assert.assertEquals(baseMethod("hasReadResolveMethod", sOSC, (Class<?>[]) null),
168                corbaMethod("hasReadResolveMethod", cOSC, (Class<?>[]) null), "hasReadResolve: " + value.getClass());
169
170        Assert.assertEquals(baseMethod("getSerialVersionUID", sOSC, (Class<?>[]) null),
171                corbaMethod("getSerialVersionUID", cOSC, (Class<?>[]) null), "getSerialVersionUID: " + value.getClass());
172
173    }
174
175
176    /**
177     * Test that objects written using Util.writeAny can be serialized
178     * and deserialized using Util.readAny to equivalent objects.
179     */
180    @Test(dataProvider = "Objects", enabled = true, dependsOnMethods = {"factCheck"})
181    static void WriteValueObjectStreamTest01(Serializable value) throws Exception {
182        ORB orb = (ORB) ORB.init(new String[0], null);
183
184        OutputStream out = (OutputStream) orb.create_output_stream();
185        Util.writeAny(out, value);
186
187        InputStream in = (InputStream) out.create_input_stream();
188        Object actual = Util.readAny(in);
189
190        checkEquals(actual, value);
191    }
192
193    /**
194     * Test that objects can be echoed to a server and come back equivalent.
195     */
196    @Test(dataProvider = "Objects", enabled = false, dependsOnMethods = {"factCheck"})
197    static void echoObjects(Serializable value) throws Exception {
198        Context initialNamingContext = Server.init();
199        Echo echo = (Echo) PortableRemoteObject.narrow(
200                initialNamingContext.lookup(Server.serverID), Echo.class);
201        Object actual = echo.echo(value);
202        checkEquals(actual, value);
203    }
204
205    /**
206     * Check if the value and result are equals, with some tests depending on the type.
207     * @param expected the expected value
208     * @param actual the actual value
209     */
210    static void checkEquals(Object actual, Object expected) {
211        Class<?> cl = expected.getClass();
212        Assert.assertEquals(actual.getClass(), cl, "type of value not equal to class of result");
213        try {
214            if (cl.isArray() || !(cl.getDeclaredMethod("equals", cl) == null)) {
215                Assert.assertEquals(actual, expected, "echo'd object not equal");
216            } else {
217                Assert.assertEquals(toString(actual), toString(expected), "toString values not equal");
218            }
219        } catch (NoSuchMethodException ex) {
220            Assert.assertEquals(toString(actual), toString(expected), "toString values not equal");
221        }
222    }
223
224    /**
225     * Convert an object to a String, and correctly for arrays.
226     * @param obj an object
227     * @return the tostring for the object.
228     */
229    static String toString(Object obj) {
230        return obj.getClass().isArray()
231                ? Arrays.toString((Object[]) obj)
232                : Objects.toString(obj);
233    }
234
235    /**
236     * SimpleObject to test round trip.
237     */
238    static class SimpleObject implements Serializable {
239        private static final long serialVersionUID = 5217577841494640354L;
240
241        private int i = 0;
242        private float f = 0.0f;
243
244        SimpleObject(int i, float f) {
245            this.i = i;
246            this.f = f;
247        }
248
249        @Override
250        public boolean equals(Object o) {
251            if (this == o) return true;
252            if (o == null || getClass() != o.getClass()) return false;
253
254            SimpleObject that = (SimpleObject) o;
255
256            if (i != that.i) return false;
257            return Float.compare(that.f, f) == 0;
258
259        }
260
261        @Override
262        public int hashCode() {
263            int result = i;
264            result = 31 * result + (f != +0.0f ? Float.floatToIntBits(f) : 0);
265            return result;
266        }
267
268        @Override
269        public String toString() {
270            return "SimpleObject{" +
271                    "i=" + i +
272                    ", f=" + f +
273                    '}';
274        }
275    }
276
277
278    /**
279     * Lookup the CORBA ObjectStreamClass instance for a class.
280     * @param clazz the class
281     * @return the CORBA ObjectStreamClass instance for the class
282     */
283    static com.sun.corba.se.impl.io.ObjectStreamClass corbaLookup(Class<?> clazz) {
284        Class<?> oscClass = com.sun.corba.se.impl.io.ObjectStreamClass.class;
285
286        try {
287            Method meth = oscClass.getDeclaredMethod("lookup", Class.class);
288            meth.setAccessible(true);
289            return (com.sun.corba.se.impl.io.ObjectStreamClass) meth.invoke(null, clazz);
290        } catch (NoSuchMethodException noMeth) {
291            throw new RuntimeException("missing method", noMeth);
292        } catch (IllegalAccessException | InvocationTargetException rex) {
293            throw new RuntimeException("invocation failed", rex);
294        }
295    }
296
297    /**
298     * Lookup aand invoke method on a serializable object via the CORBA ObjectStreamClass.
299     * @param methodName method name
300     * @param osc CORBA ObjectStreamClass
301     * @param argClasses method arguments
302     * @return the value returned from invoking the method
303     */
304    static Object corbaMethod(String methodName, com.sun.corba.se.impl.io.ObjectStreamClass osc, Class<?>... argClasses) {
305        Class<?> oscClass = com.sun.corba.se.impl.io.ObjectStreamClass.class;
306
307        try {
308            Method meth = oscClass.getDeclaredMethod(methodName, argClasses);
309            meth.setAccessible(true);
310            return meth.invoke(osc);
311
312        } catch (NoSuchMethodException noMeth) {
313            throw new RuntimeException("missing method" + osc.getName()
314                    + "::" + methodName, noMeth);
315        } catch (IllegalAccessException | InvocationTargetException rex) {
316            throw new RuntimeException("invocation failed", rex);
317        }
318    }
319
320
321    /**
322     * Lookup aand invoke method on a serializable object via java.io.ObjectStreamClass.
323     * @param methodName method name
324     * @param osc java.io.ObjectStreamClass
325     * @param argClasses method arguments
326     * @return the value returned from invoking the method
327     */
328    static Object baseMethod(String methodName, java.io.ObjectStreamClass osc, Class<?>... argClasses) {
329        Class<?> oscClass = java.io.ObjectStreamClass.class;
330
331        try {
332            Method meth = oscClass.getDeclaredMethod(methodName, argClasses);
333            meth.setAccessible(true);
334            return meth.invoke(osc);
335
336        } catch (NoSuchMethodException noMeth) {
337            throw new RuntimeException("missing method: " + osc.getName()
338                    + "::" + methodName, noMeth);
339        } catch (IllegalAccessException | InvocationTargetException rex) {
340            throw new RuntimeException("invocation failed", rex);
341        }
342    }
343
344    /**
345     * Simple echo interface to check serialization/deserialization.
346     */
347    interface Echo extends Remote {
348        Object echo(Object obj) throws RemoteException;
349    }
350
351    static class Server extends PortableRemoteObject implements Echo {
352
353        public static final String serverID = "ObjectStreamTestServer";
354
355        private static Context initialNamingContext;
356
357        private static Server server;
358
359        public Server() throws RemoteException {
360            super();
361        }
362
363        public Object echo(Object obj) {
364            return obj;
365        }
366
367
368        public static Context init() {
369            if (initialNamingContext == null) {
370                try {
371                    startOrbd();
372                    Thread.sleep(5000L);        // Give it 5 seconds
373                } catch (Exception eex) {
374                    throw new RuntimeException("Orbd", eex);
375                }
376                for (int i = 0; i < 1; i++) {
377                    try {
378                        Thread.sleep(1L);
379                        initialNamingContext = new InitialContext();
380                        server = new Server();
381                        initialNamingContext.rebind(serverID, server);
382                    } catch (CommunicationException | InterruptedException cex) {
383                        System.out.printf("retry #%d sec: ex: %s%n", i, cex);
384                    } catch (NamingException ex) {
385                        throw new RuntimeException("can't initialize naming context", ex);
386                    } catch (RemoteException rex) {
387                        throw new RuntimeException("can't initialize server", rex);
388                    }
389                }
390            }
391            if (initialNamingContext == null) {
392                Assert.fail("Can't initialize the Orb, no naming context");
393            }
394            return initialNamingContext;
395        }
396    }
397
398    static void startOrbd() throws Exception {
399        System.out.println("\nStarting orbd with NS port 1050 ");
400        JDKToolLauncher orbdLauncher = JDKToolLauncher.create("orbd")
401                        .addToolArg("-ORBInitialHost").addToolArg("localhost")
402                        .addToolArg("-ORBInitialPort").addToolArg("1050");
403
404        System.out.println("ObjectStreamTest: Executing: " + Arrays.asList(orbdLauncher.getCommand()));
405        ProcessBuilder pb = new ProcessBuilder(orbdLauncher.getCommand());
406
407        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
408        orbdProcess = pb.start();
409    }
410
411    @AfterSuite
412    static void killOrbd() throws Exception {
413        if (orbdProcess != null) {
414            orbdProcess.destroyForcibly();
415            orbdProcess.waitFor();
416            System.out.printf("destroyed orbd, pid: %d, exitValue: %d%n",
417                    orbdProcess.getPid(), orbdProcess.exitValue());
418        }
419    }
420
421
422
423    // Main can be used to run the tests from the command line with only testng.jar.
424    @SuppressWarnings("raw_types")
425    @Test(enabled = false)
426    public static void main(String[] args) {
427        Class<?>[] testclass = {ObjectStreamTest.class};
428        TestNG testng = new TestNG();
429        testng.setTestClasses(testclass);
430        testng.run();
431    }
432
433}
434