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 /**
  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         if (!TestParams.testJavaOpts.equals("")) {
 121             for (String a : TestParams.testJavaOpts.split(" +")) {
 122                 args += " -C" + a;
 123             }
 124         }
 125 
 126         if (!TestParams.testVmOpts.equals("")) {
 127             for (String a : TestParams.testVmOpts.split(" +")) {
 128                 args += " -C" + a;
 129             }
 130         }
 131 
 132         args += " -C-Djava.rmi.server.useCodebaseOnly=false ";
 133 
 134         args += " " + getCodeCoverageArgs();
 135         return args;
 136     }
 137 
 138     /**
 139      * Routine that creates an rmid that will run with or without a
 140      * policy file.
 141      */
 142     public static RMID createRMID() {
 143         return createRMID(System.out, System.err, true);
 144     }
 145 
 146     public static RMID createRMID(boolean debugExec) {
 147         return createRMID(System.out, System.err, debugExec);
 148     }
 149 
 150     public static RMID createRMID(OutputStream out, OutputStream err) {
 151         return createRMID(out, err, true);
 152     }
 153 
 154     public static RMID createRMID(OutputStream out, OutputStream err,
 155                                   boolean debugExec)
 156     {
 157         return createRMID(out, err, debugExec, true,
 158                           TestLibrary.getUnusedRandomPort());
 159     }
 160 
 161     public static RMID createRMID(OutputStream out, OutputStream err,
 162                                   boolean debugExec, boolean includePortArg,
 163                                   int port)
 164     {
 165         String options = makeOptions(debugExec);
 166         String args = makeArgs(includePortArg, port);
 167         RMID rmid = new RMID("sun.rmi.server.Activation", options, args,
 168                              out, err, port);
 169         rmid.setPolicyFile(TestParams.defaultRmidPolicy);
 170 
 171         return rmid;
 172     }
 173 
 174 
 175     /**
 176      * Test RMID should be created with the createRMID method.
 177      */
 178     protected 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     public static void removeLog() {
 186         /*
 187          * Remove previous log file directory before
 188          * starting up rmid.
 189          */
 190         File f = new File(LOGDIR, log);
 191 
 192         if (f.exists()) {
 193             mesg("removing rmid's old log file...");
 194             String[] files = f.list();
 195 
 196             if (files != null) {
 197                 for (int i=0; i<files.length; i++) {
 198                     (new File(f, files[i])).delete();
 199                 }
 200             }
 201 
 202             if (f.delete() != true) {
 203                 mesg("\t" + " unable to delete old log file.");
 204             }
 205         }
 206     }
 207 
 208     /**
 209      * This method is used for adding arguments to rmid (not its VM)
 210      * for passing as VM options to its child group VMs.
 211      * Returns the extra command line arguments required
 212      * to turn on jcov code coverage analysis for rmid child VMs.
 213      */
 214     protected static String getCodeCoverageArgs() {
 215         return TestLibrary.getExtraProperty("rmid.jcov.args","");
 216     }
 217 
 218     public void start() throws IOException {
 219         start(10000);
 220     }
 221 
 222     public void slowStart() throws IOException {
 223         start(60000);
 224     }
 225 
 226     /**
 227      * Looks up the activation system in the registry on the given port,
 228      * returning its stub, or null if it's not present. This method differs from
 229      * ActivationGroup.getSystem() because this method looks on a specific port
 230      * instead of using the java.rmi.activation.port property like
 231      * ActivationGroup.getSystem() does. This method also returns null instead
 232      * of throwing exceptions.
 233      */
 234     public static ActivationSystem lookupSystem(int port) {
 235         try {
 236             return (ActivationSystem)LocateRegistry.getRegistry(port).lookup(SYSTEM_NAME);
 237         } catch (RemoteException | NotBoundException ex) {
 238             return null;
 239         }
 240     }
 241 
 242     public void start(long waitTime) throws IOException {
 243 
 244         // if rmid is already running, then the test will fail with
 245         // a well recognized exception (port already in use...).
 246 
 247         mesg("starting rmid on port #" + port + "...");
 248         super.start();
 249 
 250         int slopFactor = 1;
 251         try {
 252             slopFactor = Integer.valueOf(
 253                 TestLibrary.getExtraProperty("jcov.sleep.multiplier","1"));
 254         } catch (NumberFormatException ignore) {}
 255         waitTime = waitTime * slopFactor;
 256 
 257         // We check several times (as many as provides passed waitTime) to
 258         // see if Rmid is currently running. Waiting steps last 100 msecs.
 259         final long rmidStartSleepTime = 100;
 260         do {
 261             // Sleeping for another rmidStartSleepTime time slice.
 262             try {
 263                 Thread.sleep(Math.min(waitTime, rmidStartSleepTime));
 264             } catch (InterruptedException ie) {
 265                 Thread.currentThread().interrupt();
 266                 mesg("Thread interrupted while checking for start of Activation System. Giving up check.");
 267                 mesg("Activation System state unknown");
 268                 return;
 269             }
 270             waitTime -= rmidStartSleepTime;
 271 
 272             // Checking if rmid is present
 273             if (lookupSystem(port) != null) {
 274                 /*
 275                  * We need to set the java.rmi.activation.port value as the
 276                  * activation system will use the property to determine the
 277                  * port #.  The activation system will use this value if set.
 278                  * If it isn't set, the activation system will set it to an
 279                  * incorrect value.
 280                  */
 281                 System.setProperty("java.rmi.activation.port", Integer.toString(port));
 282                 mesg("finished starting rmid.");
 283                 return;
 284             } else {
 285                 if (waitTime > 0) {
 286                     mesg("rmid not started, will retry for " + waitTime + "ms");
 287                 }
 288             }
 289         } while (waitTime > 0);
 290         TestLibrary.bomb("start rmid failed... giving up", null);
 291     }
 292 
 293     public void restart() throws IOException {
 294         destroy();
 295         start();
 296     }
 297 
 298     /**
 299      * Ask rmid to shutdown gracefully using a remote method call.
 300      * catch any errors that might occur from rmid not being present
 301      * at time of shutdown invocation.
 302      *
 303      * Shutdown does not nullify possible references to the rmid
 304      * process object (destroy does though).
 305      */
 306     public static void shutdown(int port) {
 307 
 308         try {
 309             ActivationSystem system = lookupSystem(port);
 310 
 311             if (system == null) {
 312                 TestLibrary.bomb("reference to the activation system was null");
 313             }
 314 
 315             system.shutdown();
 316         } catch (RemoteException re) {
 317             mesg("shutting down the activation daemon failed");
 318         } catch (Exception e) {
 319             mesg("caught exception trying to shutdown rmid");
 320             mesg(e.getMessage());
 321             e.printStackTrace();
 322         }
 323 
 324         mesg("testlibrary finished shutting down rmid");
 325     }
 326 
 327     /**
 328      * Ask rmid to shutdown gracefully but then destroy the rmid
 329      * process if it does not exit by itself.  This method only works
 330      * if rmid is a child process of the current VM.
 331      */
 332     public void destroy() {
 333         // attempt graceful shutdown of the activation system
 334         shutdown(port);
 335 
 336         if (vm != null) {
 337             try {
 338                 /* Waiting for distant RMID process to shutdown.
 339                  * Waiting is bounded at a hardcoded max of 60 secs (1 min).
 340                  * Waiting by steps of 200 msecs, thus at most 300 such attempts
 341                  * for termination of distant RMID process. If process is not
 342                  * known to be terminated properly after that time,
 343                  * we give up for a gracefull termination, and thus go for
 344                  * forcibly destroying the process.
 345                  */
 346                 boolean vmEnded = false;
 347                 int waitingTrials = 0;
 348                 final int maxTrials = 300;
 349                 final long vmProcessEndWaitInterval = 200;
 350                 int vmExitValue;
 351                 do {
 352                     try {
 353                         Thread.sleep(vmProcessEndWaitInterval);
 354                         waitingTrials++;
 355                         vmExitValue = vm.exitValue();
 356                         mesg("rmid exited on shutdown request");
 357                         vmEnded = true;
 358                     } catch (IllegalThreadStateException illegal) {
 359                         mesg("RMID's process still not terminated after more than " +
 360                              (waitingTrials * vmProcessEndWaitInterval) + " milliseconds");
 361                     }
 362                 }
 363                 while (!vmEnded &&
 364                        (waitingTrials < maxTrials));
 365 
 366                 if (waitingTrials >= maxTrials) {
 367                     mesg("RMID's process still not terminated after more than " +
 368                          (waitingTrials * vmProcessEndWaitInterval) + " milliseconds." +
 369                          "Givinp up gracefull termination...");
 370                     mesg("destroying RMID's process using Process.destroy()");
 371                     super.destroy();
 372                 }
 373 
 374             } catch (InterruptedException ie) {
 375                 Thread.currentThread().interrupt();
 376                 mesg("Thread interrupted while checking for termination of distant rmid vm. Giving up check.");
 377             } catch (Exception e) {
 378                 mesg("caught unexpected exception trying to destroy rmid: " +
 379                      e.getMessage());
 380                 e.printStackTrace();
 381             }
 382 
 383             // rmid will not restart if its process is not null
 384             vm = null;
 385         }
 386     }
 387 
 388     public int getPort() {return port;}
 389 }