1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.archive.util;
26
27 import java.io.BufferedReader;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.util.logging.Level;
32 import java.util.logging.Logger;
33
34 /***
35 * Class to run an external process.
36 * @author stack
37 * @version $Date: 2006-05-02 02:54:20 +0000 (Tue, 02 May 2006) $ $Revision: 4223 $
38 */
39 public class ProcessUtils {
40 private static final Logger LOGGER =
41 Logger.getLogger(ProcessUtils.class.getName());
42
43 protected ProcessUtils() {
44 super();
45 }
46
47 /***
48 * Thread to gobble up an output stream.
49 * See http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
50 */
51 protected class StreamGobbler extends Thread {
52 private final InputStream is;
53 private final StringBuffer sink = new StringBuffer();
54
55 StreamGobbler(InputStream is, String name) {
56 this.is = is;
57 setName(name);
58 }
59
60 public void run() {
61 try {
62 BufferedReader br =
63 new BufferedReader(new InputStreamReader(this.is));
64 for (String line = null; (line = br.readLine()) != null;) {
65 this.sink.append(line);
66 }
67 } catch (IOException ioe) {
68 ioe.printStackTrace();
69 }
70 }
71
72 public String getSink() {
73 return this.sink.toString();
74 }
75 }
76
77 /***
78 * Data structure to hold result of a process exec.
79 * @author stack
80 * @version $Date: 2006-05-02 02:54:20 +0000 (Tue, 02 May 2006) $ $Revision: 4223 $
81 */
82 public class ProcessResult {
83 private final String [] args;
84 private final int result;
85 private final String stdout;
86 private final String stderr;
87
88 protected ProcessResult(String [] args, int result, String stdout,
89 String stderr) {
90 this.args = args;
91 this.result = result;
92 this.stderr = stderr;
93 this.stdout = stdout;
94 }
95
96 public int getResult() {
97 return this.result;
98 }
99
100 public String getStdout() {
101 return this.stdout;
102 }
103
104 public String getStderr() {
105 return this.stderr;
106 }
107
108 public String toString() {
109 StringBuffer sb = new StringBuffer();
110 for (int i = 0; i < this.args.length; i++) {
111 sb.append(this.args[i]);
112 sb.append(", ");
113 }
114 return sb.toString() + " exit code: " + this.result +
115 ((this.stderr != null && this.stderr.length() > 0)?
116 "\nSTDERR: " + this.stderr: "") +
117 ((this.stdout != null && this.stdout.length() > 0)?
118 "\nSTDOUT: " + this.stdout: "");
119 }
120 }
121
122 /***
123 * Runs process.
124 * @param args List of process args.
125 * @return A ProcessResult data structure.
126 * @throws IOException If interrupted, we throw an IOException. If non-zero
127 * exit code, we throw an IOException (This may need to change).
128 */
129 public static ProcessUtils.ProcessResult exec(String [] args)
130 throws IOException {
131 Process p = Runtime.getRuntime().exec(args);
132 ProcessUtils pu = new ProcessUtils();
133
134 StreamGobbler err = pu.new StreamGobbler(p.getErrorStream(), "stderr");
135 err.setDaemon(true);
136 err.start();
137 StreamGobbler out = pu.new StreamGobbler(p.getInputStream(), "stdout");
138 out.setDaemon(true);
139 out.start();
140 int exitVal;
141 try {
142 exitVal = p.waitFor();
143 } catch (InterruptedException e) {
144 throw new IOException("Wait on process " + args + " interrupted: "
145 + e.getMessage());
146 }
147 ProcessUtils.ProcessResult result =
148 pu.new ProcessResult(args, exitVal, out.getSink(), err.getSink());
149 if (exitVal != 0) {
150 throw new IOException(result.toString());
151 } else if (LOGGER.isLoggable(Level.INFO)) {
152 LOGGER.info(result.toString());
153 }
154 return result;
155 }
156 }