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.sdm.spa; 031 032// Ptolemy packages 033import java.io.DataInputStream; 034import java.io.IOException; 035import java.util.Iterator; 036import java.util.List; 037 038import ptolemy.actor.IOPort; 039import ptolemy.actor.TypedAtomicActor; 040import ptolemy.actor.TypedIOPort; 041import ptolemy.actor.parameters.PortParameter; 042import ptolemy.data.BooleanToken; 043import ptolemy.data.StringToken; 044import ptolemy.data.Token; 045import ptolemy.data.expr.FileParameter; 046import ptolemy.data.expr.Parameter; 047import ptolemy.data.expr.SingletonParameter; 048import ptolemy.data.type.BaseType; 049import ptolemy.kernel.CompositeEntity; 050import ptolemy.kernel.util.Attribute; 051import ptolemy.kernel.util.IllegalActionException; 052import ptolemy.kernel.util.NameDuplicationException; 053 054////////////////////////////////////////////////////////////////////////// 055//// CommandLine 056/** 057 * Given a command string, the <I>CommandLine</I> actor executes it using the 058 * java Runtime class. 059 * <p> 060 * <b>SUPPORTED COMMAND TYPES:</b> 061 * <ul> 062 * <li>command 063 * <li>command < infile > outfile 064 * <li>command > outfile 065 * <li>command < infile 066 * <li>command [arg1..argn] > outfile 067 * <li>command [arg1..argn] 068 * <li>command [arg1..argn] < infile > outfile 069 * <li>command1 | command 2 <I>(<B>Warning</B>: This type of commands doesn't 070 * give the output of all the commands. Instead it outputs only the result of 071 * the last one.)</I> 072 * </ul> 073 * <p> 074 * <b>Example commands:</b> 075 * <ul> 076 * <li>C:/Program Files/Internet Explorer/IEXPLORE.EXE <br> 077 * To generate this command, just double click on the actor and type this in the 078 * <i>command</i> parameter field. 079 * <li>C:/cygwin/bin/perl.exe c:/project/kepler/test/workflows/example.pl > 080 * c:/project/kepler/test/workflows/example.out 081 * <li>C:/cygwin/bin/dir.exe > dirTemp.txt 082 * </ul> 083 * 084 * @author Ilkay Altintas 085 * @version $Id: CommandLine.java 24234 2010-05-06 05:21:26Z welker $ 086 * @category.name external execution 087 * @category.name local 088 */ 089 090public class CommandLine extends TypedAtomicActor { 091 092 /** 093 * Construct a CommandLine actor with the given container and name. 094 * 095 * @param container 096 * The container. 097 * @param name 098 * The name of this actor. 099 * @exception IllegalActionException 100 * If the actor cannot be contained by the proposed 101 * container. 102 * @exception NameDuplicationException 103 * If the container already has an actor with this name. 104 */ 105 public CommandLine(CompositeEntity container, String name) 106 throws IllegalActionException, NameDuplicationException { 107 super(container, name); 108 109 // Uncomment the next line to see debugging statements 110 // addDebugListener(new ptolemy.kernel.util.StreamListener()); 111 112 // Construct parameters. 113 command = new PortParameter(this, "command"); 114 115 // command.setExpression("Please type your command here..."); 116 outputFile = new FileParameter(this, "outputFile"); 117 // Construct input ports. 118 arguments = new TypedIOPort(this, "arguments", true, false); 119 arguments.setMultiport(true); 120 infileHandle = new TypedIOPort(this, "infileHandle", true, false); 121 infileHandle.setTypeEquals(BaseType.STRING); 122 trigger = new TypedIOPort(this, "trigger", true, false); 123 new Attribute(arguments, "_showName"); 124 new Attribute(infileHandle, "_showName"); 125 new Attribute(command, "_showName"); 126 // Attribute hide = new SingletonAttribute(trigger, "_hide"); 127 // hide.setPersistent(false); 128 hide = new SingletonParameter(trigger, "_hide"); // DFH 129 hide.setToken(BooleanToken.TRUE); // DFH 130 // Construct output ports. 131 outfileHandle = new TypedIOPort(this, "outfileHandle", false, true); 132 outfileHandle.setTypeEquals(BaseType.STRING); 133 output = new TypedIOPort(this, "output", false, true); 134 output.setTypeEquals(BaseType.STRING); 135 exitCode = new TypedIOPort(this, "exitCode", false, true); 136 exitCode.setTypeEquals(BaseType.BOOLEAN); 137 new Attribute(output, "_showName"); 138 new Attribute(outfileHandle, "_showName"); 139 new Attribute(exitCode, "_showName"); 140 141 // Set the trigger Flag. 142 outputLineByLine = new Parameter(this, "outputLineByLine", 143 new BooleanToken(false)); 144 outputLineByLine.setTypeEquals(BaseType.BOOLEAN); 145 146 hasTrigger = new Parameter(this, "hasTrigger", new BooleanToken(false)); 147 hasTrigger.setTypeEquals(BaseType.BOOLEAN); 148 149 _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" " 150 + "width=\"60\" height=\"30\" " + "style=\"fill:white\"/>\n" 151 + "<text x=\"20\" y=\"25\" " 152 + "style=\"font-size:30; fill:blue; font-family:SansSerif\">" 153 + "$</text>\n" + "</svg>\n"); 154 155 } // constructor 156 157 // //////////////// Public ports and parameters /////////////////////// 158 159 public SingletonParameter hide; 160 /** 161 * @entity.description The command to execute. <BR> 162 * <I>FIX ME: </I>The style of the command will be noted 163 * here. 164 */ 165 public PortParameter command; 166 167 /** 168 * @entity.description Needs to be filled in if the user wants the command to 169 * output to a file. 170 */ 171 public FileParameter outputFile; 172 173 /** 174 * @entity.description The arguments to the command. Implemented as a 175 * multi/input port to support more than one argument. 176 * It concatanates the inputs in all the channels. <BR> 177 * <I>If there is an input file, this port can be 178 * empty.</I> 179 */ 180 public TypedIOPort arguments; 181 182 /** 183 * @entity.description This is an optional port. Used if the file accepts an 184 * input file instead of a list of arguments. 185 */ 186 public TypedIOPort infileHandle; 187 188 /** 189 * @entity.description The trigger port. The type of this port is undeclared, 190 * meaning that it will resolve to any data type. 191 */ 192 public TypedIOPort trigger; 193 194 /** 195 * @entity.description A string that forwards the outputFile parameter if it 196 * exists. 197 */ 198 public TypedIOPort outfileHandle; 199 /** 200 * @entity.description The result stream of the command. 201 */ 202 public TypedIOPort output; 203 204 /** 205 * @entity.description Exit code will be 1 if the command executes 206 * successfully. 207 */ 208 public TypedIOPort exitCode; 209 210 /** 211 * @entity.description If selected, broadcasts the output of the command 212 * line by line. 213 */ 214 public Parameter outputLineByLine; 215 216 /** 217 * @entity.description Unhide the trigger port when this parameter is true. 218 * This Parameter is type of boolean. <BR> 219 * <b>NOTE: </b>in fact, user can use the port 220 * configuration window to hide or unhide a port. This 221 * paremeter is here to provide a more intuitive 222 * interface for this actor. 223 */ 224 public Parameter hasTrigger; 225 226 // ///////////////////////////////////////////////////////////////// 227 // // public methods //// 228 229 /** 230 * If the specified attribute is <i>showTriggerPort</i>, then get the value 231 * of it and re-render the trigger port. If it is true, show the trigger 232 * port; if it is false, hide the trigger port. 233 * 234 * @param attribute 235 * The attribute that has changed. 236 * @exception IllegalActionException. 237 */ 238 public void attributeChanged(Attribute attribute) 239 throws IllegalActionException { 240 if (attribute == hasTrigger) { 241 _triggerFlag = ((BooleanToken) hasTrigger.getToken()) 242 .booleanValue(); 243 _debug("<TRIGGER_FLAG>" + _triggerFlag + "</TRIGGER_FLAG>"); 244 if (_triggerFlag) { 245 try { 246 trigger.setContainer(this); 247 // new Attribute(trigger, "_showName"); 248 hide.setToken(BooleanToken.FALSE); // DFH 249 } catch (NameDuplicationException ndex) { 250 _debug("111: " + ndex.getMessage()); 251 } 252 } else { 253 List inPortList = this.inputPortList(); 254 Iterator ports = inPortList.iterator(); 255 while (ports.hasNext()) { 256 IOPort p = (IOPort) ports.next(); 257 if (p.isInput()) { 258 try { 259 if (p.getName().equals("trigger")) { 260 // new Attribute(trigger, "_hideName"); //DFH 261 // p.setContainer(null); //DFH 262 hide.setToken(BooleanToken.TRUE); // DFH 263 } 264 } catch (Exception e) { 265 throw new IllegalActionException(this, e 266 .getMessage()); 267 } 268 } 269 }// while 270 }// else 271 } else { 272 super.attributeChanged(attribute); 273 } 274 } 275 276 /** 277 * ... Send the exitCode and outputFileHandle(optional) to the result port. 278 * 279 * @exception IllegalActionException 280 * If there is no director. 281 */ 282 public void fire() throws IllegalActionException { 283 284 _lineFlag = ((BooleanToken) outputLineByLine.getToken()).booleanValue(); 285 _debug("<TRIGGER_FLAG>" + _lineFlag + "</TRIGGER_FLAG>"); 286 287 // Get the main command from the command parameter. 288 // _commandStr = ((StringToken)command.getToken()).stringValue(); 289 command.update(); 290 String _commandStr = ((StringToken) command.getToken()).stringValue(); 291 292 // simply consume the trigger token if there is some. 293 if (_triggerFlag) { 294 if (trigger.getWidth() > 0) { 295 if (trigger.hasToken(0)) { 296 trigger.get(0); 297 _debug("consume the token at the trigger port."); 298 } 299 } 300 } 301 302 /* 303 * Consume the input file token if there's one. 304 */ 305 Token tokenFile = null; 306 String value = null; 307 try { 308 tokenFile = infileHandle.get(0); 309 if (tokenFile != null) { 310 value = new String(tokenFile.toString()); 311 _debug("infileHandle(i) = " + value); 312 value = value.substring(1, value.length() - 1); 313 _commandStr += " < " + value; 314 } 315 } catch (Exception ex) { 316 _debug("Input file is null."); 317 } 318 319 /* 320 * The arguments can only be accepted if there's no input file. So the 321 * "value" of the infile handle is checked here and arguments are 322 * consumed only if it is null. 323 */ 324 // if (value == null) { 325 /* 326 * Create the argument string. Consume data in all the channels an 327 * combine them. 328 */ 329 String argString = ""; 330 value = new String(""); 331 int i = 0; 332 int width = arguments.getWidth(); 333 for (i = 0; i < width; i++) { 334 if (arguments.hasToken(i)) { 335 Token tokenArg = arguments.get(i); 336 value = tokenArg.toString(); 337 _debug("arguments(i) = " + value); 338 value = value.substring(1, value.length() - 1); 339 340 while (value.indexOf("\\\"") != -1) { 341 int ind = value.indexOf("\\\""); 342 value = value.substring(0, ind) 343 + value.substring(ind + 1, value.length()); 344 _debug(value); 345 } 346 argString += value + " "; 347 _debug("argString = " + argString); 348 } 349 } 350 _commandStr += " " + argString; 351 // } 352 353 int commandCount = getSystemProps(); 354 355 /* 356 * Get the output file path if there's one and add it to the command 357 * string. 358 */ 359 if (outputFile.asURL() == null) { 360 _debug("Output file is null."); 361 } else { 362 String outFilePath = outputFile.asURL().toString(); 363 if (outFilePath.startsWith("file:///")) { 364 if (_charsToSkip == 6) { 365 _charsToSkip = 8; 366 } else if (_charsToSkip == 5) { 367 _charsToSkip = 7; 368 } 369 } 370 outFilePath = outputFile.asURL().toString().substring(_charsToSkip); 371 _commandStr += " > " + outFilePath; 372 outfileHandle.broadcast(new StringToken(outFilePath)); 373 } 374 375 _commandArr[commandCount] = _commandStr; 376 _debug("<COMMAND>" + _commandArr[commandCount] + "</COMMAND>"); 377 // EXECUTION OF THE GENERATED COMMAND. 378 _debug("Executing the command..."); 379 try { 380 381 Runtime rt = Runtime.getRuntime(); 382 Process proc = rt.exec(_commandArr); 383 384 DataInputStream inStream = new DataInputStream(proc 385 .getInputStream()); 386 String result; // Temp for each line of output. 387 StringBuffer outBuff = new StringBuffer(""); 388 try { 389 while ((result = inStream.readLine()) != null) { 390 _debug(result); 391 if (_lineFlag) { 392 output.broadcast(new StringToken(result.toString())); 393 } else { 394 outBuff.append(result + "\n"); 395 } 396 } 397 } catch (IOException ioe) { 398 _debug("<IOException> when reading the input: " + ioe 399 + "</IOException>"); 400 } 401 if (!_lineFlag) { 402 output.broadcast(new StringToken(outBuff.toString())); 403 } 404 // any error? 405 int exitVal = proc.waitFor(); 406 _debug("ExitValue: " + exitVal); 407 // Broadcast the exit status. 408 if (exitVal == 0) { 409 exitCode.broadcast(new BooleanToken(true)); 410 } else { 411 exitCode.broadcast(new BooleanToken(false)); 412 } 413 } catch (Exception ex) { 414 _debug("<EXCEPTION> An exception occured when executing the command. " 415 + " \n\t Exception: " + ex + "\n</EXCEPTION>"); 416 } 417 } // end-of-fire 418 419 public int getSystemProps() { 420 // Get OS name 421 String osName = System.getProperty("os.name"); 422 _debug("<OS>" + osName + "</OS>"); 423 if (osName.equals("Windows NT") || osName.equals("Windows XP") 424 || osName.equals("Windows 2000")) { 425 _commandArr[0] = "cmd.exe"; 426 _commandArr[1] = "/C"; 427 _charsToSkip = 6; 428 return 2; 429 } else if (osName.equals("Windows 95")) { 430 _commandArr[0] = "command.com"; 431 _commandArr[1] = "/C"; 432 _charsToSkip = 6; 433 return 2; 434 } 435 /* 436 * else if (osName.equals("Linux")) { _commandArr[0] = "/bin/sh"; 437 * _commandArr[1] = "-c"; _charsToSkip = 5; return 2; } else if 438 * (osName.equals("Mac OS X")) { _commandArr[0] = "/bin/sh"; 439 * _commandArr[1] = "-c"; _charsToSkip = 5; return 2; } 440 */ 441 else { 442 _commandArr[0] = "/bin/sh"; 443 _commandArr[1] = "-c"; 444 _charsToSkip = 5; 445 return 2; 446 /* return 0; */ 447 } 448 } // end-of-getSystemProps 449 450 // //////////////////////////////////////////////////////////////////// 451 // // private variables //// 452 453 // The combined command to execute. 454 private String _commandStr = ""; 455 private String _commandArr[] = new String[3]; 456 private boolean _lineFlag = false; 457 private boolean _triggerFlag = false; 458 private int _charsToSkip = 6; 459 460} // end-of-class-CommandLine