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