1 /*
   2  * Copyright (c) 1998, 2012, 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.util.Arrays;
  30 import java.util.Properties;
  31 import java.util.StringTokenizer;
  32 
  33 /**
  34  * RMI regression test utility class that uses Runtime.exec to spawn a
  35  * java process that will run a named java class.
  36  */
  37 public class JavaVM {
  38 
  39     protected Process vm = null;
  40 
  41     private String classname = "";
  42     private String args = "";
  43     private String options = "";
  44     private OutputStream outputStream = System.out;
  45     private OutputStream errorStream = System.err;
  46     private String policyFileName = null;
  47 
  48     // This is used to shorten waiting time at startup.
  49     private volatile boolean started = false;
  50     private boolean forcesOutput = true; // default behavior
  51 
  52     private static void mesg(Object mesg) {
  53         System.err.println("JAVAVM: " + mesg.toString());
  54     }
  55 
  56     /** string name of the program execd by JavaVM */
  57     private static String javaProgram = "java";
  58 
  59     static {
  60         try {
  61             javaProgram = TestLibrary.getProperty("java.home", "") +
  62                 File.separator + "bin" + File.separator + javaProgram;
  63         } catch (SecurityException se) {
  64         }
  65     }
  66 
  67     public JavaVM(String classname) {
  68         this.classname = classname;
  69     }
  70     public JavaVM(String classname,
  71                   String options, String args) {
  72         this.classname = classname;
  73         this.options = options;
  74         this.args = args;
  75     }
  76 
  77     public JavaVM(String classname,
  78                   String options, String args,
  79                   OutputStream out, OutputStream err) {
  80         this(classname, options, args);
  81         this.outputStream = out;
  82         this.errorStream = err;
  83     }
  84 
  85     /* This constructor will instantiate a JavaVM object for which caller
  86      * can ask for forcing initial version output on child vm process
  87      * (if forcesVersionOutput is true), or letting the started vm behave freely
  88      * (when forcesVersionOutput is false).
  89      */
  90     public JavaVM(String classname,
  91                   String options, String args,
  92                   OutputStream out, OutputStream err,
  93                   boolean forcesVersionOutput) {
  94         this(classname, options, args, out, err);
  95         this.forcesOutput = forcesVersionOutput;
  96     }
  97 
  98 
  99     public void setStarted() {
 100         started = true;
 101     }
 102 
 103     // Prepends passed opts array to current options
 104     public void addOptions(String[] opts) {
 105         String newOpts = "";
 106         for (int i = 0 ; i < opts.length ; i ++) {
 107             newOpts += " " + opts[i];
 108         }
 109         newOpts += " ";
 110         options = newOpts + options;
 111     }
 112 
 113     // Prepends passed arguments array to current args
 114     public void addArguments(String[] arguments) {
 115         String newArgs = "";
 116         for (int i = 0 ; i < arguments.length ; i ++) {
 117             newArgs += " " + arguments[i];
 118         }
 119         newArgs += " ";
 120         args = newArgs + args;
 121     }
 122 
 123     public void setPolicyFile(String policyFileName) {
 124         this.policyFileName = policyFileName;
 125     }
 126 
 127     /**
 128      * This method is used for setting VM options on spawned VMs.
 129      * It returns the extra command line options required
 130      * to turn on jcov code coverage analysis.
 131      */
 132     protected static String getCodeCoverageOptions() {
 133         return TestLibrary.getExtraProperty("jcov.options","");
 134     }
 135 
 136 
 137     /**
 138      * Exec the VM as specified in this object's constructor.
 139      */
 140     public void start() throws IOException {
 141 
 142         if (vm != null) return;
 143 
 144         /*
 145          * If specified, add option for policy file
 146          */
 147         if (policyFileName != null) {
 148             String option = "-Djava.security.policy=" + policyFileName;
 149             addOptions(new String[] { option });
 150         }
 151 
 152         addOptions(new String[] { getCodeCoverageOptions() });
 153 
 154         /*
 155          * If forcesOutput is true :
 156          *  We force the new starting vm to output something so that we can know
 157          *  when it is effectively started by redirecting standard output through
 158          *  the next StreamPipe call (the vm is considered started when a first
 159          *  output has been streamed out).
 160          *  We do this by prepnding a "-showversion" option in the command line.
 161          */
 162         if (forcesOutput) {
 163             addOptions(new String[] {"-showversion"});
 164         }
 165 
 166         StringTokenizer optionsTokenizer = new StringTokenizer(options);
 167         StringTokenizer argsTokenizer = new StringTokenizer(args);
 168         int optionsCount = optionsTokenizer.countTokens();
 169         int argsCount = argsTokenizer.countTokens();
 170 
 171         String javaCommand[] = new String[optionsCount + argsCount + 2];
 172         int count = 0;
 173 
 174         javaCommand[count++] = JavaVM.javaProgram;
 175         while (optionsTokenizer.hasMoreTokens()) {
 176             javaCommand[count++] = optionsTokenizer.nextToken();
 177         }
 178         javaCommand[count++] = classname;
 179         while (argsTokenizer.hasMoreTokens()) {
 180             javaCommand[count++] = argsTokenizer.nextToken();
 181         }
 182 
 183         mesg("command = " + Arrays.asList(javaCommand).toString());
 184         System.err.println("");
 185 
 186         vm = Runtime.getRuntime().exec(javaCommand);
 187 
 188         /* output from the execed process may optionally be captured. */
 189         StreamPipe.plugTogether(this, vm.getInputStream(), this.outputStream);
 190         StreamPipe.plugTogether(this, vm.getErrorStream(), this.errorStream);
 191 
 192         try {
 193             if (forcesOutput) {
 194                 // Wait distant vm to start, by using waiting time slices of 100 ms.
 195                 // Wait at most for 2secs, after it considers the vm to be started.
 196                 final long vmStartSleepTime = 100;
 197                 final int maxTrials = 20;
 198                 int numTrials = 0;
 199                 while (!started && numTrials < maxTrials) {
 200                     numTrials++;
 201                     Thread.sleep(vmStartSleepTime);
 202                 }
 203 
 204                 // Outputs running status of distant vm
 205                 String message =
 206                     "after " + (numTrials * vmStartSleepTime) + " milliseconds";
 207                 if (started) {
 208                     mesg("distant vm process running, " + message);
 209                 }
 210                 else {
 211                     mesg("unknown running status of distant vm process, " + message);
 212                 }
 213             }
 214             else {
 215                 // Since we have no way to know if the distant vm is started,
 216                 // we just consider the vm to be started after a 2secs waiting time.
 217                 Thread.sleep(2000);
 218                 mesg("distant vm considered to be started after a waiting time of 2 secs");
 219             }
 220         } catch (InterruptedException e) {
 221             Thread.currentThread().interrupt();
 222             mesg("Thread interrupted while checking if distant vm is started. Giving up check.");
 223             mesg("Distant vm state unknown");
 224             return;
 225         }
 226     }
 227 
 228     public void destroy() {
 229         if (vm != null) {
 230             vm.destroy();
 231         }
 232         vm = null;
 233     }
 234 
 235     protected Process getVM() {
 236         return vm;
 237     }
 238 }