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