001/*
002 * Copyright (c) 2005-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.resurgence.actor;
031
032// Ptolemy packages
033import java.io.DataInputStream;
034import java.io.IOException;
035
036import org.sdm.spa.CommandLine;
037
038import ptolemy.actor.TypedIOPort;
039import ptolemy.data.BooleanToken;
040import ptolemy.data.IntToken;
041import ptolemy.data.LongToken;
042import ptolemy.data.StringToken;
043import ptolemy.data.Token;
044import ptolemy.data.expr.Parameter;
045import ptolemy.data.expr.StringParameter;
046import ptolemy.data.type.BaseType;
047import ptolemy.kernel.CompositeEntity;
048import ptolemy.kernel.util.Attribute;
049import ptolemy.kernel.util.IllegalActionException;
050import ptolemy.kernel.util.NameDuplicationException;
051import ptolemy.kernel.util.Settable;
052
053//////////////////////////////////////////////////////////////////////////
054//// StatusChecker
055/**
056 * FIXME: Add documents here. FIXME: Make it more generic... FIXME: If we
057 * decided to extend from CommandLine, then we should re-code CommandLine for
058 * code reuse.
059 * 
060 * @author Yang Zhao
061 * @author Ilkay Altintas
062 * @author Wibke Sudholt, University and ETH Zurich, November 2004
063 * @version $Id: StatusChecker.java 24234 2010-05-06 05:21:26Z welker $
064 */
065
066public class StatusChecker extends CommandLine {
067
068        /**
069         * Construct a StatusChecker actor with the given container and name.
070         * 
071         * @param container
072         *            The container.
073         * @param name
074         *            The name of this actor.
075         * @exception IllegalActionException
076         *                If the actor cannot be contained by the proposed
077         *                container.
078         * @exception NameDuplicationException
079         *                If the container already has an actor with this name.
080         */
081        public StatusChecker(CompositeEntity container, String name)
082                        throws IllegalActionException, NameDuplicationException {
083                super(container, name);
084
085                iterationOutput = new TypedIOPort(this, "iterationOutput", false, true);
086                iterationOutput.setTypeEquals(BaseType.STRING);
087                new Attribute(iterationOutput, "_showName");
088
089                // Hide the outputLineByLine parameter.
090                outputLineByLine.setVisibility(Settable.EXPERT);
091
092                sleepTime = new Parameter(this, "sleepTime", new LongToken(0));
093                sleepTime.setTypeEquals(BaseType.LONG);
094
095                checkCondition = new StringParameter(this, "checkCondition");
096                checkCondition.setTypeEquals(BaseType.STRING);
097
098                maxChecks = new Parameter(this, "maxChecks", new IntToken(1));
099                maxChecks.setTypeEquals(BaseType.INT);
100        }
101
102        // /////////////////////////////////////////////////////////////////
103        // // Public ports and parameters ////
104
105        /**
106         * The output in each iteration.
107         */
108        public TypedIOPort iterationOutput;
109
110        /**
111         * The sleep time amount, in milliseconds, between two checks. This
112         * parameter must contain a LongToken. The default value of this parameter
113         * is 0, meaning that this actor will not sleep between checks.
114         */
115        public Parameter sleepTime;
116
117        /**
118         * A regular expression for which to check in the output.
119         */
120        public StringParameter checkCondition;
121
122        /**
123         * The max amount of checks. This parameter is type of int. The default
124         * value of this parameter is -1, meaning that this actor will keep on
125         * checking until the condition is satisfied.
126         */
127        public Parameter maxChecks;
128
129        // /////////////////////////////////////////////////////////////////
130        // // public methods ////
131
132        /**
133         * If the specified attribute is <i>sleepTime</i> or <i>maxChecks</i>, then
134         * get the value of them.
135         * 
136         * @param attribute
137         *            The attribute that has changed.
138         * @exception IllegalActionException.
139         */
140        public void attributeChanged(Attribute attribute)
141                        throws IllegalActionException {
142                // FIXME: should check whether the value is valid...
143                if (attribute == sleepTime) {
144                        _sleepTime = ((LongToken) sleepTime.getToken()).longValue();
145                } else if (attribute == maxChecks) {
146                        _maxChecks = ((IntToken) maxChecks.getToken()).intValue();
147                        if (_maxChecks < 0)
148                                _maxChecks = Integer.MAX_VALUE;
149                } else if (attribute == checkCondition) {
150                        _condition = checkCondition.stringValue();
151                } else {
152                        super.attributeChanged(attribute);
153                }
154        }
155
156        /**
157         * ... Send the exitCode and outputFileHandle(optional) to the result port.
158         * 
159         * @exception IllegalActionException
160         *                If there is no director.
161         */
162        public void fire() throws IllegalActionException {
163
164                _lineFlag = ((BooleanToken) outputLineByLine.getToken()).booleanValue();
165                _debug("<TRIGGER_FLAG>" + _lineFlag + "</TRIGGER_FLAG>");
166                // simply consume the trigger token if there is some.
167                if (trigger.getWidth() > 0) {
168                        if (trigger.hasToken(0)) {
169                                trigger.get(0);
170                                _debug("consume the tokne at the trigger port.");
171                        }
172                }
173
174                // Get the main command from the command parameter.
175                command.update();
176                _commandStr = ((StringToken) command.getToken()).stringValue();
177                _createCommand();
178
179                int i = 0;
180                boolean isSatisfied = false;
181                String result = " ";
182
183                while (!isSatisfied && i < _maxChecks) {
184                        result = _executeCommand();
185                        _debug("the execution result is : " + result);
186                        if (result.matches(_condition))
187                                isSatisfied = true;
188                        if (!_lineFlag)
189                                iterationOutput.broadcast(new StringToken(result));
190                        i++;
191                        try {
192                                if (_debugging)
193                                        _debug(getName() + ": Wait for " + _sleepTime
194                                                        + " milliseconds.");
195                                Thread.sleep(_sleepTime);
196                        } catch (InterruptedException e) {
197                                // Ignore...
198                        }
199                }
200
201                if (!_lineFlag)
202                        output.broadcast(new StringToken(result));
203                if (isSatisfied) {
204                        exitCode.broadcast(new BooleanToken(true));
205                } else {
206                        exitCode.broadcast(new BooleanToken(false));
207                }
208        } // end-of-fire
209
210        // FIXME: The reason I keep this method here is because the redefined
211        // private variable from the CommandLine actor. This should be changed...
212        public int getSystemProps() {
213                // Get OS name
214                String osName = System.getProperty("os.name");
215                _debug("<OS>" + osName + "</OS>");
216                if (osName.equals("Windows NT") || osName.equals("Windows XP")
217                                || osName.equals("Windows 2000")) {
218                        _commandArr[0] = "cmd.exe";
219                        _commandArr[1] = "/C";
220                        _charsToSkip = 6;
221                        return 2;
222                } else if (osName.equals("Windows 95")) {
223                        _commandArr[0] = "command.com";
224                        _commandArr[1] = "/C";
225                        _charsToSkip = 6;
226                        return 2;
227                } else if (osName.equals("Linux")) {
228                        _commandArr[0] = "/bin/sh";
229                        _commandArr[1] = "-c";
230                        _charsToSkip = 5;
231                        return 2;
232                } else if (osName.equals("Mac OS X")) {
233                        _commandArr[0] = "/bin/sh";
234                        _commandArr[1] = "-c";
235                        _charsToSkip = 5;
236                        return 2;
237                } else {
238                        return 0;
239                }
240        } // end-of-getSystemProps
241
242        private String _executeCommand() throws IllegalActionException {
243                int commandCount = getSystemProps();
244                _commandArr[commandCount] = _commandStr;
245                _debug("<COMMAND>" + _commandArr[commandCount] + "</COMMAND>");
246                // EXECUTION OF THE GENERATED COMMAND.
247                _debug("Executing the command...");
248                try {
249
250                        Runtime rt = Runtime.getRuntime();
251                        Process proc = rt.exec(_commandArr);
252
253                        DataInputStream inStream = new DataInputStream(proc
254                                        .getInputStream());
255                        String result; // Temp for each line of output.
256                        StringBuffer outBuff = new StringBuffer("");
257                        try {
258                                while ((result = inStream.readLine()) != null) {
259                                        _debug(result);
260                                        if (_lineFlag) {
261                                                output.broadcast(new StringToken(result.toString()));
262                                        } else {
263                                                outBuff.append(result + "\n");
264                                        }
265                                }
266                                return outBuff.toString();
267                        } catch (IOException ioe) {
268                                throw new IllegalActionException(this,
269                                                "<IOException> when reading the input: " + ioe
270                                                                + "</IOException>");
271                        }
272                } catch (Exception ex) {
273                        throw new IllegalActionException(this,
274                                        "An exception occured when executing the command: " + ex);
275                }
276        }
277
278        private void _createCommand() throws IllegalActionException {
279                /*
280                 * Consume the input file token if there's one.
281                 */
282                Token tokenFile = null;
283                String value = null;
284                try {
285                        if (infileHandle.getWidth() > 0) {
286                                if (infileHandle.hasToken(0)) {
287                                        tokenFile = infileHandle.get(0);
288                                        _debug("consume the tokne at the trigger port.");
289                                }
290                        }
291
292                        if (tokenFile != null) {
293                                value = new String(tokenFile.toString());
294                                _debug("infileHandle(i) = " + value);
295                                value = value.substring(1, value.length() - 1);
296                                _commandStr += " < " + value;
297                        }
298                } catch (Exception ex) {
299                        throw new IllegalActionException(this, "Input file is null.");
300                }
301
302                /*
303                 * The arguments can only be accepted if there's no input file. So the
304                 * "value" of the infile handle is checked here and arguments are
305                 * consumed only if it is null.
306                 */
307                // if (value == null) {
308                /*
309                 * Create the argument string. Consume data in all the channels an
310                 * combine them.
311                 */
312                String argString = "";
313                value = new String("");
314                int i = 0;
315                int width = arguments.getWidth();
316                for (i = 0; i < width; i++) {
317                        if (arguments.hasToken(i)) {
318                                Token tokenArg = arguments.get(i);
319                                value = tokenArg.toString();
320                                _debug("arguments(i) = " + value);
321                                value = value.substring(1, value.length() - 1);
322
323                                while (value.indexOf("\\\"") != -1) {
324                                        int ind = value.indexOf("\\\"");
325                                        value = value.substring(0, ind)
326                                                        + value.substring(ind + 1, value.length());
327                                        _debug(value);
328                                }
329                                argString += value + " ";
330                                _debug("argString = " + argString);
331                        }
332                }
333                _commandStr += " " + argString;
334                // }
335                /*
336                 * Get the output file path if there's one and add it to the command
337                 * string.
338                 */
339                if (outputFile.asURL() == null) {
340                        _debug("Output file is null.");
341                } else {
342                        String outFilePath = outputFile.asURL().toString();
343                        if (outFilePath.startsWith("file:///")) {
344                                if (_charsToSkip == 6) {
345                                        _charsToSkip = 8;
346                                } else if (_charsToSkip == 5) {
347                                        _charsToSkip = 7;
348                                }
349                        }
350                        outFilePath = outputFile.asURL().toString().substring(_charsToSkip);
351                        _commandStr += " > " + outFilePath;
352                        outfileHandle.broadcast(new StringToken(outFilePath));
353                }
354        }
355
356        // ////////////////////////////////////////////////////////////////////
357        // // private variables ////
358        // The combined command to execute.
359        private String _commandStr = "";
360        private String _commandArr[] = new String[3];
361        private boolean _lineFlag = false;
362        private boolean _triggerFlag = false;
363        private int _charsToSkip = 6;
364        private long _sleepTime = 0;
365        private int _maxChecks = Integer.MAX_VALUE;
366        private String _condition = ".*";
367
368} // end-of-class-StatusChecker