View Javadoc
1   /*
2    * Copyright (C) 2014 Cristian Sulea ( http://cristian.sulea.net )
3    * 
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    * 
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU Lesser General Public License for more details.
13   * 
14   * You should have received a copy of the GNU Lesser General Public License
15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  
18  package jatoo.exec;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  
27  /**
28   * Handy class to ease the execution of operating system commands.
29   * 
30   * TODO: WARNING: not tested on Linux
31   * 
32   * @author <a href="http://cristian.sulea.net" rel="author">Cristian Sulea</a>
33   * @version 1.5, July 25, 2014
34   */
35  public class CommandExecutor {
36  
37    private final List<String> commandPrefix;
38  
39    public CommandExecutor() {
40  
41      String osName = System.getProperty("os.name");
42  
43      //
44      // Linux systems (WARNING: not tested)
45  
46      if (osName.equals("Linux")) {
47        commandPrefix = new ArrayList<String>(0);
48      }
49  
50      //
51      // old Windows systems
52  
53      else if (osName.equals("Windows 95") || osName.equals("Windows 98") || osName.equalsIgnoreCase("Windows ME")) {
54        commandPrefix = Arrays.asList("command.com", "/C");
55      }
56  
57      //
58      // modern (others) Windows systems
59  
60      else {
61        commandPrefix = Arrays.asList("cmd.exe", "/C");
62      }
63    }
64  
65    /**
66     * Handy method for {@link #exec(String, File, OutputStream, boolean)} running
67     * in working folder of JVM with no dump output stream.
68     * 
69     * @param command
70     *          the command to be executed
71     * 
72     * @return the exit value of the process (by convention, the value
73     *         <code>0</code> indicates normal termination)
74     * 
75     * @throws IOException
76     *           if an I/O error occurs
77     * @throws InterruptedException
78     *           if the current thread is {@linkplain Thread#interrupt()
79     *           interrupted} by another thread while it is waiting
80     */
81    public final int exec(final String command) throws IOException, InterruptedException {
82      return exec(command, null, null, false);
83    }
84  
85    /**
86     * Handy method for {@link #exec(String, File, OutputStream, boolean)} with no
87     * dump output stream.
88     * 
89     * @param command
90     *          the command to be executed
91     * @param folder
92     *          the working folder
93     * 
94     * @return the exit value of the process (by convention, the value
95     *         <code>0</code> indicates normal termination)
96     * 
97     * @throws IOException
98     *           if an I/O error occurs
99     * @throws InterruptedException
100    *           if the current thread is {@linkplain Thread#interrupt()
101    *           interrupted} by another thread while it is waiting
102    */
103   public final int exec(final String command, final File folder) throws IOException, InterruptedException {
104     return exec(command, folder, null, false);
105   }
106 
107   /**
108    * Handy method for {@link #exec(String, File, OutputStream, boolean)} running
109    * in working folder of JVM with specified dump output stream (but no
110    * closing).
111    * 
112    * @param command
113    *          the command to be executed
114    * @param dumpOutputStream
115    *          the stream where the process will dump (exhaust) his contents
116    * 
117    * @return the exit value of the process (by convention, the value
118    *         <code>0</code> indicates normal termination)
119    * 
120    * @throws IOException
121    *           if an I/O error occurs
122    * @throws InterruptedException
123    *           if the current thread is {@linkplain Thread#interrupt()
124    *           interrupted} by another thread while it is waiting
125    */
126   public final int exec(final String command, final OutputStream dumpOutputStream) throws IOException, InterruptedException {
127     return exec(command, null, dumpOutputStream, false);
128   }
129 
130   /**
131    * Handy method for {@link #exec(String, File, OutputStream, boolean)} with
132    * specified dump output stream (but no closing).
133    * 
134    * @param command
135    *          the command to be executed
136    * @param folder
137    *          the working folder
138    * @param dumpOutputStream
139    *          the stream where the process will dump (exhaust) his contents
140    * 
141    * @return the exit value of the process (by convention, the value
142    *         <code>0</code> indicates normal termination)
143    * 
144    * @throws IOException
145    *           if an I/O error occurs
146    * @throws InterruptedException
147    *           if the current thread is {@linkplain Thread#interrupt()
148    *           interrupted} by another thread while it is waiting
149    */
150   public final int exec(final String command, final File folder, final OutputStream dumpOutputStream) throws IOException, InterruptedException {
151     return exec(command, folder, dumpOutputStream, false);
152   }
153 
154   /**
155    * Executes the specified command.
156    * 
157    * @param command
158    *          the command to be executed
159    * @param folder
160    *          the working folder
161    * @param dumpOutputStream
162    *          the stream where the process will dump (exhaust) his contents
163    * @param closeDumpOutputStream
164    *          <code>true</code> if the dump stream should be closed when the
165    *          execution ends, <code>false</code> otherwise
166    * 
167    * @return the exit value of the process (by convention, the value
168    *         <code>0</code> indicates normal termination)
169    * 
170    * @throws IOException
171    *           if an I/O error occurs
172    * @throws InterruptedException
173    *           if the current thread is {@linkplain Thread#interrupt()
174    *           interrupted} by another thread while it is waiting
175    */
176   public final int exec(final String command, final File folder, final OutputStream dumpOutputStream, final boolean closeDumpOutputStream) throws IOException, InterruptedException {
177 
178     //
179     // add the prefix to the command
180 
181     List<String> commandList = new ArrayList<String>(commandPrefix.size() + 1);
182     commandList.addAll(commandPrefix);
183     commandList.add(command);
184 
185     //
186     // create and start the process
187 
188     Process process = new ProcessBuilder(commandList).directory(folder).start();
189 
190     //
191     // exhaust both the standard error stream and the standard output stream
192 
193     if (dumpOutputStream != null) {
194       new Thread(new InputStreamExhausterWithDumpStream(process.getInputStream(), dumpOutputStream, closeDumpOutputStream)).start();
195       new Thread(new InputStreamExhausterWithDumpStream(process.getErrorStream(), dumpOutputStream, closeDumpOutputStream)).start();
196     } else {
197       new Thread(new InputStreamExhauster(process.getInputStream())).start();
198       new Thread(new InputStreamExhauster(process.getErrorStream())).start();
199     }
200 
201     //
202     // wait until the process has terminated
203 
204     return process.waitFor();
205   }
206 
207 }