001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.cipres.kepler;
031
032import java.util.Observable;
033import java.util.Observer;
034
035import javax.swing.JOptionPane;
036
037import org.cipres.helpers.JRun;
038
039import ptolemy.actor.CompositeActor;
040import ptolemy.actor.Manager;
041import ptolemy.actor.TypedAtomicActor;
042import ptolemy.actor.TypedIOPort;
043import ptolemy.data.IntToken;
044import ptolemy.data.StringToken;
045import ptolemy.data.expr.FileParameter;
046import ptolemy.data.expr.Parameter;
047import ptolemy.data.type.BaseType;
048import ptolemy.kernel.CompositeEntity;
049import ptolemy.kernel.util.IllegalActionException;
050import ptolemy.kernel.util.NameDuplicationException;
051import ptolemy.kernel.util.Nameable;
052
053//////////////////////////////////////////////////////////////////////////
054//// JRunCIPRes
055/**
056 * This actor calls the external applications.
057 * 
058 * The JRunCIPRes actor provides a template to invoke external Cipres
059 * applications. A set of parameters is configured in the actor to collect
060 * application related information. The value of these parameters will be fed to
061 * the JRun object when the actor fires. Thus the GUIRun object will invoke the
062 * corresponding application with those values, like the input/output file names
063 * and the working directory. After the execution, if the external program
064 * returns correctly, the standard output of the execution will be sent to the
065 * standard output file, and the standard error will be sent to the standard
066 * error file.
067 * 
068 * The user can also set some words to be monitored in the standard output
069 * stream. The observer/observable design pattern is used here. The JRunCIPRes
070 * object is an observer. The JRun object is an observable. Whenever the JRun
071 * object find any of these monitored words in the standard output steam of the
072 * program execution, it will notify the JRunCIPRes objects to promote the user
073 * handling the issue. Usually the JRunCIPRes actor will stop the execution of
074 * the program.
075 * 
076 * @author Zhijie Guan
077 * @version $Id: JRunCIPRes.java 24234 2010-05-06 05:21:26Z welker $
078 */
079
080public class JRunCIPRes extends TypedAtomicActor implements Observer {
081
082        /**
083         * Construct JRunCIPRes source with the given container and name.
084         * 
085         * @param container
086         *            The container.
087         * @param name
088         *            The name of this actor.
089         * @exception IllegalActionException
090         *                If the entity cannot be contained by the proposed
091         *                container.
092         * @exception NameDuplicationException
093         *                If the container already has an actor with this name.
094         */
095
096        public JRunCIPRes(CompositeEntity container, String name)
097                        throws NameDuplicationException, IllegalActionException {
098                super(container, name);
099
100                // initialize the standard output port
101                standardOutput = new TypedIOPort(this, "standardOutput", false, true);
102                standardOutput.setDisplayName("Standard Output");
103                standardOutput.setTypeEquals(BaseType.STRING);
104
105                // initialize the standard error port
106                standardError = new TypedIOPort(this, "standardError", false, true);
107                standardError.setDisplayName("Standard Error");
108                standardError.setTypeEquals(BaseType.STRING);
109
110                // initialize the exit code port
111                exitCode = new TypedIOPort(this, "exitCode", false, true);
112                exitCode.setDisplayName("Exit Code");
113                exitCode.setTypeEquals(BaseType.STRING);
114
115                // initialize the input trigger port
116                inputTrigger = new TypedIOPort(this, "inputTrigger", true, false);
117                inputTrigger.setDisplayName("Input Trigger");
118                inputTrigger.setTypeEquals(BaseType.STRING);
119
120                // command name
121                command = new FileParameter(this, "command");
122                command.setDisplayName("External Command");
123
124                // standard output file name
125                outputFile = new FileParameter(this, "outputFile");
126                outputFile.setDisplayName("Standard Output File");
127
128                // standard error file name
129                errorFile = new FileParameter(this, "errorFile");
130                errorFile.setDisplayName("Standard Error File");
131
132                // arguments
133                arguments = new Parameter(this, "arguments", new StringToken(""));
134                arguments.setDisplayName("Arguments");
135
136                // working directory
137                workingDirectory = new Parameter(this, "workingDirectory",
138                                new StringToken(""));
139                workingDirectory.setDisplayName("Working Direcotry");
140
141                // monitored error words
142                monitoredErrorWords = new Parameter(this, "monitoredErrorWords",
143                                new StringToken(""));
144                monitoredErrorWords.setDisplayName("Error Words to be Monitored");
145
146                _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" "
147                                + "width=\"60\" height=\"20\" " + "style=\"fill:white\"/>\n"
148                                + "</svg>\n");
149
150        }
151
152        // /////////////////////////////////////////////////////////////////
153        // // ports and parameters ////
154
155        /**
156         * The command path and name are defined in this parameter.
157         */
158        public FileParameter command;
159
160        /**
161         * The standard output file's path and name are defined in this parameter.
162         */
163        public FileParameter outputFile;
164
165        /**
166         * The standard error file's path and name are defined in this parameter.
167         */
168        public FileParameter errorFile;
169
170        /**
171         * The arguments of the program that will be executed are defined in this
172         * parameter.
173         */
174        public Parameter arguments;
175
176        /**
177         * The workfing directory of the external program is defined in this
178         * parameter.
179         */
180        public Parameter workingDirectory;
181
182        /**
183         * The monitored words are defined in this parameter.
184         */
185        public Parameter monitoredErrorWords;
186
187        /**
188         * The standard output stream of the execution is sent out through this
189         * port.
190         */
191        public TypedIOPort standardOutput = null;
192
193        /**
194         * The standard error stream of the execution is sent out through this port.
195         */
196        public TypedIOPort standardError = null;
197
198        /**
199         * The exit code of the execution is sent out through this port.
200         */
201        public TypedIOPort exitCode = null;
202
203        /**
204         * The trigger that will enable the execution of this actor is received
205         * through this port.
206         */
207        public TypedIOPort inputTrigger = null;
208
209        // /////////////////////////////////////////////////////////////////
210        // // functional variables ////
211
212        // /////////////////////////////////////////////////////////////////
213        // // public methods ////
214
215        /**
216         * Invoke the external program. The monitored words are being monitored
217         * during the execution.
218         * 
219         * @exception IllegalActionException
220         *                If it is thrown by the send() method sending out the
221         *                token.
222         */
223        public void fire() throws IllegalActionException {
224                if (inputTrigger.hasToken(0)) {
225                        super.fire();
226                        try {
227                                // get command file name
228                                String commandFileName = ((StringToken) command.getToken())
229                                                .stringValue();
230                                if (commandFileName.length() != 0) {
231                                        _jrun.setCommand(commandFileName);
232                                } else {
233                                        System.out.println("Command is not defined!");
234                                        throw new IllegalActionException("Command is not defined");
235                                }
236
237                                // get standard output file name
238                                String outFileName = ((StringToken) outputFile.getToken())
239                                                .stringValue();
240                                if (outFileName.length() != 0) {
241                                        _jrun.setOutputFileName(outFileName);
242                                }
243
244                                // get standard error file name
245                                String errorFileName = ((StringToken) errorFile.getToken())
246                                                .stringValue();
247                                if (errorFileName.length() != 0) {
248                                        _jrun.setErrorFileName(errorFileName);
249                                }
250
251                                // get arguments string
252                                String argumentsString = ((StringToken) arguments.getToken())
253                                                .stringValue();
254                                if (argumentsString.length() != 0) {
255                                        _jrun.setArguments(argumentsString);
256                                }
257
258                                // get working directory
259                                String workingDirName = ((StringToken) workingDirectory
260                                                .getToken()).stringValue();
261                                if (workingDirName.length() != 0) {
262                                        _jrun.setWorkingDirectory(workingDirName);
263                                }
264
265                                // Here we assume all the programs executed by JRunCIPRes must
266                                // be finished to get the final results
267                                // So the JRun is set to wait until the execution is finished
268                                _jrun.setWaitForExecution(true);
269
270                                // check if the user set the monitored error words
271                                String errorWords = ((StringToken) monitoredErrorWords
272                                                .getToken()).stringValue();
273                                if (errorWords.length() != 0) {
274                                        _jrun.setMonitoredErrorWords(errorWords);
275                                        _jrun.addObserver(this); // add this GUIRunCIPRes as an
276                                                                                                // observer
277                                }
278
279                                _jrun.execute(); // invoke the external program
280
281                                exitCode.send(0, new IntToken(_jrun.getExitCode())); // send out
282                                                                                                                                                // the
283                                                                                                                                                // exit
284                                                                                                                                                // code
285                                                                                                                                                // token
286
287                                if (_jrun.getExitCode() != 0) {
288                                        _terminateWorkflow = true; // set the flag to terminate the
289                                                                                                // workflow since errors are
290                                                                                                // reported
291                                } else {
292                                        standardOutput.send(0, new StringToken(_jrun
293                                                        .getStandardOutput()));
294                                        standardError.send(0, new StringToken(_jrun
295                                                        .getStandardError()));
296
297                                }
298                        } catch (Exception e) {
299                                e.printStackTrace();
300                        }
301                }
302        }
303
304        /**
305         * Observer function update jRunObj is the jRun object that runs the program
306         * obj is the String message that jRun object send back. This string message
307         * is the standard output/error line that contains the monitored words
308         */
309        public void update(Observable jRunObj, Object obj) {
310                String outputMessage = (String) obj;
311                int stop = 0; // For default, we assume the execution should be stopped
312                                                // if any error appears
313
314                try {
315                        // ask the user if we should abort the program execution
316                        stop = JOptionPane.showConfirmDialog(null, // parent component
317                                        "The program execution reported the following error message.\n"
318                                                        + // Message
319                                                        "Would you like to stop the execution?\n" + // Message
320                                                        outputMessage + "\n", // Error message for Standard
321                                                                                                        // output/error
322                                        "Error Reported", // Title
323                                        JOptionPane.YES_NO_OPTION, // Option type
324                                        JOptionPane.ERROR_MESSAGE); // Message type
325                } catch (Exception e) {
326                        e.printStackTrace();
327                }
328
329                if (stop == JOptionPane.YES_OPTION) {
330                        ((JRun) jRunObj).stopExecution(); // stop the execution
331                }
332
333        }
334
335        /**
336         * Post fire the actor. Return false to indicated that the process has
337         * finished. If it returns true, the process will continue indefinitely.
338         */
339        public boolean postfire() throws IllegalActionException {
340                if (_terminateWorkflow) {
341                        Nameable container = getContainer();
342                        if (container instanceof CompositeActor) {
343                                Manager manager = ((CompositeActor) container).getManager();
344                                if (manager != null) {
345                                        manager.finish(); // stop the workflow
346                                } else {
347                                        throw new IllegalActionException(this,
348                                                        "Cannot terminate the workflow without a Manager");
349                                }
350                        } else {
351                                throw new IllegalActionException(this,
352                                                "Cannot terminate the workflow without a container that is a CompositeActor");
353                        }
354                }
355                return !_terminateWorkflow;
356        }
357
358        // /////////////////////////////////////////////////////////////////
359        // // private variables ////
360        /**
361         * JRun object to invoke external programs
362         */
363        private JRun _jrun = new JRun();
364
365        /**
366         * a flag to terminate the whole workflow since the execution of program got
367         * error messages
368         */
369        private boolean _terminateWorkflow = false;
370}