001/* An application that executes models specified on the command line. 002 003 Copyright (c) 1999-2018 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.actor.gui; 029 030import java.net.URL; 031import java.util.LinkedList; 032import java.util.List; 033 034import ptolemy.actor.Manager; 035import ptolemy.kernel.util.IllegalActionException; 036import ptolemy.kernel.util.KernelException; 037import ptolemy.util.MessageHandler; 038import ptolemy.util.StringUtilities; 039 040/////////////////////////////////////////////////////////////////// 041//// PtExecuteApplication 042 043/** 044 This application executes Ptolemy II models specified on the 045 command line. 046 <p> 047 The exact facilities that are available are determined by an optional 048 command line argument that names a directory in ptolemy/configs that 049 contains a configuration.xml file. For example, if we call vergil 050 -ptiny, then we will use ptolemy/configs/ptiny/configuration.xml and 051 ptolemy/configs/ptiny/intro.htm. The default configuration is 052 ptolemy/configs/runConfiguration.xml, which is loaded before any 053 other command-line arguments are processed. 054 055 <p>This application also takes an optional command line argument pair 056 <code>-conf <i>configurationFile.xml</i></code> that names a configuration 057 to be read. For example, 058 <pre> 059 $PTII/bin/ptexecute -conf ptolemy/configs/full/configuration.xml ../../domains/sdf/demo/Butterfly/Butterfly.xml 060 </pre> 061 and 062 <pre> 063 $PTII/bin/ptexecute -full ../../domains/sdf/demo/Butterfly/Butterfly.xml 064 </pre> 065 are equivalent 066 <p> 067 If no configuration is specified on the command line, then 068 the MoML file ptolemy/configs/runConfiguration.xml is loaded before 069 other command line arguments are processed. 070 071 <p> If one of the command-line arguments is -exit, then System.exit() 072 is called when all the models are finished running. System.exit() 073 returns 0 if all the models finished without throwing an exception, 074 otherwise it returns an integer that represents the number of models 075 that threw an exception. The main() method calls System.exit() 076 as well and returns an integer that represents the number of models 077 that threw an exception. 078 079 <p>If there are no command-line arguments at all, then this class 080 does nothing. 081 082 <p> This class will bring up the GUI and usually requires access 083 to a display. The {@link ptolemy.actor.gui.MoMLSimpleApplication} 084 class will run models in a non-graphical context. 085 086 @author Edward A. Lee, Steve Neuendorffer Christopher Hylands 087 @version $Id$ 088 @since Ptolemy II 1.0 089 @Pt.ProposedRating Yellow (eal) 090 @Pt.AcceptedRating Red (eal) 091 @see ModelFrame 092 @see RunTableau 093 */ 094public class PtExecuteApplication extends MoMLApplication { 095 /** Parse the specified command-line arguments, creating models 096 * and running them. 097 * @param args The command-line arguments. 098 * @exception Exception If command line arguments have problems. 099 */ 100 public PtExecuteApplication(String[] args) throws Exception { 101 // FIXME: Under JDK1.3.1_06, the MoMLApplication constructor 102 // calls setLookAndFeel() which invokes getDefaultToolkit() 103 // which may cause PtExecuteApplication to not exit. See 104 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4030718 105 // However, since we now run with JDK1.4.1, this should not 106 // be a problem. 107 super(args); 108 } 109 110 /////////////////////////////////////////////////////////////////// 111 //// public methods //// 112 113 /** Display a stack trace because one of the models has an error. 114 * A dialog box with the stack trace is created, the stack trace 115 * is printed to stderr and the eventual exiting of the process 116 * is delayed so the stack trace can be read. 117 * @param manager The manager calling this method. 118 * @param throwable The Throwable to be displayed. 119 */ 120 @Override 121 public synchronized void executionError(Manager manager, 122 Throwable throwable) { 123 124 // If you modify this code, make sure that the following command 125 // prints a meaningful message to stdout: 126 // $PTII/bin/ptexecute $PTII/ptolemy/domains/sdf/kernel/test/auto/knownFailedTests/tunneling2.xml 127 128 // One of the models has an error, so we set the return value of 129 // the java process to something other than 1. 130 _exitValue++; 131 MessageHandler.error("Command failed", throwable); 132 133 // Delay exiting for two seconds so the stack trace is visible. 134 // FIXME: this is not the best way to do this. The user will 135 // not have time to really read the message. A better design 136 // would be to some how prevent the process from exiting until 137 // after the stack trace dialog is closed. 138 _test = true; 139 140 // Print the stack trace on standard error. 141 System.err.print(KernelException.stackTraceToString(throwable)); 142 143 super.executionError(manager, throwable); 144 } 145 146 /** Create a new instance of this application, passing it the 147 * command-line arguments. 148 * @param args The command-line arguments. 149 */ 150 public static void main(String[] args) { 151 try { 152 PtExecuteApplication application = new PtExecuteApplication(args); 153 application.runModels(); 154 application.waitForFinish(); 155 } catch (Throwable throwable) { 156 MessageHandler.error("Command failed", throwable); 157 // Be sure to print the stack trace so that 158 // "$PTII/bin/ptexecute -foo" prints something. 159 System.err.print(KernelException.stackTraceToString(throwable)); 160 161 _exitValue++; 162 163 // Keep the process running so the dialog can be displayed. 164 _test = true; 165 } 166 167 // If the -test arg was set, then exit after 2 seconds. 168 if (_test) { 169 try { 170 Thread.sleep(2000); 171 } catch (InterruptedException e) { 172 } 173 } 174 StringUtilities.exit(_exitValue); 175 } 176 177 /////////////////////////////////////////////////////////////////// 178 //// protected methods //// 179 180 /** Return a default Configuration, which in this case is given by 181 * the MoML file ptolemy/configs/runConfiguration.xml. 182 * The default configuration supports executing, but not editing, 183 * Ptolemy models. 184 * If there is an _applicationInitializer parameter, then 185 * construct it. The _applicationInitializer parameter contains 186 * a string that names a class to be initialized. 187 188 * @return A default configuration. 189 * @exception Exception If the configuration cannot be opened. 190 */ 191 @Override 192 protected Configuration _createDefaultConfiguration() throws Exception { 193 if (_configurationURL == null) { 194 _configurationURL = specToURL( 195 "ptolemy/configs/runConfiguration.xml"); 196 } 197 198 _configuration = readConfiguration(_configurationURL); 199 200 // This has the side effect of merging properties from ptII.properties. 201 super._createDefaultConfiguration(); 202 203 // Read the user preferences, if any. 204 PtolemyPreferences.setDefaultPreferences(_configuration); 205 206 return _configuration; 207 } 208 209 /** Throw an exception. 210 * @return Does not return. 211 * @exception Exception Always thrown. 212 */ 213 @Override 214 protected Configuration _createEmptyConfiguration() throws Exception { 215 throw new Exception("No model specified."); 216 } 217 218 /** Parse the command-line arguments. This overrides the base class 219 * only to set the usage information. 220 * @exception Exception If an argument is not understood or triggers 221 * an error. 222 */ 223 @Override 224 protected synchronized void _parseArgs(String[] args) throws Exception { 225 _commandTemplate = "ptexecute [ options ] file ..."; 226 227 // PtExecuteApplication.super._parseArgs(args) 228 // calls _createDefaultConfiguration() asap, 229 // so delay calling it until we process 230 // the arguments and possibly get a configuration. 231 List processedArgsList = new LinkedList(); 232 233 for (int i = 0; i < args.length; i++) { 234 // Parse any configuration specific args first so that we can 235 // set up the configuration for later use by the parent class. 236 if (!_configurationParseArg(args[i])) { 237 // If we did not parse the arg, the 238 // add it to the list of args to be passed 239 // to the super class. 240 processedArgsList.add(args[i]); 241 } 242 } 243 244 if (_expectingConfiguration) { 245 throw new IllegalActionException("Missing configuration"); 246 } 247 248 String[] processedArgs = (String[]) processedArgsList 249 .toArray(new String[processedArgsList.size()]); 250 251 super._parseArgs(processedArgs); 252 } 253 254 /** Return a string summarizing the command-line arguments. 255 * @return A usage string. 256 */ 257 @Override 258 protected String _usage() { 259 return _configurationUsage(_commandTemplate, _localCommandOptions, 260 _localCommandFlags); 261 } 262 263 /////////////////////////////////////////////////////////////////// 264 //// protected variables //// 265 // _localCommandFlags and _commandOptions are static because 266 // _usage() may be called from the constructor of the parent class, 267 // in which case non-static variables are null? 268 269 /** The command-line options that are either present or not. */ 270 protected static String[] _localCommandFlags = { "-exit" }; 271 272 /** The command-line options that take arguments. */ 273 protected static String[][] _localCommandOptions = { { "-config", 274 "<configuration URL, defaults to ptolemy/configs/runConfiguration.xml>" }, }; 275 276 /////////////////////////////////////////////////////////////////// 277 //// private methods //// 278 279 /** Parse a command-line argument. Usually, we would name this 280 * method _parseArg(), but we want to handle any arguments that 281 * handle configuration changes before calling the parent class 282 * _parseArg() because the parent class depends either having a 283 * configuration to work with, or the parent class sets up a 284 * configuration. 285 * @return True if the argument is understood, false otherwise. 286 * @exception Exception If something goes wrong. 287 */ 288 private boolean _configurationParseArg(String arg) throws Exception { 289 if (arg.startsWith("-conf")) { 290 _expectingConfiguration = true; 291 } else if (arg.startsWith("-")) { 292 // If the argument names a directory in ptolemy/configs 293 // that contains a file named configuration.xml that can 294 // be found either as a URL or in the classpath, then 295 // assume that it is a configuration. For example, -ptiny 296 // will look for ptolemy/configs/ptiny/configuration.xml 297 // If the argument does not name a configuration, then 298 // we return false so that the argument can be processed 299 // by the parent class. 300 try { 301 _configurationSubdirectory = arg.substring(1); 302 303 String potentialConfiguration = "ptolemy/configs/" 304 + _configurationSubdirectory + "/configuration.xml"; 305 306 // This will throw an Exception if we can't find the config. 307 _configurationURL = specToURL(potentialConfiguration); 308 } catch (Exception ex) { 309 // The argument did not name a configuration, let the parent 310 // class have a shot. 311 return false; 312 } 313 } else if (_expectingConfiguration) { 314 _expectingConfiguration = false; 315 _configurationURL = specToURL(arg); 316 } else { 317 return false; 318 } 319 320 return true; 321 } 322 323 /////////////////////////////////////////////////////////////////// 324 //// private variables //// 325 // The subdirectory (if any) of ptolemy/configs where the configuration 326 // may be found. For example if vergil was called with -ptiny, 327 // then this variable will be set to "ptiny", and the configuration 328 // should be at ptolemy/configs/ptiny/configuration.xml 329 private String _configurationSubdirectory; 330 331 // URL of the configuration to read. 332 // The URL may absolute, or relative to the Ptolemy II tree root. 333 // We use the URL instead of the string so that if the configuration 334 // is set as a command line argument, we can use the processed value 335 // from the command line instead of calling specToURL() again, which 336 // might be expensive. 337 private URL _configurationURL; 338 339 // Return value of this process. 0 = everything ok, 2 = had an exception. 340 private static int _exitValue = 0; 341 342 // Flag indicating that the previous argument was -conf 343 private boolean _expectingConfiguration = false; 344}