/* * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * * * @author Adrian Colley * @author Laird Dornin * @author Peter Jones * @author Ann Wollrath * * The rmi library directory contains a set of simple utiltity classes * for use in rmi regression tests. * * NOTE: The JavaTest group has recommended that regression tests do * not make use of packages. */ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.URL; import java.rmi.NoSuchObjectException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RemoteRef; import java.rmi.server.UnicastRemoteObject; import java.util.Enumeration; import java.util.Properties; import sun.rmi.registry.RegistryImpl; import sun.rmi.server.UnicastServerRef; import sun.rmi.transport.Endpoint; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; /** * Class of utility/library methods (i.e. procedures) that assist with * the writing and maintainance of rmi regression tests. */ public class TestLibrary { /** * IMPORTANT! * * RMI tests are run concurrently and port conflicts result when a single * port number is used by multiple tests. When needing a port, use * getUnusedRandomPort() wherever possible. If getUnusedRandomPort() cannot * be used, reserve and specify a port to use for your test here. This * will ensure there are no port conflicts amongst the RMI tests. The * port numbers specified here may also be specified in the respective * tests. Do not change the reserved port numbers here without also * changing the port numbers in the respective tests. * * When needing an instance of the RMIRegistry, use * createRegistryOnUnusedPort wherever possible to prevent port conflicts. * * Reserved port range: FIXED_PORT_MIN to FIXED_PORT_MAX (inclusive) for * tests which cannot use a random port. If new fixed ports are added below * FIXED_PORT_MIN or above FIXED_PORT_MAX, then adjust * FIXED_PORT_MIN/MAX appropriately. */ public final static int FIXED_PORT_MIN = 64001; public final static int FIXED_PORT_MAX = 64010; public final static int RMIDVIAINHERITEDCHANNEL_ACTIVATION_PORT = 64001; public final static int RMIDVIAINHERITEDCHANNEL_REGISTRY_PORT = 64002; public final static int INHERITEDCHANNELNOTSERVERSOCKET_ACTIVATION_PORT = 64003; public final static int INHERITEDCHANNELNOTSERVERSOCKET_REGISTRY_PORT = 64004; public final static int READTEST_REGISTRY_PORT = 64005; private final static int MAX_SERVER_SOCKET_TRIES = 2*(FIXED_PORT_MAX-FIXED_PORT_MIN+1); static void mesg(Object mesg) { System.err.println("TEST_LIBRARY: " + mesg.toString()); } /** * Routines that enable rmi tests to fail in a uniformly * informative fashion. */ public static void bomb(String message, Exception e) { String testFailed = "TEST FAILED: "; if ((message == null) && (e == null)) { testFailed += " No relevant information"; } else if (e == null) { testFailed += message; } System.err.println(testFailed); if (e != null) { System.err.println("Test failed with: " + e.getMessage()); e.printStackTrace(System.err); } throw new TestFailedException(testFailed, e); } public static void bomb(String message) { bomb(message, null); } public static void bomb(Exception e) { bomb(null, e); } /** * Helper method to determine if registry has started * * @param port The port number to check * @param msTimeout The amount of milliseconds to spend checking */ public static boolean checkIfRegistryRunning(int port, int msTimeout) { long stopTime = System.currentTimeMillis() + msTimeout; // we should exercise the while loop at least once boolean oneLoopRun = false; while (stopTime > System.currentTimeMillis() || !oneLoopRun) { oneLoopRun = true; try { Registry r = LocateRegistry.getRegistry(port); String[] s = r.list(); // no exception. We're now happy that registry is running return true; } catch (RemoteException e) { // problem - not ready ? Try again try { Thread.sleep(500); } catch (InterruptedException ie) { // not expected } } } return false; } public static String getProperty(String property, String defaultVal) { final String prop = property; final String def = defaultVal; return java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public String run() { return System.getProperty(prop, def); } }); } /** * Property mutators */ public static void setBoolean(String property, boolean value) { setProperty(property, (new Boolean(value)).toString()); } public static void setInteger(String property, int value) { setProperty(property, Integer.toString(value)); } public static void setProperty(String property, String value) { final String prop = property; final String val = value; java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { System.setProperty(prop, val); return null; } }); } /** * Routines to print out a test's properties environment. */ public static void printEnvironment() { printEnvironment(System.err); } public static void printEnvironment(PrintStream out) { out.println("-------------------Test environment----------" + "---------"); for(Enumeration keys = System.getProperties().keys(); keys.hasMoreElements();) { String property = (String) keys.nextElement(); out.println(property + " = " + getProperty(property, null)); } out.println("---------------------------------------------" + "---------"); } /** * Routine that "works-around" a limitation in jtreg. * Currently it is not possible for a test to specify that the * test harness should build a given source file and install the * resulting class in a location that is not accessible from the * test's classpath. This method enables a test to move a * compiled test class file from the test's class directory into a * given "codebase" directory. As a result the test can only * access the class file for classNameif the test loads * it from a classloader (e.g. RMIClassLoader). * * Tests that use this routine must have the following permissions * granted to them: * * getProperty user.dir * getProperty etc. */ public static URL installClassInCodebase(String className, String codebase) throws MalformedURLException { return installClassInCodebase(className, codebase, true); } public static URL installClassInCodebase(String className, String codebase, boolean delete) throws MalformedURLException { /* * NOTES/LIMITATIONS: The class must not be in a named package, * and the codebase must be a relative path (it's created relative * to the working directory). */ String classFileName = className + ".class"; /* * Specify the file to contain the class definition. Make sure * that the codebase directory exists (underneath the working * directory). */ File dstDir = (new File(getProperty("user.dir", "."), codebase)); if (!dstDir.exists()) { if (!dstDir.mkdir()) { throw new RuntimeException( "could not create codebase directory"); } } File dstFile = new File(dstDir, classFileName); /* * Obtain the URL for the codebase. */ URL codebaseURL = dstDir.toURI().toURL(); /* * Specify where we will copy the class definition from, if * necessary. After the test is built, the class file can be * found in the "test.classes" directory. */ File srcDir = new File(getProperty("test.classes", ".")); File srcFile = new File(srcDir, classFileName); mesg(srcFile); mesg(dstFile); /* * If the class definition is not already located at the codebase, * copy it there from the test build area. */ if (!dstFile.exists()) { if (!srcFile.exists()) { throw new RuntimeException( "could not find class file to install in codebase " + "(try rebuilding the test): " + srcFile); } try { copyFile(srcFile, dstFile); } catch (IOException e) { throw new RuntimeException( "could not install class file in codebase"); } mesg("Installed class \"" + className + "\" in codebase " + codebaseURL); } /* * After the class definition is successfully installed at the * codebase, delete it from the test's CLASSPATH, so that it will * not be found there first before the codebase is searched. */ if (srcFile.exists()) { if (delete && !srcFile.delete()) { throw new RuntimeException( "could not delete duplicate class file in CLASSPATH"); } } return codebaseURL; } public static void copyFile(File srcFile, File dstFile) throws IOException { FileInputStream src = new FileInputStream(srcFile); FileOutputStream dst = new FileOutputStream(dstFile); byte[] buf = new byte[32768]; while (true) { int count = src.read(buf); if (count < 0) { break; } dst.write(buf, 0, count); } dst.close(); src.close(); } /** routine to unexport an object */ public static void unexport(Remote obj) { if (obj != null) { try { mesg("unexporting object..."); UnicastRemoteObject.unexportObject(obj, true); } catch (NoSuchObjectException munch) { } catch (Exception e) { e.getMessage(); e.printStackTrace(); } } } /** * Allow test framework to control the security manager set in * each test. * * @param managerClassName The class name of the security manager * to be instantiated and set if no security * manager has already been set. */ public static void suggestSecurityManager(String managerClassName) { SecurityManager manager = null; if (System.getSecurityManager() == null) { try { if (managerClassName == null) { managerClassName = TestParams.defaultSecurityManager; } manager = ((SecurityManager) Class. forName(managerClassName).newInstance()); } catch (ClassNotFoundException cnfe) { bomb("Security manager could not be found: " + managerClassName, cnfe); } catch (Exception e) { bomb("Error creating security manager. ", e); } System.setSecurityManager(manager); } } /** * Creates an RMI {@link Registry} on a random, un-reserved port. * * @returns an RMI Registry, using a random port. * @throws RemoteException if there was a problem creating a Registry. */ public static Registry createRegistryOnUnusedPort() throws RemoteException { return LocateRegistry.createRegistry(getUnusedRandomPort()); } /** * Returns the port number the RMI {@link Registry} is running on. * * @param registry the registry to find the port of. * @return the port number the registry is using. * @throws RuntimeException if there was a problem getting the port number. */ public static int getRegistryPort(Registry registry) { int port = -1; try { RemoteRef remoteRef = ((RegistryImpl)registry).getRef(); LiveRef liveRef = ((UnicastServerRef)remoteRef).getLiveRef(); Endpoint endpoint = liveRef.getChannel().getEndpoint(); TCPEndpoint tcpEndpoint = (TCPEndpoint) endpoint; port = tcpEndpoint.getPort(); } catch (Exception ex) { throw new RuntimeException("Error getting registry port.", ex); } return port; } /** * Returns an unused random port number which is not a reserved port. Will * try up to 10 times to get a random port before giving up and throwing a * RuntimeException. * * @return an unused random port number. * @throws RuntimeException if there was a problem getting a port. */ public static int getUnusedRandomPort() { int numTries = 0; IOException ex = null; while (numTries++ < MAX_SERVER_SOCKET_TRIES) { int unusedRandomPort = -1; ex = null; //reset try (ServerSocket ss = new ServerSocket(0)) { unusedRandomPort = ss.getLocalPort(); } catch (IOException e) { ex = e; // temporarily print stack trace here until we find out why // tests are failing. System.err.println("TestLibrary.getUnusedRandomPort() caught " + "exception on iteration " + numTries + (numTries==MAX_SERVER_SOCKET_TRIES ? " (the final try)." : ".")); ex.printStackTrace(); } if (unusedRandomPort >= 0) { if (isReservedPort(unusedRandomPort)) { System.out.println("INFO: On try # " + numTries + (numTries==MAX_SERVER_SOCKET_TRIES ? ", the final try, ": ",") + " ServerSocket(0) returned the reserved port " + unusedRandomPort + " in TestLibrary.getUnusedRandomPort() "); } else { return unusedRandomPort; } } } // If we're here, then either an exception was thrown or the port is // a reserved port. if (ex==null) { throw new RuntimeException("Error getting unused random port. The" +" last port returned by ServerSocket(0) was a reserved port"); } else { throw new RuntimeException("Error getting unused random port.", ex); } } /** * Determines if a port is one of the reserved port numbers. * * @param port the port to test. * @return {@code true} if the port is a reserved port, otherwise * {@code false}. */ public static boolean isReservedPort(int port) { return ((port >= FIXED_PORT_MIN) && (port <= FIXED_PORT_MAX) || (port == 1099)); } /** * Method to capture the stack trace of an exception and return it * as a string. */ public String stackTraceToString(Exception e) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(bos); e.printStackTrace(ps); return bos.toString(); } /** extra properties */ private static Properties props; /** * Returns extra test properties. Looks for the file "../../test.props" * and reads it in as a Properties file. Assuming the working directory * is "/JTwork/scratch", this will find "/test.props". */ private static synchronized Properties getExtraProperties() { if (props != null) { return props; } props = new Properties(); File f = new File(".." + File.separator + ".." + File.separator + "test.props"); if (!f.exists()) { return props; } try { FileInputStream in = new FileInputStream(f); try { props.load(in); } finally { in.close(); } } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("extra property setup failed", e); } return props; } /** * Returns an extra test property. Looks for the file "../../test.props" * and reads it in as a Properties file. Assuming the working directory * is "/JTwork/scratch", this will find "/test.props". * If the property isn't found, defaultVal is returned. */ public static String getExtraProperty(String property, String defaultVal) { return getExtraProperties().getProperty(property, defaultVal); } }