1 /*
   2  * Copyright (c) 1998, 2018, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  *
  26  *
  27  * @author Adrian Colley
  28  * @author Laird Dornin
  29  * @author Peter Jones
  30  * @author Ann Wollrath
  31  *
  32  * The rmi library directory contains a set of simple utiltity classes
  33  * for use in rmi regression tests.
  34  *
  35  * NOTE: The JavaTest group has recommended that regression tests do
  36  * not make use of packages.
  37  */
  38 
  39 import java.io.ByteArrayOutputStream;
  40 import java.io.File;
  41 import java.io.FileInputStream;
  42 import java.io.FileOutputStream;
  43 import java.io.IOException;
  44 import java.io.PrintStream;
  45 import java.net.MalformedURLException;
  46 import java.net.ServerSocket;
  47 import java.net.URL;
  48 import java.rmi.NoSuchObjectException;
  49 import java.rmi.Remote;
  50 import java.rmi.RemoteException;
  51 import java.rmi.registry.LocateRegistry;
  52 import java.rmi.registry.Registry;
  53 import java.rmi.server.RemoteRef;
  54 import java.rmi.server.UnicastRemoteObject;
  55 import java.util.Enumeration;
  56 import java.util.Properties;
  57 
  58 import sun.rmi.registry.RegistryImpl;
  59 import sun.rmi.server.UnicastServerRef;
  60 import sun.rmi.transport.Endpoint;
  61 import sun.rmi.transport.LiveRef;
  62 import sun.rmi.transport.tcp.TCPEndpoint;
  63 
  64 /**
  65  * Class of utility/library methods (i.e. procedures) that assist with
  66  * the writing and maintainance of rmi regression tests.
  67  */
  68 public class TestLibrary {
  69     /**
  70      *                       IMPORTANT!
  71      *
  72      * RMI tests are run concurrently and port conflicts result when a single
  73      * port number is used by multiple tests.  When needing a port, use
  74      * getUnusedRandomPort() wherever possible.  If getUnusedRandomPort() cannot
  75      * be used, reserve and specify a port to use for your test here.   This
  76      * will ensure there are no port conflicts amongst the RMI tests.  The
  77      * port numbers specified here may also be specified in the respective
  78      * tests.  Do not change the reserved port numbers here without also
  79      * changing the port numbers in the respective tests.
  80      *
  81      * When needing an instance of the RMIRegistry, use
  82      * createRegistryOnUnusedPort wherever possible to prevent port conflicts.
  83      *
  84      * Reserved port range: FIXED_PORT_MIN to FIXED_PORT_MAX (inclusive) for
  85      * tests which cannot use a random port.  If new fixed ports are added below
  86      * FIXED_PORT_MIN or above FIXED_PORT_MAX, then adjust
  87      * FIXED_PORT_MIN/MAX appropriately.
  88      */
  89     public final static int FIXED_PORT_MIN = 60001;
  90     public final static int FIXED_PORT_MAX = 60010;
  91     public final static int RMIDVIAINHERITEDCHANNEL_ACTIVATION_PORT = 60001;
  92     public final static int RMIDVIAINHERITEDCHANNEL_REGISTRY_PORT = 60002;
  93     public final static int INHERITEDCHANNELNOTSERVERSOCKET_ACTIVATION_PORT = 60003;
  94     public final static int INHERITEDCHANNELNOTSERVERSOCKET_REGISTRY_PORT = 60004;
  95     public final static int READTEST_REGISTRY_PORT = 60005;
  96     private final static int MAX_SERVER_SOCKET_TRIES = 2*(FIXED_PORT_MAX-FIXED_PORT_MIN+1);
  97 
  98     static void mesg(Object mesg) {
  99         System.err.println("TEST_LIBRARY: " + mesg.toString());
 100     }
 101 
 102     /**
 103      * Routines that enable rmi tests to fail in a uniformly
 104      * informative fashion.
 105      */
 106     public static void bomb(String message, Exception e) {
 107         String testFailed = "TEST FAILED: ";
 108 
 109         if ((message == null) && (e == null)) {
 110             testFailed += " No relevant information";
 111         } else if (e == null) {
 112             testFailed += message;
 113         }
 114 
 115         System.err.println(testFailed);
 116         if (e != null) {
 117             System.err.println("Test failed with: " +
 118                                e.getMessage());
 119             e.printStackTrace(System.err);
 120         }
 121         throw new TestFailedException(testFailed, e);
 122     }
 123     public static void bomb(String message) {
 124         bomb(message, null);
 125     }
 126     public static void bomb(Exception e) {
 127         bomb(null, e);
 128     }
 129 
 130     /**
 131      * Helper method to determine if registry has started
 132      *
 133      * @param port The port number to check
 134      * @param msTimeout The amount of milliseconds to spend checking
 135      */
 136 
 137     public static boolean checkIfRegistryRunning(int port, int msTimeout) {
 138         final long POLLTIME_MS = 100L;
 139         long stopTime = computeDeadline(System.currentTimeMillis(), msTimeout);
 140         do {
 141             try {
 142                 Registry r = LocateRegistry.getRegistry(port);
 143                 String[] s = r.list();
 144                 // no exception. We're now happy that registry is running
 145                 return true;
 146             } catch (RemoteException e) {
 147                 // problem - not ready ? Try again
 148                 try {
 149                     Thread.sleep(POLLTIME_MS);
 150                 } catch (InterruptedException ie) {
 151                     // not expected
 152                 }
 153             }
 154         } while (System.currentTimeMillis() < stopTime);
 155         return false;
 156     }
 157 
 158     public static String getProperty(final String property,
 159                                      final String defaultVal) {
 160         try {
 161             return java.security.AccessController.doPrivileged(
 162                 new java.security.PrivilegedAction<String>() {
 163                     public String run() {
 164                         return System.getProperty(property, defaultVal);
 165                     }
 166                 });
 167         } catch (Exception ex) {
 168             bomb("Exception getting property " + property, ex);
 169             throw new AssertionError("this should be unreachable");
 170         }
 171     }
 172 
 173     public static double getTimeoutFactor() {
 174         String prop = getProperty("test.timeout.factor", "1.0");
 175         double timeoutFactor = 1.0;
 176 
 177         try {
 178             timeoutFactor = Double.parseDouble(prop);
 179         } catch (NumberFormatException ignore) { }
 180 
 181         return timeoutFactor;
 182     }
 183 
 184     /**
 185      * Computes a deadline from a timestamp and a timeout value.
 186      * Maximum timeout (before multipliers are applied) is one hour.
 187      */
 188     public static long computeDeadline(long timestamp, long timeout) {
 189         if (timeout < 0L) {
 190             throw new IllegalArgumentException("timeout " + timeout + "ms out of range");
 191         }
 192 
 193         return timestamp + (long)(timeout * getTimeoutFactor());
 194     }
 195 
 196     /**
 197      * Property mutators
 198      */
 199     public static void setBoolean(String property, boolean value) {
 200         setProperty(property, (new Boolean(value)).toString());
 201     }
 202     public static void setInteger(String property, int value) {
 203         setProperty(property, Integer.toString(value));
 204     }
 205     public static void setProperty(String property, String value) {
 206         final String prop = property;
 207         final String val = value;
 208         java.security.AccessController.doPrivileged(
 209             new java.security.PrivilegedAction<Void>() {
 210                 public Void run() {
 211                     System.setProperty(prop, val);
 212                     return null;
 213                 }
 214         });
 215     }
 216 
 217     /**
 218      * Routines to print out a test's properties environment.
 219      */
 220     public static void printEnvironment() {
 221         printEnvironment(System.err);
 222     }
 223     public static void printEnvironment(PrintStream out) {
 224         out.println("-------------------Test environment----------" +
 225                     "---------");
 226 
 227         for(Enumeration<?> keys = System.getProperties().keys();
 228             keys.hasMoreElements();) {
 229 
 230             String property = (String) keys.nextElement();
 231             out.println(property + " = " + getProperty(property, null));
 232         }
 233         out.println("---------------------------------------------" +
 234                     "---------");
 235     }
 236 
 237     /**
 238      * Routine that "works-around" a limitation in jtreg.
 239      * Currently it is not possible for a test to specify that the
 240      * test harness should build a given source file and install the
 241      * resulting class in a location that is not accessible from the
 242      * test's classpath.  This method enables a test to move a
 243      * compiled test class file from the test's class directory into a
 244      * given "codebase" directory.  As a result the test can only
 245      * access the class file for <code>className</code>if the test loads
 246      * it from a classloader (e.g. RMIClassLoader).
 247      *
 248      * Tests that use this routine must have the following permissions
 249      * granted to them:
 250      *
 251      *   getProperty user.dir
 252      *   getProperty etc.
 253      */
 254     public static URL installClassInCodebase(String className,
 255                                              String codebase)
 256         throws MalformedURLException
 257     {
 258         return installClassInCodebase(className, codebase, true);
 259     }
 260 
 261     public static URL installClassInCodebase(String className,
 262                                              String codebase,
 263                                              boolean delete)
 264         throws MalformedURLException
 265     {
 266         /*
 267          * NOTES/LIMITATIONS: The class must not be in a named package,
 268          * and the codebase must be a relative path (it's created relative
 269          * to the working directory).
 270          */
 271         String classFileName = className + ".class";
 272 
 273         /*
 274          * Specify the file to contain the class definition.  Make sure
 275          * that the codebase directory exists (underneath the working
 276          * directory).
 277          */
 278         File dstDir = (new File(getProperty("user.dir", "."), codebase));
 279 
 280         if (!dstDir.exists()) {
 281             if (!dstDir.mkdir()) {
 282                 throw new RuntimeException(
 283                     "could not create codebase directory");
 284             }
 285         }
 286         File dstFile = new File(dstDir, classFileName);
 287 
 288         /*
 289          * Obtain the URL for the codebase.
 290          */
 291         URL codebaseURL = dstDir.toURI().toURL();
 292 
 293         /*
 294          * Specify where we will copy the class definition from, if
 295          * necessary.  After the test is built, the class file can be
 296          * found in the "test.classes" directory.
 297          */
 298         File srcDir = new File(getProperty("test.classes", "."));
 299         File srcFile = new File(srcDir, classFileName);
 300 
 301         mesg(srcFile);
 302         mesg(dstFile);
 303 
 304         /*
 305          * If the class definition is not already located at the codebase,
 306          * copy it there from the test build area.
 307          */
 308         if (!dstFile.exists()) {
 309             if (!srcFile.exists()) {
 310                 throw new RuntimeException(
 311                     "could not find class file to install in codebase " +
 312                     "(try rebuilding the test): " + srcFile);
 313             }
 314 
 315             try {
 316                 copyFile(srcFile, dstFile);
 317             } catch (IOException e) {
 318                 throw new RuntimeException(
 319                     "could not install class file in codebase");
 320             }
 321 
 322             mesg("Installed class \"" + className +
 323                 "\" in codebase " + codebaseURL);
 324         }
 325 
 326         /*
 327          * After the class definition is successfully installed at the
 328          * codebase, delete it from the test's CLASSPATH, so that it will
 329          * not be found there first before the codebase is searched.
 330          */
 331         if (srcFile.exists()) {
 332             if (delete && !srcFile.delete()) {
 333                 throw new RuntimeException(
 334                     "could not delete duplicate class file in CLASSPATH");
 335             }
 336         }
 337 
 338         return codebaseURL;
 339     }
 340 
 341     public static void copyFile(File srcFile, File dstFile)
 342         throws IOException
 343     {
 344         FileInputStream src = new FileInputStream(srcFile);
 345         FileOutputStream dst = new FileOutputStream(dstFile);
 346 
 347         byte[] buf = new byte[32768];
 348         while (true) {
 349             int count = src.read(buf);
 350             if (count < 0) {
 351                 break;
 352             }
 353             dst.write(buf, 0, count);
 354         }
 355 
 356         dst.close();
 357         src.close();
 358     }
 359 
 360     /** routine to unexport an object */
 361     public static void unexport(Remote obj) {
 362         if (obj != null) {
 363             try {
 364                 mesg("unexporting object...");
 365                 UnicastRemoteObject.unexportObject(obj, true);
 366             } catch (NoSuchObjectException munch) {
 367             } catch (Exception e) {
 368                 e.getMessage();
 369                 e.printStackTrace();
 370             }
 371         }
 372     }
 373 
 374     /**
 375      * Allow test framework to control the security manager set in
 376      * each test.
 377      *
 378      * @param managerClassName The class name of the security manager
 379      *                         to be instantiated and set if no security
 380      *                         manager has already been set.
 381      */
 382     public static void suggestSecurityManager(String managerClassName) {
 383         SecurityManager manager = null;
 384 
 385         if (System.getSecurityManager() == null) {
 386             try {
 387                 if (managerClassName == null) {
 388                     managerClassName = TestParams.defaultSecurityManager;
 389                 }
 390                 manager = ((SecurityManager) Class.
 391                            forName(managerClassName).newInstance());
 392             } catch (ClassNotFoundException cnfe) {
 393                 bomb("Security manager could not be found: " +
 394                      managerClassName, cnfe);
 395             } catch (Exception e) {
 396                 bomb("Error creating security manager. ", e);
 397             }
 398 
 399             System.setSecurityManager(manager);
 400         }
 401     }
 402 
 403     /**
 404      * Creates an RMI {@link Registry} on a random, un-reserved port.
 405      *
 406      * @returns an RMI Registry, using a random port.
 407      * @throws RemoteException if there was a problem creating a Registry.
 408      */
 409     public static Registry createRegistryOnUnusedPort() throws RemoteException {
 410         return LocateRegistry.createRegistry(getUnusedRandomPort());
 411     }
 412 
 413     /**
 414      * Creates an RMI {@link Registry} on an ephemeral port.
 415      *
 416      * @returns an RMI Registry
 417      * @throws RemoteException if there was a problem creating a Registry.
 418      */
 419     public static Registry createRegistryOnEphemeralPort() throws RemoteException {
 420         return LocateRegistry.createRegistry(0);
 421     }
 422 
 423     /**
 424      * Returns the port number the RMI {@link Registry} is running on.
 425      *
 426      * @param registry the registry to find the port of.
 427      * @return the port number the registry is using.
 428      * @throws RuntimeException if there was a problem getting the port number.
 429      */
 430     public static int getRegistryPort(Registry registry) {
 431         int port = -1;
 432 
 433         try {
 434             RemoteRef remoteRef = ((RegistryImpl)registry).getRef();
 435             LiveRef liveRef = ((UnicastServerRef)remoteRef).getLiveRef();
 436             Endpoint endpoint = liveRef.getChannel().getEndpoint();
 437             TCPEndpoint tcpEndpoint = (TCPEndpoint) endpoint;
 438             port = tcpEndpoint.getPort();
 439         } catch (Exception ex) {
 440             throw new RuntimeException("Error getting registry port.", ex);
 441         }
 442 
 443         return port;
 444     }
 445 
 446     /**
 447      * Returns an unused random port number which is not a reserved port.  Will
 448      * try up to 10 times to get a random port before giving up and throwing a
 449      * RuntimeException.
 450      *
 451      * @return an unused random port number.
 452      * @throws RuntimeException if there was a problem getting a port.
 453      */
 454     public static int getUnusedRandomPort() {
 455         int numTries = 0;
 456         IOException ex = null;
 457 
 458         while (numTries++ < MAX_SERVER_SOCKET_TRIES) {
 459             int unusedRandomPort = -1;
 460             ex = null; //reset
 461 
 462             try (ServerSocket ss = new ServerSocket(0)) {
 463                 unusedRandomPort = ss.getLocalPort();
 464             } catch (IOException e) {
 465                 ex = e;
 466                 // temporarily print stack trace here until we find out why
 467                 // tests are failing.
 468                 System.err.println("TestLibrary.getUnusedRandomPort() caught "
 469                         + "exception on iteration " + numTries
 470                         + (numTries==MAX_SERVER_SOCKET_TRIES ? " (the final try)."
 471                         : "."));
 472                 ex.printStackTrace();
 473             }
 474 
 475             if (unusedRandomPort >= 0) {
 476                 if (isReservedPort(unusedRandomPort)) {
 477                     System.out.println("INFO: On try # " + numTries
 478                         + (numTries==MAX_SERVER_SOCKET_TRIES ? ", the final try, ": ",")
 479                         + " ServerSocket(0) returned the reserved port "
 480                         + unusedRandomPort
 481                         + " in TestLibrary.getUnusedRandomPort() ");
 482                 } else {
 483                     return unusedRandomPort;
 484                 }
 485             }
 486         }
 487 
 488         // If we're here, then either an exception was thrown or the port is
 489         // a reserved port.
 490         if (ex==null) {
 491             throw new RuntimeException("Error getting unused random port. The"
 492                     +" last port returned by ServerSocket(0) was a reserved port");
 493         } else {
 494             throw new RuntimeException("Error getting unused random port.", ex);
 495         }
 496     }
 497 
 498     /**
 499      * Determines if a port is one of the reserved port numbers.
 500      *
 501      * @param port the port to test.
 502      * @return {@code true} if the port is a reserved port, otherwise
 503      *         {@code false}.
 504      */
 505     public static boolean isReservedPort(int port) {
 506         return ((port >= FIXED_PORT_MIN) && (port <= FIXED_PORT_MAX) ||
 507                 (port == 1099));
 508     }
 509 
 510     /**
 511      * Method to capture the stack trace of an exception and return it
 512      * as a string.
 513      */
 514     public String stackTraceToString(Exception e) {
 515         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 516         PrintStream ps = new PrintStream(bos);
 517 
 518         e.printStackTrace(ps);
 519         return bos.toString();
 520     }
 521 
 522     /** extra properties */
 523     private static Properties props;
 524 
 525     /**
 526      * Returns extra test properties. Looks for the file "../../test.props"
 527      * and reads it in as a Properties file. Assuming the working directory
 528      * is "<path>/JTwork/scratch", this will find "<path>/test.props".
 529      */
 530     private static synchronized Properties getExtraProperties() {
 531         if (props != null) {
 532             return props;
 533         }
 534         props = new Properties();
 535         File f = new File(".." + File.separator + ".." + File.separator +
 536                           "test.props");
 537         if (!f.exists()) {
 538             return props;
 539         }
 540         try {
 541             FileInputStream in = new FileInputStream(f);
 542             try {
 543                 props.load(in);
 544             } finally {
 545                 in.close();
 546             }
 547         } catch (IOException e) {
 548             e.printStackTrace();
 549             throw new RuntimeException("extra property setup failed", e);
 550         }
 551         return props;
 552     }
 553 
 554     /**
 555      * Returns an extra test property. Looks for the file "../../test.props"
 556      * and reads it in as a Properties file. Assuming the working directory
 557      * is "<path>/JTwork/scratch", this will find "<path>/test.props".
 558      * If the property isn't found, defaultVal is returned.
 559      */
 560     public static String getExtraProperty(String property, String defaultVal) {
 561         return getExtraProperties().getProperty(property, defaultVal);
 562     }
 563 }