1 /*
   2  * Copyright (c) 1998, 2013, 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 
  28 import java.io.*;
  29 import java.rmi.*;
  30 import java.rmi.activation.*;
  31 import java.rmi.registry.*;
  32 
  33 /**
  34  * Utility class that creates an instance of rmid with a policy
  35  * file of name <code>TestParams.defaultPolicy</code>.
  36  *
  37  * Activation groups should run with the same security manager as the
  38  * test.
  39  */
  40 public class RMID extends JavaVM {
  41 
  42     private static final String SYSTEM_NAME = ActivationSystem.class.getName();
  43         // "java.rmi.activation.ActivationSystem"
  44 
  45     public static String MANAGER_OPTION="-Djava.security.manager=";
  46 
  47     /** Test port for rmid */
  48     private final int port;
  49 
  50     /** Initial log name */
  51     protected static String log = "log";
  52     /** rmid's logfile directory; currently must be "." */
  53     protected static String LOGDIR = ".";
  54 
  55     private static void mesg(Object mesg) {
  56         System.err.println("RMID: " + mesg.toString());
  57     }
  58 
  59     /** make test options and arguments */
  60     private static String makeOptions(boolean debugExec) {
  61 
  62         String options = " -Dsun.rmi.server.activation.debugExec=" +
  63             debugExec;
  64         // +
  65         //" -Djava.compiler= ";
  66 
  67         // if test params set, want to propagate them
  68         if (!TestParams.testSrc.equals("")) {
  69             options += " -Dtest.src=" + TestParams.testSrc + " ";
  70         }
  71         //if (!TestParams.testClasses.equals("")) {
  72         //    options += " -Dtest.classes=" + TestParams.testClasses + " ";
  73         //}
  74         options += " -Dtest.classes=" + TestParams.testClasses //;
  75          +
  76          " -Djava.rmi.server.logLevel=v ";
  77 
  78         // +
  79         // " -Djava.security.debug=all ";
  80 
  81         // Set execTimeout to 60 sec (default is 30 sec)
  82         // to avoid spurious timeouts on slow machines.
  83         options += " -Dsun.rmi.activation.execTimeout=60000";
  84 
  85         return options;
  86     }
  87 
  88     private static String makeArgs(boolean includePortArg, int port) {
  89         String propagateManager = null;
  90 
  91         // rmid will run with a security manager set, but no policy
  92         // file - it should not need one.
  93         if (System.getSecurityManager() == null) {
  94             propagateManager = MANAGER_OPTION +
  95                 TestParams.defaultSecurityManager;
  96         } else {
  97             propagateManager = MANAGER_OPTION +
  98                 System.getSecurityManager().getClass().getName();
  99         }
 100 
 101         // getAbsolutePath requires permission to read user.dir
 102         String args =
 103             " -log " + (new File(LOGDIR, log)).getAbsolutePath();
 104 
 105         if (includePortArg) {
 106             args += " -port " + port;
 107         }
 108 
 109         // +
 110         //      " -C-Djava.compiler= ";
 111 
 112         // if test params set, want to propagate them
 113         if (!TestParams.testSrc.equals("")) {
 114             args += " -C-Dtest.src=" + TestParams.testSrc;
 115         }
 116         if (!TestParams.testClasses.equals("")) {
 117             args += " -C-Dtest.classes=" + TestParams.testClasses;
 118         }
 119 
 120         args += " -C-Djava.rmi.server.useCodebaseOnly=false ";
 121 
 122         args += " " + getCodeCoverageArgs();
 123         return args;
 124     }
 125 
 126     /**
 127      * Routine that creates an rmid that will run with or without a
 128      * policy file.
 129      */
 130     public static RMID createRMID() {
 131         return createRMID(System.out, System.err, true);
 132     }
 133 
 134     public static RMID createRMID(boolean debugExec) {
 135         return createRMID(System.out, System.err, debugExec);
 136     }
 137 
 138     public static RMID createRMID(OutputStream out, OutputStream err) {
 139         return createRMID(out, err, true);
 140     }
 141 
 142     public static RMID createRMID(OutputStream out, OutputStream err,
 143                                   boolean debugExec)
 144     {
 145         return createRMID(out, err, debugExec, true,
 146                           TestLibrary.getUnusedRandomPort());
 147     }
 148 
 149     public static RMID createRMID(OutputStream out, OutputStream err,
 150                                   boolean debugExec, boolean includePortArg,
 151                                   int port)
 152     {
 153         String options = makeOptions(debugExec);
 154         String args = makeArgs(includePortArg, port);
 155         RMID rmid = new RMID("sun.rmi.server.Activation", options, args,
 156                              out, err, port);
 157         rmid.setPolicyFile(TestParams.defaultRmidPolicy);
 158 
 159         return rmid;
 160     }
 161 
 162 
 163     /**
 164      * Test RMID should be created with the createRMID method.
 165      */
 166     protected RMID(String classname, String options, String args,
 167                    OutputStream out, OutputStream err, int port)
 168     {
 169         super(classname, options, args, out, err);
 170         this.port = port;
 171     }
 172 
 173     public static void removeLog() {
 174         /*
 175          * Remove previous log file directory before
 176          * starting up rmid.
 177          */
 178         File f = new File(LOGDIR, log);
 179 
 180         if (f.exists()) {
 181             mesg("removing rmid's old log file...");
 182             String[] files = f.list();
 183 
 184             if (files != null) {
 185                 for (int i=0; i<files.length; i++) {
 186                     (new File(f, files[i])).delete();
 187                 }
 188             }
 189 
 190             if (f.delete() != true) {
 191                 mesg("\t" + " unable to delete old log file.");
 192             }
 193         }
 194     }
 195 
 196     /**
 197      * This method is used for adding arguments to rmid (not its VM)
 198      * for passing as VM options to its child group VMs.
 199      * Returns the extra command line arguments required
 200      * to turn on jcov code coverage analysis for rmid child VMs.
 201      */
 202     protected static String getCodeCoverageArgs() {
 203         return TestLibrary.getExtraProperty("rmid.jcov.args","");
 204     }
 205 
 206     public void start() throws IOException {
 207         start(10000);
 208     }
 209 
 210     public void slowStart() throws IOException {
 211         start(60000);
 212     }
 213 
 214     /**
 215      * Looks up the activation system in the registry on the given port,
 216      * returning its stub, or null if it's not present. This method differs from
 217      * ActivationGroup.getSystem() because this method looks on a specific port
 218      * instead of using the java.rmi.activation.port property like
 219      * ActivationGroup.getSystem() does. This method also returns null instead
 220      * of throwing exceptions.
 221      */
 222     public static ActivationSystem lookupSystem(int port) {
 223         try {
 224             return (ActivationSystem)LocateRegistry.getRegistry(port).lookup(SYSTEM_NAME);
 225         } catch (RemoteException | NotBoundException ex) {
 226             return null;
 227         }
 228     }
 229 
 230     public void start(long waitTime) throws IOException {
 231 
 232         // if rmid is already running, then the test will fail with
 233         // a well recognized exception (port already in use...).
 234 
 235         mesg("starting rmid on port #" + port + "...");
 236         super.start();
 237 
 238         int slopFactor = 1;
 239         try {
 240             slopFactor = Integer.valueOf(
 241                 TestLibrary.getExtraProperty("jcov.sleep.multiplier","1"));
 242         } catch (NumberFormatException ignore) {}
 243         waitTime = waitTime * slopFactor;
 244 
 245         // We check several times (as many as provides passed waitTime) to
 246         // see if Rmid is currently running. Waiting steps last 100 msecs.
 247         final long rmidStartSleepTime = 100;
 248         do {
 249             // Sleeping for another rmidStartSleepTime time slice.
 250             try {
 251                 Thread.sleep(Math.min(waitTime, rmidStartSleepTime));
 252             } catch (InterruptedException ie) {
 253                 Thread.currentThread().interrupt();
 254                 mesg("Thread interrupted while checking for start of Activation System. Giving up check.");
 255                 mesg("Activation System state unknown");
 256                 return;
 257             }
 258             waitTime -= rmidStartSleepTime;
 259 
 260             // Checking if rmid is present
 261             if (lookupSystem(port) != null) {
 262                 /*
 263                  * We need to set the java.rmi.activation.port value as the
 264                  * activation system will use the property to determine the
 265                  * port #.  The activation system will use this value if set.
 266                  * If it isn't set, the activation system will set it to an
 267                  * incorrect value.
 268                  */
 269                 System.setProperty("java.rmi.activation.port", Integer.toString(port));
 270                 mesg("finished starting rmid.");
 271                 return;
 272             } else {
 273                 if (waitTime > 0) {
 274                     mesg("rmid not started, will retry for " + waitTime + "ms");
 275                 }
 276             }
 277         } while (waitTime > 0);
 278         TestLibrary.bomb("start rmid failed... giving up", null);
 279     }
 280 
 281     public void restart() throws IOException {
 282         destroy();
 283         start();
 284     }
 285 
 286     /**
 287      * Ask rmid to shutdown gracefully using a remote method call.
 288      * catch any errors that might occur from rmid not being present
 289      * at time of shutdown invocation.
 290      *
 291      * Shutdown does not nullify possible references to the rmid
 292      * process object (destroy does though).
 293      */
 294     public static void shutdown(int port) {
 295 
 296         try {
 297             ActivationSystem system = lookupSystem(port);
 298 
 299             if (system == null) {
 300                 TestLibrary.bomb("reference to the activation system was null");
 301             }
 302 
 303             system.shutdown();
 304         } catch (RemoteException re) {
 305             mesg("shutting down the activation daemon failed");
 306         } catch (Exception e) {
 307             mesg("caught exception trying to shutdown rmid");
 308             mesg(e.getMessage());
 309             e.printStackTrace();
 310         }
 311 
 312         mesg("testlibrary finished shutting down rmid");
 313     }
 314 
 315     /**
 316      * Ask rmid to shutdown gracefully but then destroy the rmid
 317      * process if it does not exit by itself.  This method only works
 318      * if rmid is a child process of the current VM.
 319      */
 320     public void destroy() {
 321         // attempt graceful shutdown of the activation system
 322         shutdown(port);
 323 
 324         if (vm != null) {
 325             try {
 326                 /* Waiting for distant RMID process to shutdown.
 327                  * Waiting is bounded at a hardcoded max of 60 secs (1 min).
 328                  * Waiting by steps of 200 msecs, thus at most 300 such attempts
 329                  * for termination of distant RMID process. If process is not
 330                  * known to be terminated properly after that time,
 331                  * we give up for a gracefull termination, and thus go for
 332                  * forcibly destroying the process.
 333                  */
 334                 boolean vmEnded = false;
 335                 int waitingTrials = 0;
 336                 final int maxTrials = 300;
 337                 final long vmProcessEndWaitInterval = 200;
 338                 int vmExitValue;
 339                 do {
 340                     try {
 341                         Thread.sleep(vmProcessEndWaitInterval);
 342                         waitingTrials++;
 343                         vmExitValue = vm.exitValue();
 344                         mesg("rmid exited on shutdown request");
 345                         vmEnded = true;
 346                     } catch (IllegalThreadStateException illegal) {
 347                         mesg("RMID's process still not terminated after more than " +
 348                              (waitingTrials * vmProcessEndWaitInterval) + " milliseconds");
 349                     }
 350                 }
 351                 while (!vmEnded &&
 352                        (waitingTrials < maxTrials));
 353 
 354                 if (waitingTrials >= maxTrials) {
 355                     mesg("RMID's process still not terminated after more than " +
 356                          (waitingTrials * vmProcessEndWaitInterval) + " milliseconds." +
 357                          "Givinp up gracefull termination...");
 358                     mesg("destroying RMID's process using Process.destroy()");
 359                     super.destroy();
 360                 }
 361 
 362             } catch (InterruptedException ie) {
 363                 Thread.currentThread().interrupt();
 364                 mesg("Thread interrupted while checking for termination of distant rmid vm. Giving up check.");
 365             } catch (Exception e) {
 366                 mesg("caught unexpected exception trying to destroy rmid: " +
 367                      e.getMessage());
 368                 e.printStackTrace();
 369             }
 370 
 371             // rmid will not restart if its process is not null
 372             vm = null;
 373         }
 374     }
 375 
 376     public int getPort() {return port;}
 377 }