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