001    /*
002     * $RCSfile: CommandLineTask.java,v $ 
003     *
004     * Created on September 30, 2002, 10:02 AM
005     *
006     * This file is part of the STAR Scheduler.
007     * Copyright (c) 2002-2006 STAR Collaboration - Brookhaven National Laboratory
008     *
009     * STAR Scheduler is free software; you can redistribute it and/or modify
010     * it under the terms of the GNU General Public License as published by
011     * the Free Software Foundation; either version 2 of the License, or
012     * (at your option) any later version.
013     *
014     * STAR Scheduler is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017     * GNU General Public License for more details.
018     *
019     * You should have received a copy of the GNU General Public License
020     * along with STAR Scheduler; if not, write to the Free Software
021     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
022     */
023    package gov.bnl.star.offline.scheduler.util;
024    
025    import java.io.*;
026    import java.util.logging.Level;
027    import org.apache.log4j.Logger;
028    
029    
030    /** A command line task.
031     *  Fork and execute programs in separate time limited thread.
032     * @author  Gabriele Carcassi
033     * @version $Revision: 1.14 $ $Date: 2006/11/21 00:41:30 $
034     */
035    public class CommandLineTask extends Task {
036        static private Logger log = Logger.getLogger(CommandLineTask.class.getName());
037        private String commandLine;
038        private String[] commandLineArray;
039        private boolean bufferOutput;
040        private String commandOutput;
041        private Process proc;
042    
043        /** A new command line task.
044         * <p>
045         * WARNING: the parsing of the command line won't be what you would have while
046         * executing at a UNIX prompt. The command line won't be passed to the shell,
047         * that is why it may have different result. For example, wildcards are usually
048         * resolved by the shell, and not by the program themselves.
049         * @param commandLine the command line to be executed
050         */
051        public CommandLineTask(String commandLine) {
052            this(commandLine, false);
053        }
054    
055        /** A new command line task. This allows you to buffer the output of the command.
056         * You can retrieve the ouput with <CODE>getOutput</CODE> or
057         * <CODE>getOutputReader</CODE>
058         * <p>
059         * WARNING: the parsing of the command line won't be what you would have while
060         * executing at a UNIX prompt. The command line won't be passed to the shell,
061         * that is why it may have different result. For example, wildcards are usually
062         * resolved by the shell, and not by the program themselves.
063         * @param commandLine the command line to be executed
064         * @param bufferCommandOutput if true, the output will be buffered
065         */
066        public CommandLineTask(String commandLine, boolean bufferCommandOutput) {
067            this.commandLine = commandLine;
068            this.bufferOutput = bufferCommandOutput;
069        }
070    
071        /** A new command line task with a timeout. The command will be aborted if the
072         * process doesn't return before the timeout.
073         * <p>
074         * WARNING: the parsing of the command line won't be what you would have while
075         * executing at a UNIX prompt. The command line won't be passed to the shell,
076         * that is why it may have different result. For example, wildcards are usually
077         * resolved by the shell, and not by the program themselves.
078         * @param commandLine
079         * @param msTimeout
080         */
081        public CommandLineTask(String commandLine, int msTimeout) {
082            super(msTimeout);
083            this.commandLine = commandLine;
084        }
085    
086        public CommandLineTask(String commandLine, int msTimeout, boolean bufferCommandOutput) {
087            super(msTimeout);
088            this.commandLine = commandLine;
089            this.bufferOutput = bufferCommandOutput;
090        }
091    
092        /** A new command line task. This allows you to buffer the output of the command.
093         * You can retrieve the ouput with <CODE>getOutput</CODE> or
094         * <CODE>getOutputReader</CODE>
095         * <p>
096         * By actually specifying he argument as a String array, this constructor gives
097         * you better control of what is being executed
098         * @param commandLineArray the command line to be executed already parsed in the different arguments
099         * @param bufferCommandOutput if true, the output will be buffered
100         */
101        public CommandLineTask(String[] commandLineArray,
102            boolean bufferCommandOutput) {
103            this.commandLineArray = commandLineArray;
104            this.bufferOutput = bufferCommandOutput;
105        }
106    
107        /** Calls the external command. Don't use this directly, or your task won't be
108         * executed in a separate thread. Use <CODE>execute</CODE> instead.
109         */
110        public void run() {
111            try {
112                if (commandLine != null) {
113                    proc = Runtime.getRuntime().exec(commandLine);
114                } else {
115                    proc = Runtime.getRuntime().exec(commandLineArray);
116                }
117    
118                if (bufferOutput) {
119                    Reader read = new InputStreamReader(proc.getInputStream());
120                    StringWriter write = new StringWriter();
121                    char[] cbuf = new char[256];
122                    int nChars = 0;
123    
124                    while ((nChars = read.read(cbuf)) != -1) {
125                        write.write(cbuf, 0, nChars);
126                    }
127    
128                    commandOutput = write.getBuffer().toString();
129                } else {
130                //    log.log(Level.WARNING,"No bufferOutput...");
131                    proc.waitFor();
132                }
133                //proc.waitFor();
134                
135                // TODO: This is a hack for a bug that showed up
136                // after RC2. I don't understand if it's a problem caused by a code
137                // change, I hardly see how, or upgrade of the compiler.
138                // The problem is that sometimes proc.exitValue() would generate
139                // an exception, even if before was executed proc.waitFor().
140                // Seems some sort of problem of synchronization.
141                // Waiting for a second seems to make the problem disappear.
142                try {
143                    Thread.sleep(1000);
144                } catch (Exception e) {
145                }
146                
147                exitStatus = proc.exitValue();
148            } catch (Exception e) {
149                log.error("Command line task failed", e);
150                exitStatus = -1;
151            }
152        }
153    
154        public void destroy() {
155            try {
156                proc.destroy();
157                log.warn("The thread command had to be destroyed");
158            } catch (Exception e) {
159                log.fatal("The thread command could not be destroyed",e);
160            }
161        }
162    
163        /** Returns all the output of the command.
164         * @return the output of the command line
165         */
166        public String getOutput() {
167            return commandOutput;
168        }
169    
170        /** Returns the output of the command
171         * @return the output of the command
172         */
173        public BufferedReader getOutputReader() {
174            if (commandOutput == null) {
175                return null;
176            }
177    
178            return new BufferedReader(new StringReader(commandOutput));
179        }
180    
181        /**
182         * @param args the command line arguments
183         */
184        public static void main(String[] args) {
185            CommandLineTask test = new CommandLineTask(args[1], true);
186            System.out.println("Executing \"" + args[1] + "\": ");
187            test.execute();
188            System.out.println();
189            System.out.println("Output: " + test.getOutput());
190            System.out.println();
191            System.out.println("Exit status: " + test.getExitStatus());
192        }
193    }