1 /* 2 * Copyright (c) 1998, 2016, 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 import java.io.*; 25 import java.time.LocalTime; 26 import java.rmi.*; 27 import java.rmi.activation.*; 28 import java.rmi.registry.*; 29 import java.util.concurrent.TimeoutException; 30 31 /** 32 * Utility class that creates an instance of rmid with a policy 33 * file of name <code>TestParams.defaultPolicy</code>. 34 * 35 * Activation groups should run with the same security manager as the 36 * test. 37 */ 38 public class RMID extends JavaVM { 39 40 // TODO: adjust these based on the timeout factor 41 // such as jcov.sleep.multiplier; see start(long) method. 42 // Also consider the test.timeout.factor property (a float). 43 private static final long TIMEOUT_SHUTDOWN_MS = 60_000L; 44 private static final long TIMEOUT_DESTROY_MS = 10_000L; 45 private static final long STARTTIME_MS = 15_000L; 46 private static final long POLLTIME_MS = 100L; 47 48 private static final String SYSTEM_NAME = ActivationSystem.class.getName(); 49 // "java.rmi.activation.ActivationSystem" 50 51 public static String MANAGER_OPTION="-Djava.security.manager="; 52 53 /** 54 * Test port for rmid. 55 * 56 * May initially be 0, which means that the child rmid process will choose 57 * an ephemeral port and report it back to the parent process. This field 58 * will then be set to the child rmid's ephemeral port value. 59 */ 60 private volatile int port; 61 //private final boolean ephemeralPort 62 63 /** Initial log name */ 64 protected static String log = "log"; 65 /** rmid's logfile directory; currently must be "." */ 66 protected static String LOGDIR = "."; 67 68 /** The output message from the child rmid process that directly precedes 69 * the ephemeral port number.*/ 70 public static final String EPHEMERAL_MSG = "RmidSelectorProvider-listening-On:"; 71 72 private static void mesg(Object mesg) { 73 System.err.println("RMID: " + mesg.toString()); 74 } 75 76 /** make test options and arguments */ 77 private static String makeOptions(int port, boolean debugExec, 78 boolean enableSelectorProvider) { 79 80 String options = " -Dsun.rmi.server.activation.debugExec=" + 81 debugExec; 82 // + 83 //" -Djava.compiler= "; 84 85 // if test params set, want to propagate them 86 if (!TestParams.testSrc.equals("")) { 87 options += " -Dtest.src=" + TestParams.testSrc + " "; 88 } 89 //if (!TestParams.testClasses.equals("")) { 90 // options += " -Dtest.classes=" + TestParams.testClasses + " "; 91 //} 92 options += " -Dtest.classes=" + TestParams.testClasses //; 93 + 94 " -Djava.rmi.server.logLevel=v "; 95 96 // + 97 // " -Djava.security.debug=all "; 98 99 // Set execTimeout to 60 sec (default is 30 sec) 100 // to avoid spurious timeouts on slow machines. 101 options += " -Dsun.rmi.activation.execTimeout=60000"; 102 103 if (port == 0 || enableSelectorProvider) { 104 // Ephemeral port, so have the rmid child process create the 105 // server socket channel and report its port number, over stdin. 106 options += " -classpath " + TestParams.testClassPath; 107 options += " --add-exports=java.base/sun.nio.ch=ALL-UNNAMED"; 108 options += " -Djava.nio.channels.spi.SelectorProvider=RMIDSelectorProvider"; 109 options += " -Djava.nio.channels.spi.SelectorProviderPort=" + port; 110 111 // Disable redirection of System.err to /tmp 112 options += " -Dsun.rmi.server.activation.disableErrRedirect=true"; 113 } 114 115 return options; 116 } 117 118 private static String makeArgs() { 119 return makeArgs(false, 0); 120 } 121 122 private static String makeArgs(boolean includePortArg, int port) { 123 String propagateManager = null; 124 125 // rmid will run with a security manager set, but no policy 126 // file - it should not need one. 127 if (System.getSecurityManager() == null) { 128 propagateManager = MANAGER_OPTION + 129 TestParams.defaultSecurityManager; 130 } else { 131 propagateManager = MANAGER_OPTION + 132 System.getSecurityManager().getClass().getName(); 133 } 134 135 // getAbsolutePath requires permission to read user.dir 136 String args = 137 " -log " + (new File(LOGDIR, log)).getAbsolutePath(); 138 139 // 0 = ephemeral port, do not include an explicit port number 140 if (includePortArg && port != 0) { 141 args += " -port " + port; 142 } 143 144 // + 145 // " -C-Djava.compiler= "; 146 147 // if test params set, want to propagate them 148 if (!TestParams.testSrc.equals("")) { 149 args += " -C-Dtest.src=" + TestParams.testSrc; 150 } 151 if (!TestParams.testClasses.equals("")) { 152 args += " -C-Dtest.classes=" + TestParams.testClasses; 153 } 154 155 if (!TestParams.testJavaOpts.equals("")) { 156 for (String a : TestParams.testJavaOpts.split(" +")) { 157 args += " -C" + a; 158 } 159 } 160 161 if (!TestParams.testVmOpts.equals("")) { 162 for (String a : TestParams.testVmOpts.split(" +")) { 163 args += " -C" + a; 164 } 165 } 166 167 args += " -C-Djava.rmi.server.useCodebaseOnly=false "; 168 169 args += " " + getCodeCoverageArgs(); 170 return args; 171 } 172 173 /** 174 * Routine that creates an rmid that will run with or without a 175 * policy file. 176 */ 177 public static RMID createRMID() { 178 return createRMID(System.out, System.err, true, true, 179 TestLibrary.getUnusedRandomPort()); 180 } 181 182 public static RMID createRMID(OutputStream out, OutputStream err, 183 boolean debugExec) 184 { 185 return createRMID(out, err, debugExec, true, 186 TestLibrary.getUnusedRandomPort()); 187 } 188 189 public static RMID createRMID(OutputStream out, OutputStream err, 190 boolean debugExec, boolean includePortArg, 191 int port) 192 { 193 String options = makeOptions(port, debugExec, false); 194 String args = makeArgs(includePortArg, port); 195 RMID rmid = new RMID("sun.rmi.server.Activation", options, args, 196 out, err, port); 197 rmid.setPolicyFile(TestParams.defaultRmidPolicy); 198 199 return rmid; 200 } 201 202 public static RMID createRMIDOnEphemeralPort() { 203 return createRMID(System.out, System.err, true, false, 0); 204 } 205 206 public static RMID createRMIDOnEphemeralPort(OutputStream out, 207 OutputStream err, 208 boolean debugExec) 209 { 210 return createRMID(out, err, debugExec, false, 0); 211 } 212 213 214 /** 215 * Private constructor. RMID instances should be created 216 * using the static factory methods. 217 */ 218 private RMID(String classname, String options, String args, 219 OutputStream out, OutputStream err, int port) 220 { 221 super(classname, options, args, out, err); 222 this.port = port; 223 } 224 225 /** 226 * Removes rmid's log file directory. 227 */ 228 public static void removeLog() { 229 File f = new File(LOGDIR, log); 230 231 if (f.exists()) { 232 mesg("Removing rmid's old log file."); 233 String[] files = f.list(); 234 235 if (files != null) { 236 for (int i=0; i<files.length; i++) { 237 (new File(f, files[i])).delete(); 238 } 239 } 240 241 if (! f.delete()) { 242 mesg("Warning: unable to delete old log file."); 243 } 244 } 245 } 246 247 /** 248 * This method is used for adding arguments to rmid (not its VM) 249 * for passing as VM options to its child group VMs. 250 * Returns the extra command line arguments required 251 * to turn on jcov code coverage analysis for rmid child VMs. 252 */ 253 protected static String getCodeCoverageArgs() { 254 return TestLibrary.getExtraProperty("rmid.jcov.args",""); 255 } 256 257 /** 258 * Looks up the activation system in the registry on the given port, 259 * returning its stub, or null if it's not present. This method differs from 260 * ActivationGroup.getSystem() because this method looks on a specific port 261 * instead of using the java.rmi.activation.port property like 262 * ActivationGroup.getSystem() does. This method also returns null instead 263 * of throwing exceptions. 264 */ 265 public static ActivationSystem lookupSystem(int port) { 266 try { 267 return (ActivationSystem)LocateRegistry.getRegistry(port).lookup(SYSTEM_NAME); 268 } catch (RemoteException | NotBoundException ex) { 269 return null; 270 } 271 } 272 273 /** 274 * Starts rmid and waits up to the default timeout period 275 * to confirm that it's running. 276 */ 277 public void start() throws IOException { 278 start(STARTTIME_MS); 279 } 280 281 /** 282 * Starts rmid and waits up to the given timeout period 283 * to confirm that it's running. 284 */ 285 public void start(long waitTime) throws IOException { 286 287 // if rmid is already running, then the test will fail with 288 // a well recognized exception (port already in use...). 289 290 mesg("Starting rmid on port " + port + ", at " + LocalTime.now()); 291 int p = super.startAndGetPort(); 292 if (p != -1) 293 port = p; 294 mesg("Started rmid on port " + port + ", at " + LocalTime.now()); 295 296 // int slopFactor = 1; 297 // try { 298 // slopFactor = Integer.valueOf( 299 // TestLibrary.getExtraProperty("jcov.sleep.multiplier","1")); 300 // } catch (NumberFormatException ignore) {} 301 // waitTime = waitTime * slopFactor; 302 303 long startTime = System.currentTimeMillis(); 304 long deadline = TestLibrary.computeDeadline(startTime, waitTime); 305 306 while (true) { 307 try { 308 Thread.sleep(POLLTIME_MS); 309 } catch (InterruptedException ie) { 310 Thread.currentThread().interrupt(); 311 mesg("Starting rmid interrupted, giving up at " + 312 (System.currentTimeMillis() - startTime) + "ms."); 313 return; 314 } 315 316 try { 317 int status = vm.exitValue(); 318 waitFor(TIMEOUT_SHUTDOWN_MS); 319 TestLibrary.bomb("Rmid process exited with status " + status + " after " + 320 (System.currentTimeMillis() - startTime) + "ms."); 321 } catch (InterruptedException | TimeoutException e) { 322 mesg(e); 323 } catch (IllegalThreadStateException ignore) { } 324 325 // The rmid process is alive; check to see whether 326 // it responds to a remote call. 327 328 mesg("looking up activation system, at " + LocalTime.now()); 329 if (lookupSystem(port) != null) { 330 /* 331 * We need to set the java.rmi.activation.port value as the 332 * activation system will use the property to determine the 333 * port #. The activation system will use this value if set. 334 * If it isn't set, the activation system will set it to an 335 * incorrect value. 336 */ 337 System.setProperty("java.rmi.activation.port", Integer.toString(port)); 338 mesg("Started successfully after " + 339 (System.currentTimeMillis() - startTime) + "ms, at " + LocalTime.now()); 340 return; 341 } 342 343 mesg("after fail to looking up activation system, at " + LocalTime.now()); 344 if (System.currentTimeMillis() > deadline) { 345 TestLibrary.bomb("Failed to start rmid, giving up after " + 346 (System.currentTimeMillis() - startTime) + "ms.", null); 347 } 348 } 349 } 350 351 /** 352 * Destroys rmid and restarts it. Note that this does NOT clean up 353 * the log file, because it stores information about restartable 354 * and activatable objects that must be carried over to the new 355 * rmid instance. 356 */ 357 public void restart() throws IOException { 358 destroy(); 359 options = makeOptions(port, true, true); 360 args = makeArgs(); 361 362 start(600_000); 363 } 364 365 /** 366 * Ask rmid to shutdown gracefully using a remote method call. 367 * catch any errors that might occur from rmid not being present 368 * at time of shutdown invocation. If the remote call is 369 * successful, wait for the process to terminate. Return true 370 * if the process terminated, otherwise return false. 371 */ 372 private boolean shutdown() throws InterruptedException { 373 mesg("shutdown()"); 374 long startTime = System.currentTimeMillis(); 375 ActivationSystem system = lookupSystem(port); 376 if (system == null) { 377 mesg("lookupSystem() returned null after " + 378 (System.currentTimeMillis() - startTime) + "ms."); 379 return false; 380 } 381 382 try { 383 mesg("ActivationSystem.shutdown()"); 384 system.shutdown(); 385 } catch (Exception e) { 386 mesg("Caught exception from ActivationSystem.shutdown():"); 387 e.printStackTrace(); 388 } 389 390 try { 391 waitFor(TIMEOUT_SHUTDOWN_MS); 392 mesg("Shutdown successful after " + 393 (System.currentTimeMillis() - startTime) + "ms."); 394 return true; 395 } catch (TimeoutException ex) { 396 mesg("Shutdown timed out after " + 397 (System.currentTimeMillis() - startTime) + "ms:"); 398 ex.printStackTrace(); 399 return false; 400 } 401 } 402 403 /** 404 * Ask rmid to shutdown gracefully but then destroy the rmid 405 * process if it does not exit by itself. This method only works 406 * if rmid is a child process of the current VM. 407 */ 408 public void destroy() { 409 if (vm == null) { 410 throw new IllegalStateException("can't wait for RMID that isn't running"); 411 } 412 413 long startTime = System.currentTimeMillis(); 414 415 // First, attempt graceful shutdown of the activation system. 416 try { 417 if (! shutdown()) { 418 // Graceful shutdown failed, use Process.destroy(). 419 mesg("Destroying RMID process."); 420 vm.destroy(); 421 try { 422 waitFor(TIMEOUT_DESTROY_MS); 423 mesg("Destroy successful after " + 424 (System.currentTimeMillis() - startTime) + "ms."); 425 } catch (TimeoutException ex) { 426 mesg("Destroy timed out, giving up after " + 427 (System.currentTimeMillis() - startTime) + "ms:"); 428 ex.printStackTrace(); 429 } 430 } 431 } catch (InterruptedException ie) { 432 mesg("Shutdown/destroy interrupted, giving up at " + 433 (System.currentTimeMillis() - startTime) + "ms."); 434 ie.printStackTrace(); 435 Thread.currentThread().interrupt(); 436 return; 437 } 438 439 vm = null; 440 } 441 442 /** 443 * Shuts down rmid and then removes its log file. 444 */ 445 public void cleanup() { 446 destroy(); 447 RMID.removeLog(); 448 } 449 450 /** 451 * Gets the port on which this rmid is listening. 452 */ 453 public int getPort() { 454 return port; 455 } 456 }