View Javadoc

1   /* ProcessUtils.java
2    *
3    * $Id: ProcessUtils.java 4223 2006-05-02 02:54:20Z stack-sf $
4    *
5    * Created Jul 19, 2005
6    *
7    * Copyright (C) 2005 Internet Archive.
8    *
9    * This file is part of the Heritrix web crawler (crawler.archive.org).
10   *
11   * Heritrix is free software; you can redistribute it and/or modify
12   * it under the terms of the GNU Lesser Public License as published by
13   * the Free Software Foundation; either version 2.1 of the License, or
14   * any later version.
15   *
16   * Heritrix is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU Lesser Public License for more details.
20   *
21   * You should have received a copy of the GNU Lesser Public License
22   * along with Heritrix; if not, write to the Free Software
23   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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         // Gobble up any output.
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 }