jdk/test/sun/tools/jmap/heapconfig/LingeredApp.java
changeset 31086 d24a2c340a47
parent 31056 07cd15548b1b
parent 31085 9bde92d55eae
child 31087 59d2a5d94dd6
equal deleted inserted replaced
31056:07cd15548b1b 31086:d24a2c340a47
     1 /*
       
     2  * Copyright (c) 2015, 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 import java.io.BufferedReader;
       
    24 import java.io.IOException;
       
    25 import java.io.InputStream;
       
    26 import java.io.InputStreamReader;
       
    27 import java.nio.file.Files;
       
    28 import java.nio.file.NoSuchFileException;
       
    29 import java.nio.file.Path;
       
    30 import java.nio.file.Paths;
       
    31 import java.nio.file.attribute.BasicFileAttributes;
       
    32 import java.nio.file.attribute.FileTime;
       
    33 import java.util.ArrayList;
       
    34 import java.util.Date;
       
    35 import java.util.List;
       
    36 import java.util.Map;
       
    37 import java.util.UUID;
       
    38 
       
    39 /**
       
    40  * This is a framework to launch an app that could be synchronized with caller
       
    41  * to make further attach actions reliable across supported platforms
       
    42 
       
    43  * Caller example:
       
    44  *   SmartTestApp a = SmartTestApp.startApp(cmd);
       
    45  *     // do something
       
    46  *   a.stopApp();
       
    47  *
       
    48  *   or fine grained control
       
    49  *
       
    50  *   a = new SmartTestApp("MyLock.lck");
       
    51  *   a.createLock();
       
    52  *   a.runApp();
       
    53  *   a.waitAppReady();
       
    54  *     // do something
       
    55  *   a.deleteLock();
       
    56  *   a.waitAppTerminate();
       
    57  *
       
    58  *  Then you can work with app output and process object
       
    59  *
       
    60  *   output = a.getAppOutput();
       
    61  *   process = a.getProcess();
       
    62  *
       
    63  */
       
    64 public class LingeredApp {
       
    65 
       
    66     private static final long spinDelay = 1000;
       
    67 
       
    68     private final String lockFileName;
       
    69     private long lockCreationTime;
       
    70     private Process appProcess;
       
    71     private final ArrayList<String> storedAppOutput;
       
    72 
       
    73     /*
       
    74      * Drain child process output, store it into string array
       
    75      */
       
    76     class InputGobbler extends Thread {
       
    77 
       
    78         InputStream is;
       
    79         List<String> astr;
       
    80 
       
    81         InputGobbler(InputStream is, List<String> astr) {
       
    82             this.is = is;
       
    83             this.astr = astr;
       
    84         }
       
    85 
       
    86         public void run() {
       
    87             try {
       
    88                 InputStreamReader isr = new InputStreamReader(is);
       
    89                 BufferedReader br = new BufferedReader(isr);
       
    90                 String line = null;
       
    91                 while ((line = br.readLine()) != null) {
       
    92                     astr.add(line);
       
    93                 }
       
    94             } catch (IOException ex) {
       
    95                 // pass
       
    96             }
       
    97         }
       
    98     }
       
    99 
       
   100     /**
       
   101      * Create LingeredApp object on caller side. Lock file have be a valid filename
       
   102      * at writable location
       
   103      *
       
   104      * @param lockFileName - the name of lock file
       
   105      */
       
   106     public LingeredApp(String lockFileName) {
       
   107         this.lockFileName = lockFileName;
       
   108         this.storedAppOutput = new ArrayList();
       
   109     }
       
   110 
       
   111     /**
       
   112      *
       
   113      * @return name of lock file
       
   114      */
       
   115     public String getLockFileName() {
       
   116         return this.lockFileName;
       
   117     }
       
   118 
       
   119     /**
       
   120      *
       
   121      * @return name of testapp
       
   122      */
       
   123     public String getAppName() {
       
   124         return this.getClass().getName();
       
   125     }
       
   126 
       
   127     /**
       
   128      *
       
   129      *  @return pid of java process running testapp
       
   130      */
       
   131     public long getPid() {
       
   132         if (appProcess == null) {
       
   133             throw new RuntimeException("Process is not alive");
       
   134         }
       
   135         return appProcess.getPid();
       
   136     }
       
   137 
       
   138     /**
       
   139      *
       
   140      * @return process object
       
   141      */
       
   142     public Process getProcess() {
       
   143         return appProcess;
       
   144     }
       
   145 
       
   146     /**
       
   147      *
       
   148      * @return application output as string array. Empty array if application produced no output
       
   149      */
       
   150     List<String> getAppOutput() {
       
   151         if (appProcess.isAlive()) {
       
   152             throw new RuntimeException("Process is still alive. Can't get its output.");
       
   153         }
       
   154         return storedAppOutput;
       
   155     }
       
   156 
       
   157     /* Make sure all part of the app use the same method to get dates,
       
   158      as different methods could produce different results
       
   159      */
       
   160     private static long epoch() {
       
   161         return new Date().getTime();
       
   162     }
       
   163 
       
   164     private static long lastModified(String fileName) throws IOException {
       
   165         Path path = Paths.get(fileName);
       
   166         BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class);
       
   167         return attr.lastModifiedTime().toMillis();
       
   168     }
       
   169 
       
   170     private static void setLastModified(String fileName, long newTime) throws IOException {
       
   171         Path path = Paths.get(fileName);
       
   172         FileTime fileTime = FileTime.fromMillis(newTime);
       
   173         Files.setLastModifiedTime(path, fileTime);
       
   174     }
       
   175 
       
   176     /**
       
   177      * create lock
       
   178      *
       
   179      * @throws IOException
       
   180      */
       
   181     public void createLock() throws IOException {
       
   182         Path path = Paths.get(lockFileName);
       
   183         // Files.deleteIfExists(path);
       
   184         Files.createFile(path);
       
   185         lockCreationTime = lastModified(lockFileName);
       
   186     }
       
   187 
       
   188     /**
       
   189      * Delete lock
       
   190      *
       
   191      * @throws IOException
       
   192      */
       
   193     public void deleteLock() throws IOException {
       
   194         try {
       
   195             Path path = Paths.get(lockFileName);
       
   196             Files.delete(path);
       
   197         } catch (NoSuchFileException ex) {
       
   198             // Lock already deleted. Ignore error
       
   199         }
       
   200     }
       
   201 
       
   202     public void waitAppTerminate() {
       
   203         while (true) {
       
   204             try {
       
   205                 appProcess.waitFor();
       
   206                 break;
       
   207             } catch (InterruptedException ex) {
       
   208                 // pass
       
   209             }
       
   210         }
       
   211     }
       
   212 
       
   213     /**
       
   214      * The app touches the lock file when it's started
       
   215      * wait while it happens. Caller have to delete lock on wait error.
       
   216      *
       
   217      * @param timeout
       
   218      * @throws java.io.IOException
       
   219      */
       
   220     public void waitAppReady(long timeout) throws IOException {
       
   221         long here = epoch();
       
   222         while (true) {
       
   223             long epoch = epoch();
       
   224             if (epoch - here > (timeout * 1000)) {
       
   225                 throw new IOException("App waiting timeout");
       
   226             }
       
   227 
       
   228             // Live process should touch lock file every second
       
   229             long lm = lastModified(lockFileName);
       
   230             if (lm > lockCreationTime) {
       
   231                 break;
       
   232             }
       
   233 
       
   234             // Make sure process didn't already exit
       
   235             if (!appProcess.isAlive()) {
       
   236                 throw new IOException("App exited unexpectedly with " + appProcess.exitValue());
       
   237             }
       
   238 
       
   239             try {
       
   240                 Thread.sleep(spinDelay);
       
   241             } catch (InterruptedException ex) {
       
   242                 // pass
       
   243             }
       
   244         }
       
   245     }
       
   246 
       
   247     /**
       
   248      * Run the app
       
   249      *
       
   250      * @param vmArguments
       
   251      * @throws IOException
       
   252      */
       
   253     public void runApp(List<String> vmArguments)
       
   254             throws IOException {
       
   255 
       
   256         // We should always use testjava or throw an exception,
       
   257         // so we can't use JDKToolFinder.getJDKTool("java");
       
   258         // that falls back to compile java on error
       
   259         String jdkPath = System.getProperty("test.jdk");
       
   260         if (jdkPath == null) {
       
   261             // we are not under jtreg, try env
       
   262             Map<String, String> env = System.getenv();
       
   263             jdkPath = env.get("TESTJAVA");
       
   264         }
       
   265 
       
   266         if (jdkPath == null) {
       
   267             throw new RuntimeException("Can't determine jdk path neither test.jdk property no TESTJAVA env are set");
       
   268         }
       
   269 
       
   270         String osname = System.getProperty("os.name");
       
   271         String javapath = jdkPath + ((osname.startsWith("window")) ? "/bin/java.exe" : "/bin/java");
       
   272 
       
   273         List<String> cmd = new ArrayList();
       
   274         cmd.add(javapath);
       
   275 
       
   276 
       
   277         if (vmArguments == null) {
       
   278             // Propagate test.vm.options to LingeredApp, filter out possible empty options
       
   279             String testVmOpts[] = System.getProperty("test.vm.opts","").split("\\s+");
       
   280             for (String s : testVmOpts) {
       
   281                 if (!s.equals("")) {
       
   282                     cmd.add(s);
       
   283                 }
       
   284             }
       
   285         }
       
   286         else{
       
   287             // Lets user manage LingerApp options
       
   288             cmd.addAll(vmArguments);
       
   289         }
       
   290 
       
   291         // Make sure we set correct classpath to run the app
       
   292         cmd.add("-cp");
       
   293         String classpath = System.getProperty("test.class.path");
       
   294         cmd.add((classpath == null) ? "." : classpath);
       
   295 
       
   296         cmd.add(this.getAppName());
       
   297         cmd.add(lockFileName);
       
   298 
       
   299         // Reporting
       
   300         StringBuilder cmdLine = new StringBuilder();
       
   301         for (String strCmd : cmd) {
       
   302             cmdLine.append("'").append(strCmd).append("' ");
       
   303         }
       
   304 
       
   305         // A bit of verbosity
       
   306         System.out.println("Command line: [" + cmdLine.toString() + "]");
       
   307 
       
   308         ProcessBuilder pb = new ProcessBuilder(cmd);
       
   309         // we don't expect any error output but make sure we are not stuck on pipe
       
   310         // pb.redirectErrorStream(false);
       
   311         pb.redirectError(ProcessBuilder.Redirect.INHERIT);
       
   312 
       
   313         appProcess = pb.start();
       
   314 
       
   315         // Create pipe reader for process, and read stdin and stderr to array of strings
       
   316         InputGobbler gb = new InputGobbler(appProcess.getInputStream(), storedAppOutput);
       
   317         gb.start();
       
   318     }
       
   319 
       
   320     /**
       
   321      *  High level interface for test writers
       
   322      */
       
   323     /**
       
   324      * Factory method that creates SmartAppTest object with ready to use application
       
   325      * lock name is autogenerated, wait timeout is hardcoded
       
   326      * @param cmd - vm options, could be null to auto add testvm.options
       
   327      * @return LingeredApp object
       
   328      * @throws IOException
       
   329      */
       
   330     public static LingeredApp startApp(List<String> cmd) throws IOException {
       
   331         final String lockName = UUID.randomUUID().toString() + ".lck";
       
   332         final int waitTime = 10;
       
   333 
       
   334         LingeredApp a = new LingeredApp(lockName);
       
   335         a.createLock();
       
   336         try {
       
   337             a.runApp(cmd);
       
   338             a.waitAppReady(waitTime);
       
   339         } catch (Exception ex) {
       
   340             a.deleteLock();
       
   341             throw ex;
       
   342         }
       
   343 
       
   344         return a;
       
   345     }
       
   346 
       
   347     public static LingeredApp startApp() throws IOException {
       
   348         return startApp(null);
       
   349     }
       
   350 
       
   351     /**
       
   352      * Delete lock file that signal app to terminate, then
       
   353      * waits until app is actually terminated.
       
   354      * @throws IOException
       
   355      */
       
   356     public void stopApp() throws IOException {
       
   357         deleteLock();
       
   358         waitAppTerminate();
       
   359         int exitcode = appProcess.exitValue();
       
   360         if (exitcode != 0) {
       
   361             throw new IOException("LingeredApp terminated with non-zero exit code " + exitcode);
       
   362         }
       
   363     }
       
   364 
       
   365     /**
       
   366      * LastModified time might not work correctly in some cases it might
       
   367      * cause later failures
       
   368      */
       
   369 
       
   370     public static boolean isLastModifiedWorking() {
       
   371         boolean sane = true;
       
   372         try {
       
   373             long lm = lastModified(".");
       
   374             if (lm == 0) {
       
   375                 System.err.println("SANITY Warning! The lastModifiedTime() doesn't work on this system, it returns 0");
       
   376                 sane = false;
       
   377             }
       
   378 
       
   379             long now = epoch();
       
   380             if (lm > now) {
       
   381                 System.err.println("SANITY Warning! The Clock is wrong on this system lastModifiedTime() > getTime()");
       
   382                 sane = false;
       
   383             }
       
   384 
       
   385             setLastModified(".", epoch());
       
   386             long lm1 = lastModified(".");
       
   387             if (lm1 <= lm) {
       
   388                 System.err.println("SANITY Warning! The setLastModified doesn't work on this system");
       
   389                 sane = false;
       
   390             }
       
   391         }
       
   392         catch(IOException e) {
       
   393             System.err.println("SANITY Warning! IOException during sanity check " + e);
       
   394             sane = false;
       
   395         }
       
   396 
       
   397         return sane;
       
   398     }
       
   399 
       
   400     /**
       
   401      * This part is the application it self
       
   402      */
       
   403     public static void main(String args[]) {
       
   404 
       
   405         if (args.length != 1) {
       
   406             System.err.println("Lock file name is not specified");
       
   407             System.exit(7);
       
   408         }
       
   409 
       
   410         String theLockFileName = args[0];
       
   411 
       
   412         try {
       
   413             Path path = Paths.get(theLockFileName);
       
   414 
       
   415             while (Files.exists(path)) {
       
   416                 // Touch the lock to indicate our readiness
       
   417                 setLastModified(theLockFileName, epoch());
       
   418                 Thread.sleep(spinDelay);
       
   419             }
       
   420         } catch (NoSuchFileException ex) {
       
   421             // Lock deleted while we are setting last modified time.
       
   422             // Ignore error and lets the app exits
       
   423         } catch (Exception ex) {
       
   424             System.err.println("LingeredApp ERROR: " + ex);
       
   425             // Leave exit_code = 1 to Java launcher
       
   426             System.exit(3);
       
   427         }
       
   428 
       
   429         System.exit(0);
       
   430     }
       
   431 }