001    /*
002     * $RCSfile: CSHCommandLineTask.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 gov.bnl.star.offline.scheduler.ComponentLibrary;
026    import java.io.*;
027    import java.util.logging.Level;
028    import org.apache.log4j.Logger;
029    
030    
031    
032    /** A command line task.
033     *
034     * @author  Gabriele Carcassi
035     * @version $Revision: 1.13 $ $Date: 2006/11/21 00:41:29 $
036     */
037    public class CSHCommandLineTask extends Task {
038        static private Logger log = Logger.getLogger(CSHCommandLineTask.class.getName());
039        //ISSUE: Unixism 
040        private static String csh = ComponentLibrary.getInstance().getProgramLocation("csh");
041        private String commandLine;
042        private String[] commandLineArray;
043        private boolean bufferOutput;
044        private boolean bufferError;
045        private String commandOutput;
046        private Process proc;
047    
048        /** A new command line task.
049         * <p>
050         * WARNING: the parsing of the command line won't be what you would have while
051         * executing at a UNIX prompt. The command line won't be passed to the shell,
052         * that is why it may have different result. For example, wildcards are usually
053         * resolved by the shell, and not by the program themselves.
054         * @param commandLine the command line to be executed
055         */
056        public CSHCommandLineTask(String commandLine) {
057            this(commandLine, false);
058        }
059    
060        /** A new command line task. This allows you to buffer the output of the command.
061         * You can retrieve the ouput with <CODE>getOutput</CODE> or
062         * <CODE>getOutputReader</CODE>
063         * <p>
064         * WARNING: the parsing of the command line won't be what you would have while
065         * executing at a UNIX prompt. The command line won't be passed to the shell,
066         * that is why it may have different result. For example, wildcards are usually
067         * resolved by the shell, and not by the program themselves.
068         * @param commandLine the command line to be executed
069         * @param bufferCommandOutput if true, the output will be buffered
070         */
071        public CSHCommandLineTask(String commandLine, boolean bufferCommandOutput) {
072            this.commandLine = commandLine;
073            this.bufferOutput = bufferCommandOutput;
074        }
075    
076        /** A new command line task with a timeout. The command will be aborted if the
077         * process doesn't return before the timeout.
078         * <p>
079         * WARNING: the parsing of the command line won't be what you would have while
080         * executing at a UNIX prompt. The command line won't be passed to the shell,
081         * that is why it may have different result. For example, wildcards are usually
082         * resolved by the shell, and not by the program themselves.
083         * @param commandLine
084         * @param msTimeout
085         */
086        public CSHCommandLineTask(String commandLine, int msTimeout) {
087            super(msTimeout);
088            this.commandLine = commandLine;
089        }
090    
091        public CSHCommandLineTask(String commandLine, int msTimeout, boolean bufferCommandOutput) {
092            super(msTimeout);
093            this.commandLine = commandLine;
094            this.bufferOutput = bufferCommandOutput;
095        }
096    
097        public CSHCommandLineTask(String commandLine, boolean bufferCommandError, int msTimeout) {
098            super(msTimeout);
099            this.commandLine = commandLine;
100            this.bufferOutput = bufferCommandError;
101            this.bufferError = bufferCommandError;
102        }
103    
104        /** Calls the external command. Don't use this directly, or your task won't be
105         * executed in a separate thread. Use <CODE>execute</CODE> instead.
106         */
107        public void run() {
108            try {
109                // Beware of Unixism ...
110                if (System.getProperty("os.name").startsWith("Win")){
111                    proc = Runtime.getRuntime().exec(commandLine);
112                } else {
113                    proc = Runtime.getRuntime().exec(
114                                new String[] {csh, "-cf",  commandLine}
115                            );  // Explicit shell needs to be specified
116                                // otherwise, LSF breaks probably because
117                                // of the LSB shell env.
118                }
119                            
120                // will use a StrigWriter
121                StringWriter write = new StringWriter();
122                char[] cbuf = new char[256];
123                int nChars = 0;
124               
125                if (bufferOutput) {
126                    InputStreamReader read = new InputStreamReader(proc.getInputStream());                                  
127                    while ( (nChars = read.read(cbuf)) != -1 ){
128                        write.write(cbuf,0,nChars);
129                    }
130                    read.close();
131                }
132                if (bufferError) {
133                    InputStreamReader errorStream = new InputStreamReader(proc.getErrorStream());
134                    while ( (nChars = errorStream.read(cbuf)) != -1) {
135                        write.write(cbuf, 0, nChars);
136                    }
137                    errorStream.close();
138                }
139                    
140                commandOutput = write.getBuffer().toString();
141                log.info(" We got return string=" + commandOutput.trim());
142                
143                // This Thread is mainly composed of a Runtime.getRuntime().exec()
144                // This has to be waited for.
145                try {
146                    proc.waitFor();
147                } catch (InterruptedException e){
148                    log.warn("[proc] was interrupted ",e);  
149                }
150                
151                // TODO: This is a hack for a bug that showed up
152                // after RC2. I don't understand if it's a problem caused by a code
153                // change, I hardly see how, or upgrade of the compiler.
154                // The problem is that sometimes proc.exitValue() would generate
155                // an exception, even if before was executed proc.waitFor().
156                // Seems some sort of problem of synchronization.
157                // Waiting for a second seems to make the problem disappear.
158                //try {
159                //    Thread.sleep(1000);
160                //} catch (Exception e) {
161                //}
162                exitStatus = proc.exitValue();
163                
164            } catch (Exception e) {
165                log.warn("Command line task failed", e);
166                exitStatus = -1;
167            }
168        }
169    
170        public void destroy() {
171            try {
172                proc.destroy();
173                log.warn("The thread process had to be destroyed");
174            } catch (Exception e) {
175                log.fatal("The thread process could not be destroyed",e);
176            }
177        }
178    
179        /** Returns all the output of the command.
180         * @return the output of the command line
181         */
182        public String getOutput() {
183            return commandOutput;
184        }
185    
186        /** Returns the output of the command
187         * @return the output of the command
188         */
189        public BufferedReader getOutputReader() {
190            if (commandOutput == null) {
191                return null;
192            }
193    
194            return new BufferedReader(new StringReader(commandOutput));
195        }
196    
197        /**
198         * @param args the command line arguments
199         */
200        public static void main(String[] args) {
201            CommandLineTask test = new CommandLineTask(args[1], true);
202            System.out.println("Executing \"" + args[1] + "\": ");
203            test.execute();
204            System.out.println();
205            System.out.println("Output: " + test.getOutput());
206            System.out.println();
207            System.out.println("Exit status: " + test.getExitStatus());
208        }
209        
210        
211    }
212    
213    
214