findbugs
[utils] / support / general / src / main / java / org / wamblee / io / SimpleProcess.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
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 org.wamblee.io;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.OutputStream;
27 import java.io.PrintStream;
28 import java.io.StringWriter;
29 import java.io.Writer;
30 import java.util.Arrays;
31
32 /**
33  * 
34  * @author $author$
35  * @version $Revision$
36  */
37 public class SimpleProcess {
38     private static final Log LOG = LogFactory.getLog(SimpleProcess.class);
39
40     private File directory;
41
42     private String[] cmd;
43
44     private String stdout;
45
46     private String stderr;
47
48     /**
49      * Creates a new SimpleProcess object.
50      * 
51      */
52     public SimpleProcess(File aDirectory, String[] aCmd) {
53         directory = aDirectory;
54         cmd = Arrays.copyOf(aCmd, aCmd.length);
55     }
56
57     /**
58      * 
59      * @return the stdout
60      */
61     public String getStdout() {
62         return stdout;
63     }
64
65     /**
66      * 
67      * @return the stderr
68      */
69     public String getStderr() {
70         return stderr;
71     }
72
73     /**
74      * Runs the process and blocks until it is done.
75      * 
76      * @return Exit status of the process.
77      * 
78      * @throws IOException
79      *             In case of problems.
80      */
81     public int run() throws IOException {
82         return runImpl();
83     }
84
85     private int runImpl() throws IOException {
86         try {
87             StringBuffer fullcmd = new StringBuffer();
88
89             for (String part : cmd) {
90                 fullcmd.append(" " + part);
91             }
92
93             LOG.debug("Executing '" + fullcmd + "' in directory '" + directory +
94                 "'");
95
96             java.lang.Process proc = Runtime.getRuntime().exec(cmd, null,
97                 directory);
98
99             // Read standard output and error in separate threads to avoid
100             // deadlock.
101             StringWriter myStdout = new StringWriter();
102             StringWriter myStderr = new StringWriter();
103             Thread stdoutReader = readAndLogStream("STDOUT>  ", proc
104                 .getInputStream(), myStdout);
105             Thread stderrReader = readAndLogStream("STDERR>  ", proc
106                 .getErrorStream(), myStderr);
107
108             try {
109                 proc.waitFor();
110             } catch (InterruptedException e) {
111                 IOException exception = new IOException(
112                     "Process was terminated: " + this);
113                 exception.initCause(e);
114                 throw exception;
115             }
116
117             waitForReader(stdoutReader);
118             waitForReader(stderrReader);
119
120             stdout = myStdout.toString();
121             stderr = myStderr.toString();
122
123             if (proc.exitValue() != 0) {
124                 LOG.warn("Exit value was non-zero: " + this);
125             } else {
126                 LOG.debug("Process finished");
127             }
128
129             return proc.exitValue();
130         } catch (IOException e) {
131             IOException exception = new IOException(
132                 "Error executing process: " + this);
133             exception.initCause(e);
134             throw exception;
135         }
136     }
137
138     private void waitForReader(Thread aReaderThread) {
139         try {
140             aReaderThread.join();
141         } catch (InterruptedException e) {
142             LOG
143                 .warn(this +
144                     ": error waiting for output stream reader of process to finish");
145         }
146     }
147
148     private Thread readAndLogStream(final String aPrefix,
149         final InputStream aStream, final Writer aOutput) {
150         Thread inputReader = new Thread() {
151             @Override
152             public void run() {
153                 BufferedReader br = null;
154
155                 try {
156                     br = new BufferedReader(new InputStreamReader(aStream));
157
158                     String str;
159
160                     while ((str = br.readLine()) != null) {
161                         LOG.debug(aPrefix + str);
162                         aOutput.write(str);
163                     }
164                 } catch (IOException e) {
165                     LOG.warn(SimpleProcess.this +
166                         ": error reading input stream", e);
167                 } finally {
168                     if (br != null) {
169                         try {
170                             br.close();
171                         } catch (IOException e) {
172                             LOG.warn("Error closing stream " + aPrefix);
173                         }
174                     }
175                 }
176             }
177         };
178
179         inputReader.start();
180
181         return inputReader;
182     }
183
184     @Override
185     public String toString() {
186         String fullcmd = "";
187
188         for (String part : cmd) {
189             fullcmd += (part + " ");
190         }
191
192         return "process(dir = '" + directory + "', cmd = '" + fullcmd + "')";
193     }
194 }