Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.sdklib.util;
     18 
     19 import com.android.annotations.NonNull;
     20 import com.android.annotations.Nullable;
     21 
     22 import java.io.BufferedReader;
     23 import java.io.IOException;
     24 import java.io.InputStreamReader;
     25 
     26 public class GrabProcessOutput {
     27 
     28     public enum Wait {
     29         /**
     30          * Doesn't wait for the exec to complete.
     31          * This still monitors the output but does not wait for the process to finish.
     32          * In this mode the process return code is unknown and always 0.
     33          */
     34         ASYNC,
     35         /**
     36          * This waits for the process to finish.
     37          * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the
     38          * error code from the process.
     39          * In some rare cases and depending on the OS, the process might not have
     40          * finished dumping data into stdout/stderr.
     41          * <p/>
     42          * Use this when you don't particularly care for the output but instead
     43          * care for the return code of the executed process.
     44          */
     45         WAIT_FOR_PROCESS,
     46         /**
     47          * This waits for the process to finish <em>and</em> for the stdout/stderr
     48          * threads to complete.
     49          * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the
     50          * error code from the process.
     51          * <p/>
     52          * Use this one when capturing all the output from the process is important.
     53          */
     54         WAIT_FOR_READERS,
     55     }
     56 
     57     public interface IProcessOutput {
     58         /**
     59          * Processes an stdout message line.
     60          * @param line The stdout message line. Null when the reader reached the end of stdout.
     61          */
     62         public void out(@Nullable String line);
     63         /**
     64          * Processes an stderr message line.
     65          * @param line The stderr message line. Null when the reader reached the end of stderr.
     66          */
     67         public void err(@Nullable String line);
     68     }
     69 
     70     /**
     71      * Get the stderr/stdout outputs of a process and return when the process is done.
     72      * Both <b>must</b> be read or the process will block on windows.
     73      *
     74      * @param process The process to get the ouput from.
     75      * @param output Optional object to capture stdout/stderr.
     76      *      Note that on Windows capturing the output is not optional. If output is null
     77      *      the stdout/stderr will be captured and discarded.
     78      * @param waitMode Whether to wait for the process and/or the readers to finish.
     79      * @return the process return code.
     80      * @throws InterruptedException if {@link Process#waitFor()} was interrupted.
     81      */
     82     public static int grabProcessOutput(
     83             @NonNull final Process process,
     84             Wait waitMode,
     85             @Nullable final IProcessOutput output) throws InterruptedException {
     86         // read the lines as they come. if null is returned, it's
     87         // because the process finished
     88         Thread threadErr = new Thread("stderr") {
     89             @Override
     90             public void run() {
     91                 // create a buffer to read the stderr output
     92                 InputStreamReader is = new InputStreamReader(process.getErrorStream());
     93                 BufferedReader errReader = new BufferedReader(is);
     94 
     95                 try {
     96                     while (true) {
     97                         String line = errReader.readLine();
     98                         if (output != null) {
     99                             output.err(line);
    100                         }
    101                         if (line == null) {
    102                             break;
    103                         }
    104                     }
    105                 } catch (IOException e) {
    106                     // do nothing.
    107                 }
    108             }
    109         };
    110 
    111         Thread threadOut = new Thread("stdout") {
    112             @Override
    113             public void run() {
    114                 InputStreamReader is = new InputStreamReader(process.getInputStream());
    115                 BufferedReader outReader = new BufferedReader(is);
    116 
    117                 try {
    118                     while (true) {
    119                         String line = outReader.readLine();
    120                         if (output != null) {
    121                             output.out(line);
    122                         }
    123                         if (line == null) {
    124                             break;
    125                         }
    126                     }
    127                 } catch (IOException e) {
    128                     // do nothing.
    129                 }
    130             }
    131         };
    132 
    133         threadErr.start();
    134         threadOut.start();
    135 
    136         if (waitMode == Wait.ASYNC) {
    137             return 0;
    138         }
    139 
    140         // it looks like on windows process#waitFor() can return
    141         // before the thread have filled the arrays, so we wait for both threads and the
    142         // process itself.
    143         if (waitMode == Wait.WAIT_FOR_READERS) {
    144             try {
    145                 threadErr.join();
    146             } catch (InterruptedException e) {
    147             }
    148             try {
    149                 threadOut.join();
    150             } catch (InterruptedException e) {
    151             }
    152         }
    153 
    154         // get the return code from the process
    155         return process.waitFor();
    156     }
    157 }
    158