1 /*
   2  * Copyright (c) 1998, 2014, 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         long stopTime = System.currentTimeMillis() + msTimeout;
 139         do {
 140             try {
 141                 Registry r = LocateRegistry.getRegistry(port);
 142                 String[] s = r.list();
 143                 // no exception. We're now happy that registry is running
 144                 return true;
 145             } catch (RemoteException e) {
 146                 // problem - not ready ? Try again
 147                 try {
 148                     Thread.sleep(500);
 149                 } catch (InterruptedException ie) {
 150                     // not expected
 151                 }
 152             }
 153         } while (stopTime > System.currentTimeMillis());
 154         return false;
 155     }
 156 
 157     public static String getProperty(final String property,
 158                                      final String defaultVal) {
 159         try {
 160             return java.security.AccessController.doPrivileged(
 161                 new java.security.PrivilegedAction<String>() {
 162                     public String run() {
 163                         return System.getProperty(property, defaultVal);
 164                     }
 165                 });
 166         } catch (Exception ex) {
 167             bomb("Exception getting property " + property, ex);
 168             throw new AssertionError("this should be unreachable");
 169         }
 170     }
 171 
 172     /**
 173      * Property mutators
 174      */
 175     public static void setBoolean(String property, boolean value) {
 176         setProperty(property, (new Boolean(value)).toString());
 177     }
 178     public static void setInteger(String property, int value) {
 179         setProperty(property, Integer.toString(value));
 180     }
 181     public static void setProperty(String property, String value) {
 182         final String prop = property;
 183         final String val = value;
 184         java.security.AccessController.doPrivileged(
 185             new java.security.PrivilegedAction<Void>() {
 186                 public Void run() {
 187                     System.setProperty(prop, val);
 188                     return null;
 189                 }
 190         });
 191     }
 192 
 193     /**
 194      * Routines to print out a test's properties environment.
 195      */
 196     public static void printEnvironment() {
 197         printEnvironment(System.err);
 198     }
 199     public static void printEnvironment(PrintStream out) {
 200         out.println("-------------------Test environment----------" +
 201                     "---------");
 202 
 203         for(Enumeration<?> keys = System.getProperties().keys();
 204             keys.hasMoreElements();) {
 205 
 206             String property = (String) keys.nextElement();
 207             out.println(property + " = " + getProperty(property, null));
 208         }
 209         out.println("---------------------------------------------" +
 210                     "---------");
 211     }
 212 
 213     /**
 214      * Routine that "works-around" a limitation in jtreg.
 215      * Currently it is not possible for a test to specify that the
 216      * test harness should build a given source file and install the
 217      * resulting class in a location that is not accessible from the
 218      * test's classpath.  This method enables a test to move a
 219      * compiled test class file from the test's class directory into a
 220      * given "codebase" directory.  As a result the test can only
 221      * access the class file for <code>className</code>if the test loads
 222      * it from a classloader (e.g. RMIClassLoader).
 223      *
 224      * Tests that use this routine must have the following permissions
 225      * granted to them:
 226      *
 227      *   getProperty user.dir
 228      *   getProperty etc.
 229      */
 230     public static URL installClassInCodebase(String className,
 231                                              String codebase)
 232         throws MalformedURLException
 233     {
 234         return installClassInCodebase(className, codebase, true);
 235     }
 236 
 237     public static URL installClassInCodebase(String className,
 238                                              String codebase,
 239                                              boolean delete)
 240         throws MalformedURLException
 241     {
 242         /*
 243          * NOTES/LIMITATIONS: The class must not be in a named package,
 244          * and the codebase must be a relative path (it's created relative
 245          * to the working directory).
 246          */
 247         String classFileName = className + ".class";
 248 
 249         /*
 250          * Specify the file to contain the class definition.  Make sure
 251          * that the codebase directory exists (underneath the working
 252          * directory).
 253          */
 254         File dstDir = (new File(getProperty("user.dir", "."), codebase));
 255 
 256         if (!dstDir.exists()) {
 257             if (!dstDir.mkdir()) {
 258                 throw new RuntimeException(
 259                     "could not create codebase directory");
 260             }
 261         }
 262         File dstFile = new File(dstDir, classFileName);
 263 
 264         /*
 265          * Obtain the URL for the codebase.
 266          */
 267         URL codebaseURL = dstDir.toURI().toURL();
 268 
 269         /*
 270          * Specify where we will copy the class definition from, if
 271          * necessary.  After the test is built, the class file can be
 272          * found in the "test.classes" directory.
 273          */
 274         File srcDir = new File(getProperty("test.classes", "."));
 275         File srcFile = new File(srcDir, classFileName);
 276 
 277         mesg(srcFile);
 278         mesg(dstFile);
 279 
 280         /*
 281          * If the class definition is not already located at the codebase,
 282          * copy it there from the test build area.
 283          */
 284         if (!dstFile.exists()) {
 285             if (!srcFile.exists()) {
 286                 throw new RuntimeException(
 287                     "could not find class file to install in codebase " +
 288                     "(try rebuilding the test): " + srcFile);
 289             }
 290 
 291             try {
 292                 copyFile(srcFile, dstFile);
 293             } catch (IOException e) {
 294                 throw new RuntimeException(
 295                     "could not install class file in codebase");
 296             }
 297 
 298             mesg("Installed class \"" + className +
 299                 "\" in codebase " + codebaseURL);
 300         }
 301 
 302         /*
 303          * After the class definition is successfully installed at the
 304          * codebase, delete it from the test's CLASSPATH, so that it will
 305          * not be found there first before the codebase is searched.
 306          */
 307         if (srcFile.exists()) {
 308             if (delete && !srcFile.delete()) {
 309                 throw new RuntimeException(
 310                     "could not delete duplicate class file in CLASSPATH");
 311             }
 312         }
 313 
 314         return codebaseURL;
 315     }
 316 
 317     public static void copyFile(File srcFile, File dstFile)
 318         throws IOException
 319     {
 320         FileInputStream src = new FileInputStream(srcFile);
 321         FileOutputStream dst = new FileOutputStream(dstFile);
 322 
 323         byte[] buf = new byte[32768];
 324         while (true) {
 325             int count = src.read(buf);
 326             if (count < 0) {
 327                 break;
 328             }
 329             dst.write(buf, 0, count);
 330         }
 331 
 332         dst.close();
 333         src.close();
 334     }
 335 
 336     /** routine to unexport an object */
 337     public static void unexport(Remote obj) {
 338         if (obj != null) {
 339             try {
 340                 mesg("unexporting object...");
 341                 UnicastRemoteObject.unexportObject(obj, true);
 342             } catch (NoSuchObjectException munch) {
 343             } catch (Exception e) {
 344                 e.getMessage();
 345                 e.printStackTrace();
 346             }
 347         }
 348     }
 349 
 350     /**
 351      * Allow test framework to control the security manager set in
 352      * each test.
 353      *
 354      * @param managerClassName The class name of the security manager
 355      *                         to be instantiated and set if no security
 356      *                         manager has already been set.
 357      */
 358     public static void suggestSecurityManager(String managerClassName) {
 359         SecurityManager manager = null;
 360 
 361         if (System.getSecurityManager() == null) {
 362             try {
 363                 if (managerClassName == null) {
 364                     managerClassName = TestParams.defaultSecurityManager;
 365                 }
 366                 manager = ((SecurityManager) Class.
 367                            forName(managerClassName).newInstance());
 368             } catch (ClassNotFoundException cnfe) {
 369                 bomb("Security manager could not be found: " +
 370                      managerClassName, cnfe);
 371             } catch (Exception e) {
 372                 bomb("Error creating security manager. ", e);
 373             }
 374 
 375             System.setSecurityManager(manager);
 376         }
 377     }
 378 
 379     /**
 380      * Creates an RMI {@link Registry} on a random, un-reserved port.
 381      *
 382      * @returns an RMI Registry, using a random port.
 383      * @throws RemoteException if there was a problem creating a Registry.
 384      */
 385     public static Registry createRegistryOnUnusedPort() throws RemoteException {
 386         return LocateRegistry.createRegistry(getUnusedRandomPort());
 387     }
 388 
 389     /**
 390      * Returns the port number the RMI {@link Registry} is running on.
 391      *
 392      * @param registry the registry to find the port of.
 393      * @return the port number the registry is using.
 394      * @throws RuntimeException if there was a problem getting the port number.
 395      */
 396     public static int getRegistryPort(Registry registry) {
 397         int port = -1;
 398 
 399         try {
 400             RemoteRef remoteRef = ((RegistryImpl)registry).getRef();
 401             LiveRef liveRef = ((UnicastServerRef)remoteRef).getLiveRef();
 402             Endpoint endpoint = liveRef.getChannel().getEndpoint();
 403             TCPEndpoint tcpEndpoint = (TCPEndpoint) endpoint;
 404             port = tcpEndpoint.getPort();
 405         } catch (Exception ex) {
 406             throw new RuntimeException("Error getting registry port.", ex);
 407         }
 408 
 409         return port;
 410     }
 411 
 412     /**
 413      * Returns an unused random port number which is not a reserved port.  Will
 414      * try up to 10 times to get a random port before giving up and throwing a
 415      * RuntimeException.
 416      *
 417      * @return an unused random port number.
 418      * @throws RuntimeException if there was a problem getting a port.
 419      */
 420     public static int getUnusedRandomPort() {
 421         int numTries = 0;
 422         IOException ex = null;
 423 
 424         while (numTries++ < MAX_SERVER_SOCKET_TRIES) {
 425             int unusedRandomPort = -1;
 426             ex = null; //reset
 427 
 428             try (ServerSocket ss = new ServerSocket(0)) {
 429                 unusedRandomPort = ss.getLocalPort();
 430             } catch (IOException e) {
 431                 ex = e;
 432                 // temporarily print stack trace here until we find out why
 433                 // tests are failing.
 434                 System.err.println("TestLibrary.getUnusedRandomPort() caught "
 435                         + "exception on iteration " + numTries
 436                         + (numTries==MAX_SERVER_SOCKET_TRIES ? " (the final try)."
 437                         : "."));
 438                 ex.printStackTrace();
 439             }
 440 
 441             if (unusedRandomPort >= 0) {
 442                 if (isReservedPort(unusedRandomPort)) {
 443                     System.out.println("INFO: On try # " + numTries
 444                         + (numTries==MAX_SERVER_SOCKET_TRIES ? ", the final try, ": ",")
 445                         + " ServerSocket(0) returned the reserved port "
 446                         + unusedRandomPort
 447                         + " in TestLibrary.getUnusedRandomPort() ");
 448                 } else {
 449                     return unusedRandomPort;
 450                 }
 451             }
 452         }
 453 
 454         // If we're here, then either an exception was thrown or the port is
 455         // a reserved port.
 456         if (ex==null) {
 457             throw new RuntimeException("Error getting unused random port. The"
 458                     +" last port returned by ServerSocket(0) was a reserved port");
 459         } else {
 460             throw new RuntimeException("Error getting unused random port.", ex);
 461         }
 462     }
 463 
 464     /**
 465      * Determines if a port is one of the reserved port numbers.
 466      *
 467      * @param port the port to test.
 468      * @return {@code true} if the port is a reserved port, otherwise
 469      *         {@code false}.
 470      */
 471     public static boolean isReservedPort(int port) {
 472         return ((port >= FIXED_PORT_MIN) && (port <= FIXED_PORT_MAX) ||
 473                 (port == 1099));
 474     }
 475 
 476     /**
 477      * Method to capture the stack trace of an exception and return it
 478      * as a string.
 479      */
 480     public String stackTraceToString(Exception e) {
 481         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 482         PrintStream ps = new PrintStream(bos);
 483 
 484         e.printStackTrace(ps);
 485         return bos.toString();
 486     }
 487 
 488     /** extra properties */
 489     private static Properties props;
 490 
 491     /**
 492      * Returns extra test properties. Looks for the file "../../test.props"
 493      * and reads it in as a Properties file. Assuming the working directory
 494      * is "<path>/JTwork/scratch", this will find "<path>/test.props".
 495      */
 496     private static synchronized Properties getExtraProperties() {
 497         if (props != null) {
 498             return props;
 499         }
 500         props = new Properties();
 501         File f = new File(".." + File.separator + ".." + File.separator +
 502                           "test.props");
 503         if (!f.exists()) {
 504             return props;
 505         }
 506         try {
 507             FileInputStream in = new FileInputStream(f);
 508             try {
 509                 props.load(in);
 510             } finally {
 511                 in.close();
 512             }
 513         } catch (IOException e) {
 514             e.printStackTrace();
 515             throw new RuntimeException("extra property setup failed", e);
 516         }
 517         return props;
 518     }
 519 
 520     /**
 521      * Returns an extra test property. Looks for the file "../../test.props"
 522      * and reads it in as a Properties file. Assuming the working directory
 523      * is "<path>/JTwork/scratch", this will find "<path>/test.props".
 524      * If the property isn't found, defaultVal is returned.
 525      */
 526     public static String getExtraProperty(String property, String defaultVal) {
 527         return getExtraProperties().getProperty(property, defaultVal);
 528     }
 529 }