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