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