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
032// ptolemy packages
033import java.util.Observable;
034import java.util.Observer;
035
036import javax.swing.JOptionPane;
037
038import org.cipres.helpers.GUIRun;
039
040import ptolemy.actor.CompositeActor;
041import ptolemy.actor.Manager;
042import ptolemy.actor.TypedAtomicActor;
043import ptolemy.actor.TypedIOPort;
044import ptolemy.data.IntToken;
045import ptolemy.data.StringToken;
046import ptolemy.data.expr.FileParameter;
047import ptolemy.data.expr.Parameter;
048import ptolemy.data.type.BaseType;
049import ptolemy.kernel.CompositeEntity;
050import ptolemy.kernel.util.IllegalActionException;
051import ptolemy.kernel.util.NameDuplicationException;
052import ptolemy.kernel.util.Nameable;
053
054//////////////////////////////////////////////////////////////////////////
055//// GUIRunCIPRes
056/**
057 * This actor calls external Cipres applications with GUIGen interfaces.
058 * 
059 * The GUIRunCIPRes actor provides a template to invoke external Cipres
060 * applications. A set of parameters is configured in the actor to collect
061 * application related information. The value of these parameters will be fed to
062 * the GUIRun object when the actor fires. Thus the GUIRun object will invoke
063 * the corresponding application with those values, like the input/output file
064 * names and the working directory. After the execution, if the external program
065 * returns correctly, the standard output of the execution will be sent to the
066 * standard output file, the standard error will be sent to the standard error
067 * file, and the program output file name will be sent to the
068 * outputParameterValue port.
069 * 
070 * The user can also set some words to be monitored in the standard output
071 * stream. The observer/observable design pattern is used here. The GUIRunCIPRes
072 * object is an observer. The GUIRun object is an observable. Whenever the
073 * GUIRun object find any of these monitored words in the standard output steam
074 * of the program execution, it will notify the GUIRunCIPRes objects to promote
075 * the user handling the issue. Usually the GUIRunCIPRes actor will stop the
076 * execution of the program.
077 * 
078 * @author Zhijie Guan
079 * @version $Id: GUIRunCIPRes.java 24234 2010-05-06 05:21:26Z welker $
080 */
081
082public class GUIRunCIPRes extends TypedAtomicActor implements Observer {
083
084        /**
085         * Construct GUIRunCIPRes source with the given container and name.
086         * 
087         * @param container
088         *            The container.
089         * @param name
090         *            The name of this actor.
091         * @exception IllegalActionException
092         *                If the entity cannot be contained by the proposed
093         *                container.
094         * @exception NameDuplicationException
095         *                If the container already has an actor with this name.
096         */
097
098        public GUIRunCIPRes(CompositeEntity container, String name)
099                        throws NameDuplicationException, IllegalActionException {
100                super(container, name);
101
102                // initialize the standard output port
103                standardOutput = new TypedIOPort(this, "standardOutput", false, true);
104                standardOutput.setDisplayName("Standard Output");
105                standardOutput.setTypeEquals(BaseType.STRING);
106
107                // initialize the standard error port
108                standardError = new TypedIOPort(this, "standardError", false, true);
109                standardError.setDisplayName("Standard Error");
110                standardError.setTypeEquals(BaseType.STRING);
111
112                // initialize the exit code port
113                exitCode = new TypedIOPort(this, "exitCode", false, true);
114                exitCode.setDisplayName("Exit Code");
115                exitCode.setTypeEquals(BaseType.STRING);
116
117                // parameters initialization
118                // output parameter value
119                outputParameterValue = new TypedIOPort(this, "outputParameterValue",
120                                false, true);
121                outputParameterValue.setDisplayName("Parameter Value (Output)");
122                outputParameterValue.setTypeEquals(BaseType.STRING);
123
124                // input parameter name
125                inputParameterName = new TypedIOPort(this, "inputParameterName", true,
126                                false);
127                inputParameterName.setDisplayName("Input Parameter Name");
128                inputParameterName.setTypeEquals(BaseType.STRING);
129
130                // input parameter value
131                inputParameterValue = new TypedIOPort(this, "inputParameterValue",
132                                true, false);
133                inputParameterValue.setDisplayName("Input Parameter Value");
134                inputParameterValue.setTypeEquals(BaseType.STRING);
135
136                // command name
137                command = new FileParameter(this, "command");
138                command.setDisplayName("External Command");
139
140                // GUIGen xml file name
141                uiXMLFile = new FileParameter(this, "uiXMLFile");
142                uiXMLFile.setDisplayName("GUIGen XML File");
143
144                // Standard output file
145                outputFile = new FileParameter(this, "outputFile");
146                outputFile.setDisplayName("Standard Output File");
147
148                // Standard error file
149                errorFile = new FileParameter(this, "errorFile");
150                errorFile.setDisplayName("Standard Error File");
151
152                // Working directory
153                workingDirectory = new Parameter(this, "workingDirectory",
154                                new StringToken(""));
155                workingDirectory.setDisplayName("Working Direcotry");
156
157                // parameter's name in the GUIRun. Usually it is "output"
158                parameterForOutput = new Parameter(this, "parameterForOutput",
159                                new StringToken(""));
160                parameterForOutput.setDisplayName("Parameter to Output");
161
162                // words to be monitored
163                monitoredErrorWords = new Parameter(this, "monitoredErrorWords",
164                                new StringToken(""));
165                monitoredErrorWords.setDisplayName("Error Words to be Monitored");
166
167                _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" "
168                                + "width=\"60\" height=\"20\" " + "style=\"fill:white\"/>\n"
169                                + "</svg>\n");
170
171        }
172
173        // /////////////////////////////////////////////////////////////////
174        // // ports and parameters ////
175
176        /**
177         * The command path and name are defined in this parameter.
178         */
179        public FileParameter command;
180
181        /**
182         * The GUIGen xml file's path and name are defined in this parameter.
183         */
184        public FileParameter uiXMLFile;
185
186        /**
187         * The standard output file's path and name are defined in this parameter.
188         */
189        public FileParameter outputFile;
190
191        /**
192         * The standard error file's path and name are defined in this parameter.
193         */
194        public FileParameter errorFile;
195
196        /**
197         * The workfing directory of the external program is defined in this
198         * parameter.
199         */
200        public Parameter workingDirectory;
201
202        /**
203         * The GUIRun parameter to be send out is defined in this parameter. Usually
204         * this GUIRun parameter is "outfile".
205         */
206        public Parameter parameterForOutput;
207
208        /**
209         * The monitored words are defined in this parameter.
210         */
211        public Parameter monitoredErrorWords;
212
213        /**
214         * The standard output stream of the execution is sent out through this
215         * port.
216         */
217        public TypedIOPort standardOutput = null;
218
219        /**
220         * The standard error stream of the execution is sent out through this port.
221         */
222        public TypedIOPort standardError = null;
223
224        /**
225         * The exit code of the execution is sent out through this port.
226         */
227        public TypedIOPort exitCode = null;
228
229        /**
230         * The output parameter value, usually the name of the output file of the
231         * execution, is sent out throught this port.
232         */
233        public TypedIOPort outputParameterValue = null;
234
235        /**
236         * The input parameter name (usually "infile") is sent through this port.
237         */
238        public TypedIOPort inputParameterName = null;
239
240        /**
241         * The input parameter value (usually the name of the input file of the
242         * execution) is sent through this port.
243         */
244        public TypedIOPort inputParameterValue = null;
245
246        // /////////////////////////////////////////////////////////////////
247        // // functional variables ////
248
249        // /////////////////////////////////////////////////////////////////
250        // // public methods ////
251
252        /**
253         * Invoke the external program by setting the parameters of the execution
254         * using GUIGen. The monitored words are being monitored during the
255         * execution.
256         * 
257         * @exception IllegalActionException
258         *                If it is thrown by the send() method sending out the
259         *                token.
260         */
261        public void fire() throws IllegalActionException {
262                if (inputParameterValue.hasToken(0) && inputParameterName.hasToken(0)) {
263                        super.fire();
264                        try {
265
266                                // get command
267                                String commandFileName = ((StringToken) command.getToken())
268                                                .stringValue();
269                                if (commandFileName.length() != 0) {
270                                        _grun.setCommand(commandFileName);
271                                } else {
272                                        System.out.println("Command is not defined!");
273                                        throw new IllegalActionException("Command is not defined");
274                                }
275
276                                // get GUIGen xml file
277                                String uiXMLFileName = ((StringToken) uiXMLFile.getToken())
278                                                .stringValue();
279                                if (uiXMLFileName.length() != 0) {
280                                        _grun.setUIXMLFile(uiXMLFileName);
281                                }
282
283                                // get standard output file name
284                                String outFileName = ((StringToken) outputFile.getToken())
285                                                .stringValue();
286                                if (outFileName.length() != 0) {
287                                        _grun.setOutputFileName(outFileName);
288                                }
289
290                                // get standard error file name
291                                String errorFileName = ((StringToken) errorFile.getToken())
292                                                .stringValue();
293                                if (errorFileName.length() != 0) {
294                                        _grun.setErrorFileName(errorFileName);
295                                }
296
297                                // get the working directory
298                                String workingDirName = ((StringToken) workingDirectory
299                                                .getToken()).stringValue();
300                                if (workingDirName.length() != 0) {
301                                        _grun.setWorkingDirectory(workingDirName);
302                                }
303
304                                // set the input file to the GUIRun object
305                                _grun
306                                                .setArgumentValue(((StringToken) inputParameterName
307                                                                .get(0)).stringValue(),
308                                                                ((StringToken) inputParameterValue.get(0))
309                                                                                .stringValue());
310
311                                // get arguments information with GUIGen interface
312                                _grun.setArgumentsWithGUIGen();
313
314                                // Here we assume all the programs executed by GUIRunCIPRes must
315                                // be finished to get the final results
316                                // So the GUIRun is set to wait until the execution is finished
317                                _grun.setWaitForExecution(true);
318
319                                // check if the user set the monitored error words
320                                String errorWords = ((StringToken) monitoredErrorWords
321                                                .getToken()).stringValue();
322                                if (errorWords.length() != 0) {
323                                        _grun.setMonitoredErrorWords(errorWords);
324                                        _grun.addObserver(this); // add this GUIRunCIPRes as an
325                                                                                                // observer
326                                }
327
328                                _grun.execute(); // invoke the external program
329
330                                String outputFileName = _grun
331                                                .getArgumentValue(((StringToken) parameterForOutput
332                                                                .getToken()).stringValue());
333
334                                // call postExecutionProcess to process the output data after
335                                // the execution
336                                postExecutionProcess(outputFileName);
337
338                                exitCode.send(0, new IntToken(_grun.getExitCode())); // send out
339                                                                                                                                                // the
340                                                                                                                                                // exit
341                                                                                                                                                // code
342                                                                                                                                                // token
343
344                                if (_grun.getExitCode() != 0) {
345                                        _terminateWorkflow = true; // set the flag to terminate the
346                                                                                                // workflow since errors are
347                                                                                                // reported
348                                } else {
349                                        // send out standard output/error and output file
350                                        standardOutput.send(0, new StringToken(_grun
351                                                        .getStandardOutput()));
352                                        standardError.send(0, new StringToken(_grun
353                                                        .getStandardError()));
354
355                                        outputParameterValue.send(0,
356                                                        new StringToken(outputFileName));
357                                }
358                        } catch (Exception e) {
359                                e.printStackTrace();
360                        }
361                }
362        }
363
364        /**
365         * This function processes the output data after the execution. Usually
366         * subclasses of the GUIRunCIPRes class will implement this function.
367         * 
368         * @param outputFileName
369         *            is the output file name of the execution
370         */
371        public void postExecutionProcess(String outputFileName) {
372        }
373
374        /**
375         * Observer function update guiRunObj is the GUIRun object that runs the
376         * program obj is the String message that GUIRun object send back. This
377         * string message is the standard output/error line that contains the
378         * monitored words
379         */
380        public void update(Observable guiRunObj, Object obj) {
381                String outputMessage = (String) obj;
382                int stop = 0; // For default, we assume the execution should be stopped
383                                                // if any error appears
384
385                try {
386                        // ask the user if we should abort the program execution
387                        stop = JOptionPane.showConfirmDialog(null, // parent component
388                                        "The program execution reported the following error message.\n"
389                                                        + // Message
390                                                        "Would you like to stop the execution?\n" + // Message
391                                                        outputMessage + "\n", // Error message for Standard
392                                                                                                        // output/error
393                                        "Error Reported", // Title
394                                        JOptionPane.YES_NO_OPTION, // Option type
395                                        JOptionPane.ERROR_MESSAGE); // Message type
396                } catch (Exception e) {
397                        e.printStackTrace();
398                }
399
400                if (stop == JOptionPane.YES_OPTION) {
401                        ((GUIRun) guiRunObj).stopExecution(); // stop the execution
402                }
403
404        }
405
406        /**
407         * Post fire the actor. Return false to indicated that the process has
408         * finished. If it returns true, the process will continue indefinitely.
409         */
410        public boolean postfire() throws IllegalActionException {
411                if (_terminateWorkflow) { // try to kill the execution
412                        Nameable container = getContainer();
413                        if (container instanceof CompositeActor) {
414                                Manager manager = ((CompositeActor) container).getManager();
415                                if (manager != null) {
416                                        manager.finish(); // stop the workflow
417                                } else {
418                                        throw new IllegalActionException(this,
419                                                        "Cannot terminate the workflow without a Manager");
420                                }
421                        } else {
422                                throw new IllegalActionException(this,
423                                                "Cannot terminate the workflow without a container that is a CompositeActor");
424                        }
425                }
426                return !_terminateWorkflow;
427        }
428
429        // /////////////////////////////////////////////////////////////////
430        // // private variables ////
431        /**
432         * GUIRun object to invoke external programs.
433         */
434        protected GUIRun _grun = new GUIRun("External Program");
435
436        /**
437         * a flag to terminate the whole workflow if the execution of program got
438         * error messages
439         */
440        private boolean _terminateWorkflow = false;
441}