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  * @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
  76 public 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 }