in progress...
[jalview.git] / forester / java / src / org / forester / util / SystemCommandExecutor.java
1 // $Id:
2 /**
3  * This class can be used to execute a system command from a Java application.
4  * See the documentation for the public methods of this class for more
5  * information.
6  * 
7  * Documentation for this class is available at this URL:
8  * 
9  * http://devdaily.com/java/java-processbuilder-process-system-exec
10  * 
11  * 
12  * Copyright 2010 alvin j. alexander, devdaily.com.
13  * 
14  * This program is free software: you can redistribute it and/or modify it under
15  * the terms of the GNU Lesser Public License as published by the Free Software
16  * Foundation, either version 3 of the License, or (at your option) any later
17  * version.
18  * 
19  * This program is distributed in the hope that it will be useful, but WITHOUT
20  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21  * FOR A PARTICULAR PURPOSE. See the GNU Lesser Public License for more details.
22  * 
23  * You should have received a copy of the GNU Lesser Public License along with
24  * this program. If not, see <http://www.gnu.org/licenses/>.
25  * 
26  * Please ee the following page for the LGPL license:
27  * http://www.gnu.org/licenses/lgpl.txt
28  * 
29  */
30
31 package org.forester.util;
32
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.OutputStream;
37 import java.util.List;
38
39 public class SystemCommandExecutor {
40
41     private final List<String>    commandInformation;
42     private final String          adminPassword;
43     private ThreadedStreamHandler inputStreamHandler;
44     private ThreadedStreamHandler errorStreamHandler;
45
46     /**
47      * Pass in the system command you want to run as a List of Strings, as shown here:
48      * 
49      * List<String> commands = new ArrayList<String>();
50      * commands.add("/sbin/ping");
51      * commands.add("-c");
52      * commands.add("5");
53      * commands.add("www.google.com");
54      * SystemCommandExecutor commandExecutor = new SystemCommandExecutor(commands);
55      * commandExecutor.executeCommand();
56      * 
57      * Note: I've removed the other constructor that was here to support executing
58      *       the sudo command. I'll add that back in when I get the sudo command
59      *       working to the point where it won't hang when the given password is
60      *       wrong.
61      *
62      * @param commandInformation The command you want to run.
63      */
64     public SystemCommandExecutor( final List<String> commandInformation ) {
65         if ( ( commandInformation == null ) || commandInformation.isEmpty() ) {
66             throw new IllegalArgumentException( "The commandInformation is required." );
67         }
68         checkCmdFile( new File( commandInformation.get( 0 ) ) );
69         this.commandInformation = commandInformation;
70         adminPassword = null;
71     }
72
73     public static boolean isExecuteableFile( final File path_to_cmd_f ) {
74         if ( !path_to_cmd_f.exists() ) {
75             return false;
76         }
77         else if ( path_to_cmd_f.isDirectory() ) {
78             return false;
79         }
80         else if ( !path_to_cmd_f.canExecute() ) {
81             return false;
82         }
83         return true;
84     }
85
86     private void checkCmdFile( final File path_to_cmd_f ) {
87         if ( !path_to_cmd_f.exists() ) {
88             throw new IllegalArgumentException( "[" + path_to_cmd_f.getAbsolutePath() + "] does not exist" );
89         }
90         else if ( path_to_cmd_f.isDirectory() ) {
91             throw new IllegalArgumentException( "[" + path_to_cmd_f.getAbsolutePath() + "] is a directory" );
92         }
93         else if ( !path_to_cmd_f.canExecute() ) {
94             throw new IllegalArgumentException( "[" + path_to_cmd_f.getAbsolutePath() + "] is not executeable" );
95         }
96     }
97
98     public int executeCommand() throws IOException, InterruptedException {
99         int exitValue = -99;
100         try {
101             final ProcessBuilder pb = new ProcessBuilder( commandInformation );
102             final Process process = pb.start();
103             // you need this if you're going to write something to the command's input stream
104             // (such as when invoking the 'sudo' command, and it prompts you for a password).
105             final OutputStream stdOutput = process.getOutputStream();
106             // i'm currently doing these on a separate line here in case i need to set them to null
107             // to get the threads to stop.
108             // see http://java.sun.com/j2se/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
109             final InputStream inputStream = process.getInputStream();
110             final InputStream errorStream = process.getErrorStream();
111             // these need to run as java threads to get the standard output and error from the command.
112             // the inputstream handler gets a reference to our stdOutput in case we need to write
113             // something to it, such as with the sudo command
114             inputStreamHandler = new ThreadedStreamHandler( inputStream, stdOutput, adminPassword );
115             errorStreamHandler = new ThreadedStreamHandler( errorStream );
116             // TODO the inputStreamHandler has a nasty side-effect of hanging if the given password is wrong; fix it
117             inputStreamHandler.start();
118             errorStreamHandler.start();
119             // TODO a better way to do this?
120             exitValue = process.waitFor();
121             // TODO a better way to do this?
122             inputStreamHandler.interrupt();
123             errorStreamHandler.interrupt();
124             inputStreamHandler.join();
125             errorStreamHandler.join();
126         }
127         catch ( final IOException e ) {
128             // TODO deal with this here, or just throw it?
129             throw e;
130         }
131         catch ( final InterruptedException e ) {
132             // generated by process.waitFor() call
133             // TODO deal with this here, or just throw it?
134             throw e;
135         }
136         finally {
137             return exitValue;
138         }
139     }
140
141     /**
142      * Get the standard error (stderr) from the command you just exec'd.
143      */
144     public StringBuilder getStandardErrorFromCommand() {
145         return errorStreamHandler.getOutputBuffer();
146     }
147
148     /**
149      * Get the standard output (stdout) from the command you just exec'd.
150      */
151     public StringBuilder getStandardOutputFromCommand() {
152         return inputStreamHandler.getOutputBuffer();
153     }
154 }