001    /*BEGIN_COPYRIGHT_BLOCK
002     *
003     * This file is part of DrJava.  Download the current version of this project from http://www.drjava.org/
004     * or http://sourceforge.net/projects/drjava/
005     *
006     * DrJava Open Source License
007     * 
008     * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu).  All rights reserved.
009     *
010     * Developed by:   Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
011     * 
012     * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
013     * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
014     * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
015     * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
016     * 
017     *     - Redistributions of source code must retain the above copyright notice, this list of conditions and the 
018     *       following disclaimers.
019     *     - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the 
020     *       following disclaimers in the documentation and/or other materials provided with the distribution.
021     *     - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to 
022     *       endorse or promote products derived from this Software without specific prior written permission.
023     *     - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their 
024     *       names without prior written permission from the JavaPLT group.  For permission, write to javaplt@rice.edu.
025     * 
026     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
027     * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN_EQ NO EVENT SHALL THE
028     * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN_EQ AN ACTION OF
029     * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT_EQ OF OR IN_EQ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
030     * WITH THE SOFTWARE.
031     * 
032    END_COPYRIGHT_BLOCK*/
033    
034    package edu.rice.cs.cunit.util;
035    
036    import java.io.File;
037    import java.io.IOException;
038    import java.util.LinkedList;
039    import java.util.Locale;
040    
041    /** A utility class to allow executing another JVM.
042     *  @version $Id: ExecJVM.java 3928 2006-08-01 19:18:36Z rcartwright $
043     */
044    public final class ExecJVM {
045      private static final String PATH_SEPARATOR = System.getProperty("path.separator");
046      private static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US);
047    
048      private ExecJVM() { }
049    
050      /** Runs a new JVM.
051       *  @param mainClass Class to run
052       *  @param classParams Parameters to pass to the main class
053       *  @param classPath Array of items to put in classpath of new JVM
054       *  @param jvmParams Array of additional command-line parameters to pass to JVM
055       *  @param workDir working directory
056       *  @return {@link Process} object corresponding to the executed JVM
057       *  @throws IOException
058       */
059      public static Process runJVM(String mainClass, String[] classParams, String[] classPath, String[] jvmParams, File workDir)
060        throws IOException {
061    
062        final StringBuilder buf = new StringBuilder();
063        for (int i = 0; i < classPath.length; i++) {
064          if (i != 0) buf.append(PATH_SEPARATOR);
065    
066          buf.append(classPath[i]);
067        }
068    
069        return runJVM(mainClass, classParams, buf.toString(), jvmParams, workDir);
070      }
071    
072      /** Runs a new JVM.
073       *  @param mainClass Class to run
074       *  @param classParams Parameters to pass to the main class
075       *  @param classPath Pre-formatted classpath parameter
076       *  @param jvmParams Array of additional command-line parameters to pass to JVM
077       *  @param workDir working directory
078       *  @return {@link Process} object corresponding to the executed JVM
079       *  @throws IOException
080       */
081      public static Process runJVM(String mainClass, String[] classParams, String classPath, String[] jvmParams, File workDir)
082        throws IOException {
083    
084        LinkedList<String> args = new LinkedList<String>();
085        args.add("-classpath");
086        args.add(edu.rice.cs.cunit.util.FileOps.convertToAbsolutePathEntries(classPath));
087        _addArray(args, jvmParams);
088        String[] jvmWithCP = args.toArray(new String[args.size()]);
089    
090        return _runJVM(mainClass, classParams, jvmWithCP, workDir);
091      }
092    
093      /** Runs a new JVM, propagating the present classpath.  It changes the entries in the class path to absolute form.
094       *  @param mainClass Class to run
095       *  @param classParams Parameters to pass to the main class
096       *  @param jvmParams Array of additional command-line parameters to pass to JVM
097       *  @param workDir working directory
098       *  @return {@link Process} object corresponding to the executed JVM
099       *  @throws IOException
100       */
101      public static Process runJVMPropagateClassPath(String mainClass, String[] classParams, String[] jvmParams, File workDir)
102        throws IOException {
103        String cp = System.getProperty("java.class.path");
104        return runJVM(mainClass, classParams, cp, jvmParams, workDir);
105      }
106    
107      /** Runs a new JVM, propagating the present classpath.
108       *  @param mainClass Class to run
109       *  @param classParams Parameters to pass to the main class
110       *  @param workDir working directory
111       *  @return {@link Process} object corresponding to the new JVM process
112       *  @throws IOException
113       */
114      public static Process runJVMPropagateClassPath(String mainClass, String[] classParams, File workDir)
115        throws IOException {
116        return runJVMPropagateClassPath(mainClass, classParams, new String[0], workDir);
117      }
118    
119      /** Creates and runs a new JVM.  This method is private now because it cannot change the classpath entries to 
120       *  absolute paths, so it should not be used.
121       *
122       *  @param mainClass Class to run
123       *  @param classParams Parameters to pass to the main class
124       *  @param jvmParams Array of additional command-line parameters to pass to JVM
125       *  @param workDir working directory
126       *
127       *  @return {@link Process} object corresponding to the executed JVM
128       *
129       *  @throws IOException
130       */
131      private static Process _runJVM(String mainClass, String[] classParams, String[] jvmParams, File workDir) throws IOException {
132        LinkedList<String> args = new LinkedList<String>();
133        args.add(_getExecutable());
134        _addArray(args, jvmParams);
135        args.add(mainClass);
136        _addArray(args, classParams);
137    
138        String[] argArray = args.toArray(new String[args.size()]);
139    
140        // exec our "java" command in the specified working directory setting
141        Process p;
142        if (workDir != null) {
143          // execute in the working directory
144          if (workDir.exists()) {
145              p = Runtime.getRuntime().exec(argArray, null, workDir);
146          }
147          else {
148            p = Runtime.getRuntime().exec(argArray);
149          }
150        }
151        else {
152          // execute without caring about working directory
153          p = Runtime.getRuntime().exec(argArray);
154        }
155        return p;
156      }
157    
158    
159      private static void _addArray(LinkedList<String> list, String[] array) {
160        if (array != null) {
161          for (int i = 0; i < array.length; i++) {
162            list.add(array[i]);
163          }
164        }
165      }
166    
167    
168        /** DOS/Windows family OS's use ; to separate paths.
169         *  @return true if DOS/Windows */
170        private static boolean _isDOS() {
171          return PATH_SEPARATOR.equals(";");
172        }
173    
174        /** @return true if Netware */
175        private static boolean _isNetware() {
176          return OS_NAME.indexOf("netware") != -1;
177        }
178    
179        /**
180         * Find the java executable.
181         * This logic comes from Ant.
182         * @return java executable string
183         */
184        private static String _getExecutable() {
185          // this netware thing is based on comments from ant's code
186          if (_isNetware()) return "java";
187    
188          File executable;
189    
190          String java_home = System.getProperty("java.home") + "/";
191    
192          String[] candidates = { java_home + "../bin/java", java_home + "bin/java", java_home + "java", };
193    
194          // search all the candidates to find java
195          for (int i = 0; i < candidates.length; i++) {
196            String current = candidates[i];
197    
198            // try javaw.exe first for dos, otherwise try java.exe for dos
199            if (_isDOS()) {
200              executable = new File(current + "w.exe");
201              if (! executable.exists())  executable = new File(current + ".exe");
202            }
203            else executable = new File(current);
204    
205            //System.err.println("checking: " + executable);
206    
207            if (executable.exists()) {
208              //System.err.println("JVM executable found: " + executable.getAbsolutePath());
209              return executable.getAbsolutePath();
210            }
211          }
212    
213          // hope for the best using the system's path!
214          //System.err.println("Could not find java executable, using 'java'!");
215          return "java";
216        }
217      }
218