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 
mesg(Object mesg)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      */
bomb(String message, Exception e)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     }
bomb(String message)123     public static void bomb(String message) {
124         bomb(message, null);
125     }
bomb(Exception e)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 
checkIfRegistryRunning(int port, int msTimeout)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 
getProperty(final String property, final String defaultVal)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 }
564