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