View Javadoc
1   /*
2    * Copyright (C) 2014 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  package com.simpligility.maven.plugins.android;
17  
18  import com.android.annotations.NonNull;
19  import com.android.ide.common.process.JavaProcessExecutor;
20  import com.android.ide.common.process.JavaProcessInfo;
21  import com.android.ide.common.process.ProcessException;
22  import com.android.ide.common.process.ProcessOutput;
23  import com.android.ide.common.process.ProcessOutputHandler;
24  import com.android.ide.common.process.ProcessResult;
25  import com.android.utils.ILogger;
26  import com.google.common.base.Joiner;
27  import com.google.common.collect.Lists;
28  import com.google.common.io.ByteStreams;
29  import com.google.common.io.Closeables;
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.io.OutputStream;
33  import java.util.List;
34  import java.util.Map;
35  /**
36   * Simple implementation of ProcessExecutor, using the standard Java Process(Builder) API.
37   */
38  public class DefaultJavaProcessExecutor implements JavaProcessExecutor
39  {
40      private final ILogger mLogger;
41      public DefaultJavaProcessExecutor( ILogger logger )
42      {
43          mLogger = logger;
44      }
45      @NonNull
46      @Override
47      public ProcessResult execute(
48              @NonNull JavaProcessInfo processInfo,
49              @NonNull ProcessOutputHandler processOutputHandler )
50      {
51          List<String> command = Lists.newArrayList();
52          command.add( processInfo.getExecutable() );
53          command.addAll( processInfo.getArgs() );
54          String commandString = Joiner.on( ' ' ).join( command );
55          mLogger.info( "command: " + commandString );
56          try
57          {
58              // launch the command line process
59              ProcessBuilder processBuilder = new ProcessBuilder( command );
60              Map<String, Object> envVariableMap = processInfo.getEnvironment();
61              if ( !envVariableMap.isEmpty() )
62              {
63                  Map<String, String> env = processBuilder.environment();
64                  for ( Map.Entry<String, Object> entry : envVariableMap.entrySet() )
65                  {
66                      env.put( entry.getKey(), entry.getValue().toString() );
67                  }
68              }
69              // start the process
70              Process process = processBuilder.start();
71              // and grab the output, and the exit code
72              ProcessOutput output = processOutputHandler.createOutput();
73              int exitCode = grabProcessOutput( process, output );
74              processOutputHandler.handleOutput( output );
75              return new ProcessResultImplCopy( commandString, exitCode );
76          }
77          catch ( IOException e )
78          {
79              return new ProcessResultImplCopy( commandString, e );
80          }
81          catch ( InterruptedException e )
82          {
83              // Restore the interrupted status
84              Thread.currentThread().interrupt();
85              return new ProcessResultImplCopy( commandString, e );
86          }
87          catch ( ProcessException e )
88          {
89              return new ProcessResultImplCopy( commandString, e );
90          }
91      }
92      /**
93       * Get the stderr/stdout outputs of a process and return when the process is done.
94       * Both <b>must</b> be read or the process will block on windows.
95       *
96       * @param process The process to get the output from.
97       * @param output The processOutput containing where to send the output.
98       *      Note that on Windows capturing the output is not optional. If output is null
99       *      the stdout/stderr will be captured and discarded.
100      * @return the process return code.
101      * @throws InterruptedException if {@link Process#waitFor()} was interrupted.
102      */
103     private static int grabProcessOutput(
104             @NonNull final Process process,
105             @NonNull final ProcessOutput output ) throws InterruptedException
106     {
107         Thread threadErr = new Thread( "stderr" )
108         {
109             @Override
110             public void run()
111             {
112                 InputStream stderr = process.getErrorStream();
113                 OutputStream stream = output.getErrorOutput();
114                 try
115                 {
116                     ByteStreams.copy( stderr, stream );
117                     stream.flush();
118                 }
119                 catch ( IOException e )
120                 {
121                     // ignore?
122                 }
123                 finally
124                 {
125                     try
126                     {
127                         Closeables.close( stderr, true /* swallowIOException */ );
128                     }
129                     catch ( IOException e )
130                     {
131                         // cannot happen
132                     }
133                     try
134                     {
135                         Closeables.close( stream, true /* swallowIOException */ );
136                     }
137                     catch ( IOException e )
138                     {
139                         // cannot happen
140                     }
141                 }
142             }
143         };
144         Thread threadOut = new Thread( "stdout" )
145         {
146             @Override
147             public void run()
148             {
149                 InputStream stdout = process.getInputStream();
150                 OutputStream stream = output.getStandardOutput();
151                 try
152                 {
153                     ByteStreams.copy( stdout, stream );
154                     stream.flush();
155                 }
156                 catch ( IOException e )
157                 {
158                     // ignore?
159                 }
160                 finally
161                 {
162                     try
163                     {
164                         Closeables.close( stdout, true /* swallowIOException */ );
165                     }
166                     catch ( IOException e )
167                     {
168                         // cannot happen
169                     }
170                     try
171                     {
172                         Closeables.close( stream, true /* swallowIOException */ );
173                     }
174                     catch ( IOException e )
175                     {
176                         // cannot happen
177                     }
178                 }
179             }
180         };
181         threadErr.start();
182         threadOut.start();
183         // it looks like on windows process#waitFor() can return
184         // before the thread have filled the arrays, so we wait for both threads and the
185         // process itself.
186         threadErr.join();
187         threadOut.join();
188         // get the return code from the process
189         return process.waitFor();
190     }
191 }