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