1 /*
   2  * Copyright (c) 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 import java.io.IOException;
  25 import java.io.File;
  26 import java.nio.file.Files;
  27 import java.util.Arrays;
  28 import java.util.regex.Pattern;
  29 import java.util.regex.Matcher;
  30 
  31 import jdk.testlibrary.OutputAnalyzer;
  32 import jdk.testlibrary.ProcessTools;
  33 import jdk.testlibrary.Utils;
  34 import jdk.testlibrary.ProcessThread;
  35 
  36 /*
  37  * Utility functions for test runners.
  38  * (Test runner = class that launch a test)
  39  */
  40 public class RunnerUtil {
  41 
  42     /**
  43      * The Application process must be run concurrently with our tests since
  44      * the tests will attach to the Application.
  45      * We will run the Application process in a separate thread.
  46      *
  47      * The Application must be started with flag "-Xshare:off" for the Retransform
  48      * test in TestBasics to pass on all platforms.
  49      *
  50      * The Application will write its pid and shutdownPort in the given outFile.
  51      */
  52     public static ProcessThread startApplication(String outFile, String... additionalOpts) throws Throwable {
  53         String classpath = System.getProperty("test.class.path", ".");
  54         String[] myArgs = concat(additionalOpts, new String [] { "-Dattach.test=true", "-classpath", classpath, "Application", outFile });
  55         String[] args = Utils.addTestJavaOpts(myArgs);
  56         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args);
  57         ProcessThread pt = new ProcessThread("runApplication", pb);
  58         pt.start();
  59         return pt;
  60     }
  61 
  62     public static String[] concat(String[] a, String[] b) {
  63         if (a == null) {
  64             return b;
  65         }
  66         if (b == null) {
  67             return a;
  68         }
  69         int aLen = a.length;
  70         int bLen = b.length;
  71         String[] c = new String[aLen + bLen];
  72         System.arraycopy(a, 0, c, 0, aLen);
  73         System.arraycopy(b, 0, c, aLen, bLen);
  74         return c;
  75      }
  76 
  77     /**
  78      * Will stop the running Application.
  79      * First tries to shutdown nicely by connecting to the shut down port.
  80      * If that fails, the process will be killed hard with stopProcess().
  81      *
  82      * If the nice shutdown fails, then an Exception is thrown and the test should fail.
  83      *
  84      * @param port The shut down port.
  85      * @param processThread The process to stop.
  86      */
  87     public static void stopApplication(int port, ProcessThread processThread) throws Throwable {
  88         if (processThread == null) {
  89             System.out.println("RunnerUtil.stopApplication ignored since proc is null");
  90             return;
  91         }
  92         try {
  93             System.out.println("RunnerUtil.stopApplication waiting to for shutdown");
  94             OutputAnalyzer output = ProcessTools.executeTestJvm(
  95                     "-classpath",
  96                     System.getProperty("test.class.path", "."),
  97                     "Shutdown",
  98                     Integer.toString(port));
  99             // Verify that both the Shutdown command and the Application finished ok.
 100             output.shouldHaveExitValue(0);
 101             processThread.joinAndThrow();
 102             processThread.getOutput().shouldHaveExitValue(0);
 103         } catch (Throwable t) {
 104             System.out.println("RunnerUtil.stopApplication failed. Will kill it hard: " + t);
 105             processThread.stopProcess();
 106             throw t;
 107         }
 108     }
 109 
 110     /**
 111      * Creates a jar file.
 112      * @param args Command to the jar tool.
 113      */
 114     public static void createJar(String... args) {
 115         System.out.println("Running: jar " + Arrays.toString(args));
 116         sun.tools.jar.Main jar = new sun.tools.jar.Main(System.out, System.err, "jar");
 117         if (!jar.run(args)) {
 118             throw new RuntimeException("jar failed: args=" + Arrays.toString(args));
 119         }
 120     }
 121 
 122     /**
 123      * Read process info for the running Application.
 124      * The Application writes its info to a file with this format:
 125      * shutdownPort=42994
 126      * pid=19597
 127      * done
 128      *
 129      * The final "done" is used to make sure the complete file has been written
 130      * before we try to read it.
 131      * This function will wait until the file is available.
 132      *
 133      * @param filename Path to file to read.
 134      * @return The ProcessInfo containing pid and shutdownPort.
 135      */
 136     public static ProcessInfo readProcessInfo(String filename) throws Throwable {
 137         System.out.println("Reading port and pid from file: " + filename);
 138         File file = new File(filename);
 139         String content = null;
 140 
 141         // Read file or wait for it to be created.
 142         while (true) {
 143             content = readFile(file);
 144             if (content != null && content.indexOf("done") >= 0) {
 145                 break;
 146             }
 147             Thread.sleep(100);
 148         }
 149 
 150         ProcessInfo info = new ProcessInfo();
 151         // search for a line with format: key=nnn
 152         Pattern pattern = Pattern.compile("(\\w*)=([0-9]+)\\r?\\n");
 153         Matcher matcher = pattern.matcher(content);
 154         while (matcher.find()) {
 155             String key = matcher.group(1);
 156             int value  = Integer.parseInt(matcher.group(2));
 157             if ("pid".equals(key)) {
 158                 info.pid = value;
 159             } else if ("shutdownPort".equals(key)) {
 160                 info.shutdownPort = value;
 161             }
 162         }
 163         System.out.println("processInfo.pid:" + info.pid);
 164         System.out.println("processInfo.shutdownPort:" + info.shutdownPort);
 165         return info;
 166     }
 167 
 168     /**
 169      * Read the content of a file.
 170      * @param file The file to read.
 171      * @return The file content or null if file does not exists.
 172      */
 173     public static String readFile(File file) throws IOException {
 174         if (!file.exists()) {
 175             return null;
 176         }
 177         try {
 178             byte[] bytes = Files.readAllBytes(file.toPath());
 179             String content = new String(bytes);
 180             return content;
 181         } catch (IOException e) {
 182             e.printStackTrace();
 183             throw e;
 184         }
 185     }
 186 
 187     /**
 188      * Helper class with info of the running Application.
 189      */
 190     public static class ProcessInfo {
 191         public int pid = -1;
 192         public int shutdownPort = -1;
 193     }
 194 
 195 }