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