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