001/* An atomic actor that executes a model specified by a file or URL. 002 003 Copyright (c) 2003-2015 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 028 */ 029package ptolemy.actor.lib.hoc; 030 031import java.io.File; 032import java.net.URI; 033import java.net.URL; 034import java.util.Iterator; 035import java.util.concurrent.Semaphore; 036import java.util.logging.Level; 037import java.util.logging.Logger; 038 039import ptolemy.actor.CompositeActor; 040import ptolemy.actor.Director; 041import ptolemy.actor.Executable; 042import ptolemy.actor.ExecutionListener; 043import ptolemy.actor.IOPort; 044import ptolemy.actor.Manager; 045import ptolemy.actor.TypedAtomicActor; 046import ptolemy.actor.parameters.FilePortParameter; 047import ptolemy.actor.parameters.ParameterPort; 048import ptolemy.actor.parameters.PortParameter; 049import ptolemy.data.BooleanToken; 050import ptolemy.data.LongToken; 051import ptolemy.data.StringToken; 052import ptolemy.data.Token; 053import ptolemy.data.expr.Parameter; 054import ptolemy.data.expr.StringParameter; 055import ptolemy.data.expr.Variable; 056import ptolemy.data.type.BaseType; 057import ptolemy.kernel.CompositeEntity; 058import ptolemy.kernel.attributes.URIAttribute; 059import ptolemy.kernel.util.Attribute; 060import ptolemy.kernel.util.IllegalActionException; 061import ptolemy.kernel.util.InternalErrorException; 062import ptolemy.kernel.util.NameDuplicationException; 063import ptolemy.kernel.util.NamedObj; 064import ptolemy.kernel.util.Settable; 065import ptolemy.kernel.util.Workspace; 066import ptolemy.moml.MoMLParser; 067import ptolemy.util.MessageHandler; 068 069/////////////////////////////////////////////////////////////////// 070//// ModelReference 071 072/** 073 This is an atomic actor that can execute a model specified by 074 a file or URL. This can be used to define an actor whose firing behavior 075 is given by a complete execution of another model. 076 <p> 077 An instance of this actor can have ports added to it. If it has 078 input ports, then on each firing, before executing the referenced 079 model, this actor will read an input token from the input port, if 080 there is one, and use it to set the value of a top-level parameter 081 in the referenced model that has the same name as the port, if there 082 is one. The simplest way to ensure that there is a matching parameter 083 is to use a PortParameter for inputs. However, this actor will work 084 also for ordinary ports. In this case, if there is a top-level 085 parameter of the referenced model with the same name as the port, and 086 it is an instance of Variable (or its derived class Parameter), then 087 the token read at the input is moved into it using its setToken() method. 088 Otherwise, if it is an instance of Settable, then a string representation 089 of the token is copied using the setExpression() method. 090 Input ports should not be multiports, and if they are, then 091 all but the first channel will be ignored. 092 </p> 093 <p> 094 If this actor has output ports and the referenced model is executed, 095 then upon completion of that execution, this actor looks for top-level 096 parameters in the referenced model whose names match those of the output 097 ports. If there are such parameters, then the final value of those 098 parameters is sent to the output ports. If such a parameter is an 099 instance of Variable (or its derived class Parameter), then its 100 contained token is sent to the output. Otherwise, if it is an 101 instance of Settable, then a string token is produced on the output 102 with its value equal to that returned by getExpression() of the 103 Settable. If the model is executed in the calling thread, then 104 the outputs will be produced before the fire() method returns. 105 If the model is executed in a new thread, then the outputs will 106 be produced whenever that thread completes execution of the model. 107 Output ports should not be multiports. If they are, then all but 108 the first channel will be ignored. 109 Normally, when you create output ports for this actor, you will have 110 to manually set the type. There is no type inference from the 111 parameter of the referenced model. 112 </p> 113 <p> 114 A typical use of this actor will use the SetVariable actor 115 inside to define the value of the output port. 116 </p> 117 <p> 118 A suite of parameters is provided to control what happens when this 119 actor executes:</p> 120 <ul> 121 <li> <i>executionOnFiring</i>: 122 The value of this string attribute determines what execution 123 happens when the fire() method is invoked. The recognized 124 values are: 125 <ul> 126 <li> "run in calling thread" (the default) </li> 127 <li> "run in a new thread" </li> 128 <li> "do nothing". </li> 129 </ul> 130 If execution in a separate thread is selected, then the execution can 131 optionally be stopped by the postfire() method (see below). If the model 132 is still executing the next time fire() is called on this actor, then 133 the fire() method will wait for completion of the first execution. 134 If an exception occurs during a run in another thread, then it will 135 be reported at the next invocation of fire(), postfire(), or wrapup(). 136 Note that if you select "run in a new thread" and this actor has 137 output ports, the data is produced to those output ports when 138 the execution completes, whenever that might be. This may make 139 output ports difficult to use in some domains. 140 </li> 141 <li> <i>lingerTime</i>: 142 The amount of time (in milliseconds) to linger in the fire() 143 method of this actor. This is a long that defaults to 0L. 144 If the model is run in the calling thread, then the linger 145 occurs after the run is complete. If the model is run in a 146 new thread, then the linger occurs after the run starts, 147 and the run is stopped after the linger time expires. 148 This can be used, for example, to run a model for a specified 149 amount of time, then ask it to finish() and continue. 150 </li> 151 <li> <i>modelFileOrURL</i>: 152 The file name or URL of the model that this actor will execute. 153 This can be specified either by setting the parameter or by 154 providing a string at the input port. 155 </li> 156 <li> <i>postfireAction</i>: 157 The value of this string attribute determines what happens 158 in the postfire() method. The recognized values are: 159 <ul> 160 <li> "do nothing" (the default) </li> 161 <li> "stop executing" </li> 162 </ul> 163 The "stop executing" choices will only have an effect if 164 if <i>executionOnFiring</i> is set to "run in a new thread". 165 This can be used, for example, to run a model for a specified 166 amount of time, and then stop it. 167 </li> 168 </ul> 169 170 <p> 171 There are currently some limitations: 172 </p> 173 <ul> 174 <li> 175 The referenced model cannot create any displays. Use the subclass 176 VisualModelReference to do that. 177 </li> 178 <li> 179 FIXME: Pausing the referring model doesn't pause the referenced model. 180 </li> 181 <li> 182 FIXME: Need options for error handling. 183 </li> 184 </ul> 185 186 187 @author Edward A. Lee, Elaine Cheong 188 @version $Id$ 189 @since Ptolemy II 4.0 190 @see RunCompositeActor 191 @see ptolemy.actor.lib.SetVariable 192 @Pt.ProposedRating Yellow (eal) 193 @Pt.AcceptedRating Red (eal) 194 */ 195public class ModelReference extends TypedAtomicActor 196 implements ExecutionListener { 197 /** Construct a ModelReference with a name and a container. 198 * The container argument must not be null, or a 199 * NullPointerException will be thrown. This actor will use the 200 * workspace of the container for synchronization and version counts. 201 * If the name argument is null, then the name is set to the empty string. 202 * Increment the version of the workspace. This actor will have no 203 * local director initially, and its executive director will be simply 204 * the director of the container. 205 * 206 * @param container The container. 207 * @param name The name of this actor. 208 * @exception IllegalActionException If the container is incompatible 209 * with this actor. 210 * @exception NameDuplicationException If the name coincides with 211 * an actor already in the container. 212 */ 213 public ModelReference(CompositeEntity container, String name) 214 throws IllegalActionException, NameDuplicationException { 215 super(container, name); 216 217 // FIXME: Need a way to specify a filter for the file browser. 218 modelFileOrURL = new FilePortParameter(this, "modelFileOrURL"); 219 220 // Create the executionOnFiring parameter. 221 executionOnFiring = new StringParameter(this, "executionOnFiring"); 222 executionOnFiring.setExpression("run in calling thread"); 223 executionOnFiring.addChoice("run in calling thread"); 224 executionOnFiring.addChoice("run in a new thread"); 225 executionOnFiring.addChoice("do nothing"); 226 227 // Create the lingerTime parameter. 228 lingerTime = new Parameter(this, "lingerTime"); 229 lingerTime.setTypeEquals(BaseType.LONG); 230 lingerTime.setExpression("0L"); 231 232 // Create the postfireAction parameter. 233 postfireAction = new StringParameter(this, "postfireAction"); 234 postfireAction.setExpression("do nothing"); 235 postfireAction.addChoice("do nothing"); 236 postfireAction.addChoice("stop executing"); 237 238 spawnSeparateModels = new Parameter(this, "spawnSeparateModels"); 239 spawnSeparateModels.setTypeEquals(BaseType.BOOLEAN); 240 spawnSeparateModels.setExpression("false"); 241 spawnSeparateModels.setPersistent(true); 242 243 _semaphore = new Semaphore(0); 244 } 245 246 /////////////////////////////////////////////////////////////////// 247 //// parameters //// 248 249 /** The value of this string parameter determines what execution 250 * happens when the fire() method is invoked. The recognized 251 * values are: 252 * <ul> 253 * <li> "run in calling thread" (the default) </li> 254 * <li> "run in a new thread" </li> 255 * <li> "do nothing". </li> 256 * </ul> 257 */ 258 public StringParameter executionOnFiring; 259 260 /** The amount of time (in milliseconds) to linger in the fire() 261 * method of this actor. This is a long that defaults to 0L. 262 * If the model is run, then the linger occurs after the run 263 * is complete (if the run occurs in the calling thread) or 264 * after the run starts (if the run occurs in a separate thread). 265 */ 266 public Parameter lingerTime; 267 268 /** The file name or URL of the model that this actor represents. 269 * This is empty by default, which means that there is no 270 * associated model to execute. 271 */ 272 public FilePortParameter modelFileOrURL; 273 274 /** The value of this string attribute determines what happens 275 * in the postfire() method. The recognized values are: 276 * <ul> 277 * <li> "do nothing" (the default) </li> 278 * <li> "stop executing" </li> 279 * </ul> 280 * The "stop executing" choices will only have an effect if 281 * if <i>executionOnFiring</i> is set to "run in a new thread". 282 * This can be used, for example, to run a model for a specified 283 * amount of time, and then stop it. 284 */ 285 public StringParameter postfireAction; 286 287 /** If true, then on each firing, create a new instance of 288 * the model given by <i>modelFileOrURL</i>. If false 289 * (the default), then re-use the same model. 290 */ 291 public Parameter spawnSeparateModels; 292 293 /////////////////////////////////////////////////////////////////// 294 //// public methods //// 295 296 /** Override the base class to open the model specified if the 297 * attribute is modelFileOrURL, or for other parameters, to cache 298 * their values. 299 * @param attribute The attribute that changed. 300 * @exception IllegalActionException If the change is not acceptable 301 * to this container (not thrown in this base class). 302 */ 303 @Override 304 public void attributeChanged(Attribute attribute) 305 throws IllegalActionException { 306 if (attribute == modelFileOrURL) { 307 if (_debugging) { 308 _debug("Setting modelFileOrURL to: " 309 + modelFileOrURL.getExpression()); 310 } 311 312 // Open the file and read the MoML to create a model. 313 URL url = modelFileOrURL.asURL(); 314 315 if (url != null) { 316 // If the protocol is that of a file, 317 // make sure it is in fact a file, and not 318 // a directory. 319 if (url.getProtocol().equals("file")) { 320 File asFile = modelFileOrURL.asFile(); 321 322 if (!asFile.isFile()) { 323 throw new IllegalActionException(this, 324 "Not a file: " + url); 325 } 326 } 327 328 // By specifying no workspace argument to the parser, we 329 // are asking it to create a new workspace for the referenced 330 // model. This is necessary because the execution of that 331 // model will proceed through its own sequences, and it 332 // will need to get write permission on the workspace. 333 // Particularly if it is executing in a new thread, then 334 // during the fire() method of this actor it would be 335 // inappropriate to grant write access on the workspace 336 // of this actor. 337 MoMLParser parser = new MoMLParser(); 338 339 try { 340 // It is possible for the specified model to actually 341 // be the model that contains this ModelReference, which is an 342 // error. To prevent arcane stack overflow exceptions, catch this. 343 URI myURI = URIAttribute.getModelURI(this); 344 345 if (myURI != null && myURI.toURL().toExternalForm() 346 .equals(url.toExternalForm())) { 347 throw new IllegalActionException(this, 348 "Cannot reference my own container."); 349 } 350 351 _model = parser.parse(null, url); 352 353 // If we choose the option to spawn models of the same URL separately 354 // then get rid of the spawned model. 355 if (((BooleanToken) spawnSeparateModels.getToken()) 356 .booleanValue()) { 357 MoMLParser.purgeModelRecord(url); 358 } 359 360 } catch (Exception ex) { 361 throw new IllegalActionException(this, ex, 362 "Failed to read model from: " + url); 363 } 364 365 // Create a manager, if appropriate. 366 if (_model instanceof CompositeActor) { 367 _manager = new Manager(_model.workspace(), "Manager"); 368 ((CompositeActor) _model).setManager(_manager); 369 370 if (_debugging) { 371 _debug("** Created new manager."); 372 } 373 } 374 } else { 375 // URL is null... delete the current model. 376 _model = null; 377 _manager = null; 378 _throwable = null; 379 } 380 } else if (attribute == executionOnFiring) { 381 String executionOnFiringValue = executionOnFiring.stringValue(); 382 383 if (executionOnFiringValue.equals("run in calling thread")) { 384 _executionOnFiringValue = _RUN_IN_CALLING_THREAD; 385 } else if (executionOnFiringValue.equals("run in a new thread")) { 386 _executionOnFiringValue = _RUN_IN_A_NEW_THREAD; 387 } else if (executionOnFiringValue.equals("do nothing")) { 388 _executionOnFiringValue = _DO_NOTHING; 389 } else { 390 throw new IllegalActionException(this, 391 "Unrecognized option for executionOnFiring: " 392 + executionOnFiringValue); 393 } 394 } else if (attribute == postfireAction) { 395 String postfireActionValue = postfireAction.stringValue(); 396 397 if (postfireActionValue.equals("do nothing")) { 398 _postfireActionValue = _DO_NOTHING; 399 } else if (postfireActionValue.equals("stop executing")) { 400 _postfireActionValue = _STOP_EXECUTING; 401 } else { 402 throw new IllegalActionException(this, 403 "Unrecognized value for postfireAction: " 404 + postfireActionValue); 405 } 406 } else { 407 super.attributeChanged(attribute); 408 } 409 } 410 411 /** Clone the actor into the specified workspace. This overrides the 412 * base class to ensure that private variables are reset to null. 413 * @param workspace The workspace for the new object. 414 * @return A new actor. 415 * @exception CloneNotSupportedException If a derived class contains 416 * an attribute that cannot be cloned. 417 */ 418 @Override 419 public Object clone(Workspace workspace) throws CloneNotSupportedException { 420 ModelReference newActor = (ModelReference) super.clone(workspace); 421 newActor._manager = null; 422 newActor._model = null; 423 newActor._semaphore = new Semaphore(0); 424 newActor._throwable = null; 425 return newActor; 426 } 427 428 /** React to the fact that execution has failed by unregistering 429 * as an execution listener and by allowing subsequent executions. 430 * Report an execution failure at the next opportunity. 431 * This method will be called when an exception or error 432 * is caught by a manager during a run in another thread 433 * of the referenced model. 434 * @param manager The manager controlling the execution. 435 * @param throwable The throwable to report. 436 */ 437 @Override 438 public synchronized void executionError(Manager manager, 439 Throwable throwable) { 440 _throwable = throwable; 441 _executing = false; 442 443 // NOTE: Can't remove these now! The list is being 444 // currently used to notify me! 445 // manager.removeExecutionListener(this); 446 manager.removeDebugListener(this); 447 notifyAll(); 448 // Need to report the error, otherwise if PlotterBase fails to parse 449 // plotml, then the error will not be displayed. 450 MessageHandler.error("Execution failed.", throwable); 451 } 452 453 /** React to the fact that execution is finished by unregistering 454 * as an execution listener and by allowing subsequent executions. 455 * This is called when an execution of the referenced 456 * model in another thread has finished and the wrapup sequence 457 * has completed normally. The number of successfully completed 458 * iterations can be obtained by calling getIterationCount() 459 * on the manager. 460 * @param manager The manager controlling the execution. 461 */ 462 @Override 463 public synchronized void executionFinished(Manager manager) { 464 _executing = false; 465 // NOTE: Can't remove these now! The list is being 466 // currently used to notify me! 467 // manager.removeExecutionListener(this); 468 manager.removeDebugListener(this); 469 notifyAll(); 470 } 471 472 /** Run a complete execution of the referenced model. A complete 473 * execution consists of invocation of super.initialize(), repeated 474 * invocations of super.prefire(), super.fire(), and super.postfire(), 475 * followed by super.wrapup(). The invocations of prefire(), fire(), 476 * and postfire() are repeated until either the model indicates it 477 * is not ready to execute (prefire() returns false), or it requests 478 * a stop (postfire() returns false or stop() is called). 479 * Before running the complete execution, this method examines input 480 * ports, and if they are connected, have data, and if the referenced 481 * model has a top-level parameter with the same name, then one token 482 * is read from the input port and used to set the value of the 483 * parameter in the referenced model. 484 * After running the complete execution, if there are any output ports, 485 * then this method looks for top-level parameters in the referenced 486 * model with the same name as the output ports, and if there are any, 487 * reads their values and produces them on the output. 488 * If no model has been specified, then this method does nothing. 489 * @exception IllegalActionException If there is no director, or if 490 * the director's action methods throw it. 491 */ 492 @Override 493 public void fire() throws IllegalActionException { 494 super.fire(); 495 496 if (_model == null) { 497 throw new IllegalActionException(this, "No model to execute"); 498 } 499 500 if (_throwable != null) { 501 Throwable throwable = _throwable; 502 _throwable = null; 503 throw new IllegalActionException(this, throwable, 504 "Run in a new thread threw an exception " 505 + "on the previous firing."); 506 } 507 508 // Read the inputs. This should be done even if there is 509 // no model. 510 // Derived classes may need to read inputs earlier in their 511 // fire() method, before calling this class, in which case 512 // they are expected to set this flag to true. 513 if (!_alreadyReadInputs) { 514 // Iterate over input ports and read any available values 515 // into the referenced model parameters and validate 516 // settable attributes. 517 _readInputsAndValidateSettables(); 518 } 519 520 // Set the flag to false for the next firing. 521 _alreadyReadInputs = false; 522 523 if (_model instanceof CompositeActor) { 524 CompositeActor executable = (CompositeActor) _model; 525 526 _manager = executable.getManager(); 527 528 if (_manager == null) { 529 throw new InternalErrorException("No manager!"); 530 } 531 532 if (_debugging) { 533 _manager.addDebugListener(this); 534 535 Director director = executable.getDirector(); 536 537 if (director != null) { 538 director.addDebugListener(this); 539 } 540 } else { 541 _manager.removeDebugListener(this); 542 543 Director director = executable.getDirector(); 544 545 if (director != null) { 546 director.removeDebugListener(this); 547 } 548 } 549 550 // If there is a previous execution, then wait for it to finish. 551 // Avoid the synchronize block if possible. 552 if (_executing) { 553 // Synchronizing here is not correct. 554 // See Workspace.wait(Object) 555 // synchronized (this) { 556 while (_executing) { 557 try { 558 if (_debugging) { 559 _debug("** Waiting for previous execution to finish."); 560 } 561 562 // Use workspace version of wait to release 563 // read permission on the workspace. 564 workspace().wait(this); 565 } catch (InterruptedException ex) { 566 // Cancel subsequent execution. 567 getManager().finish(); 568 return; 569 } 570 } 571 572 if (_debugging) { 573 _debug("** Previous execution has finished."); 574 } 575 // } 576 } 577 578 if (_executionOnFiringValue == _RUN_IN_CALLING_THREAD) { 579 if (_debugging) { 580 _debug("** Executing referenced model in the calling thread."); 581 } 582 583 _manager.addExecutionListener(this); 584 585 try { 586 _manager.execute(); 587 } catch (Throwable ex) { 588 throw new IllegalActionException(this, ex, 589 "Execution failed."); 590 } 591 592 _writeOutputs(); 593 } else if (_executionOnFiringValue == _RUN_IN_A_NEW_THREAD) { 594 // Listen for exceptions. The listener is 595 // removed in the listener methods, executionError() 596 // and executionFinished(). 597 if (_debugging) { 598 _debug("** Creating a new thread to execute the model."); 599 } 600 601 _manager.addExecutionListener(this); 602 603 // Create a thread. Can't directly use _manager.startRun() 604 // because we need to write outputs upon completion. 605 if (_manager.getState() != Manager.IDLE) { 606 throw new IllegalActionException(this, 607 "Cannot start an execution. " 608 + "Referenced model is " 609 + _manager.getState().getDescription()); 610 } 611 612 // NOTE: To avoid race condition, we use 613 // local copy of manager and then release that semaphore. 614 //That way, postfire wait before setting _manager to null 615 //(Otherwise, we could even have localManager = null 616 Thread thread = new Thread() { 617 @Override 618 public void run() { 619 Manager localManager = _manager; 620 _semaphore.release(); 621 try { 622 if (_debugging) { 623 _debug("** Executing model in a new thread."); 624 } 625 626 localManager.execute(); 627 _writeOutputs(); 628 } catch (Throwable throwable) { 629 // If running tried to load in some native code using JNI 630 // then we may get an Error here 631 localManager.notifyListenersOfThrowable(throwable); 632 } 633 // we dont remove listeners, that is done in the callbacks 634 // by the listner itself 635 } 636 }; 637 638 // Priority set to the minimum to get responsive UI during execution. 639 thread.setPriority(Thread.MIN_PRIORITY); 640 thread.start(); 641 } 642 643 long lingerTimeValue = ((LongToken) lingerTime.getToken()) 644 .longValue(); 645 646 if (lingerTimeValue > 0L) { 647 try { 648 if (_debugging) { 649 _debug("** Lingering for " + lingerTimeValue 650 + " milliseconds."); 651 } 652 653 _lingeringThread = Thread.currentThread(); 654 Thread.sleep(lingerTimeValue); 655 } catch (InterruptedException ex) { 656 // Ignore. 657 } finally { 658 _lingeringThread = null; 659 } 660 } 661 } 662 } 663 664 /** Reset the state. 665 * @throws IllegalActionException If the parent class throws it. 666 */ 667 @Override 668 public void initialize() throws IllegalActionException { 669 super.initialize(); 670 _throwable = null; 671 } 672 673 /** Report in debugging statements that the manager state has changed. 674 * This method is called if the referenced model 675 * is executed in another thread and the manager changes state. 676 * @param manager The manager controlling the execution. 677 * @see Manager#getState() 678 */ 679 @Override 680 public void managerStateChanged(Manager manager) { 681 if (_debugging) { 682 _debug("Referenced model manager state: " + manager.getState()); 683 } 684 } 685 686 /** Override the base class to perform requested postfire actions. 687 * @return Whatever the superclass returns (probably true). 688 * @exception IllegalActionException Thrown if a parent class throws it. 689 */ 690 @Override 691 public boolean postfire() throws IllegalActionException { 692 693 if (_executionOnFiringValue == _RUN_IN_A_NEW_THREAD) { 694 try { 695 //wait for the notifier to finish if started in a new thread 696 //that way we avoid a race condition in fire 697 _semaphore.acquire(1); 698 } catch (InterruptedException ex) { 699 Logger.getLogger(ModelReference.class.getName()) 700 .log(Level.SEVERE, null, ex); 701 } 702 } 703 704 if (_postfireActionValue == _STOP_EXECUTING && _manager != null) { 705 if (_debugging) { 706 _debug("** Calling finish() on the Manager to request termination."); 707 } 708 709 _manager.finish(); 710 711 // Wait for the finish. 712 if (_debugging) { 713 _debug("** Waiting for completion of execution."); 714 } 715 716 _manager.waitForCompletion(); 717 718 // Test auto/ModelReference2.xml seems to end up here with 719 _manager = null; 720 } 721 722 return super.postfire(); 723 } 724 725 /** Override the base class to call stop() on the referenced model. 726 */ 727 @Override 728 public void stop() { 729 if (_model instanceof Executable) { 730 ((Executable) _model).stop(); 731 } 732 733 if (_lingeringThread != null) { 734 _lingeringThread.interrupt(); 735 } 736 737 super.stop(); 738 } 739 740 /* Override the base class to call stopFire() on the referenced model. 741 */ 742 @Override 743 public void stopFire() { 744 if (_model instanceof Executable) { 745 ((Executable) _model).stopFire(); 746 } 747 748 if (_lingeringThread != null) { 749 _lingeringThread.interrupt(); 750 } 751 752 super.stopFire(); 753 } 754 755 /** Override the base class to call terminate() on the referenced model. 756 */ 757 @Override 758 public void terminate() { 759 if (_model instanceof Executable) { 760 ((Executable) _model).terminate(); 761 } 762 763 super.terminate(); 764 } 765 766 /** Report an exception if it occurred in a background run. 767 * @exception IllegalActionException If there is no director, or if 768 * a background run threw an exception. 769 */ 770 @Override 771 public void wrapup() throws IllegalActionException { 772 super.wrapup(); 773 if (_manager != null) { 774 _manager.finish(); 775 776 // Wait for the finish. 777 if (_debugging) { 778 _debug("** Waiting for completion of execution."); 779 } 780 781 _manager.waitForCompletion(); 782 } 783 _alreadyReadInputs = false; 784 785 if (_throwable != null) { 786 Throwable throwable = _throwable; 787 _throwable = null; 788 throw new IllegalActionException(this, throwable, 789 "Background run threw an exception"); 790 } 791 } 792 793 /////////////////////////////////////////////////////////////////// 794 //// protected variables //// 795 796 /** If a derived class calls modelFileOrURL.update() in its fire() 797 * method prior to calling super.fire(), then it should set this 798 * flag to true. 799 */ 800 protected boolean _alreadyReadInputs = false; 801 802 /////////////////////////////////////////////////////////////////// 803 //// protected methods //// 804 805 /** Iterate over input ports and read any available values into 806 * the referenced model parameters and validate settable 807 * attributes. 808 * 809 * Note: We call validateSettables() on the referenced model in 810 * this method, since input values read into the referenced model 811 * may cause structural changes to the model (e.g. if a Ptalon 812 * parameter changes and causes the internal structure of a 813 * PtalonActor to be regenerated). Since validateSettables() is 814 * not currently called in 815 * ptolemy.actor.Manager#preinitializeAndResolveTypes(), we must 816 * call it here before type resolution begins in a later step, in 817 * order to avoid collecting invalid type constraints (due to 818 * deleted or invalidated parts of the model) and to avoid 819 * insufficient type constraints (due to newly generated parts of 820 * the model). 821 * @exception IllegalActionException If reading the ports or 822 * setting the parameters causes it. 823 */ 824 protected void _readInputsAndValidateSettables() 825 throws IllegalActionException { 826 // NOTE: This is an essentially exact copy of the code in 827 // RunCompositeActor, but this class and that one can't easily 828 // share a common base class. 829 if (_debugging) { 830 _debug("** Reading inputs (if any)."); 831 } 832 833 // Flag to check if a value has actually been changed. 834 boolean changeMade = false; 835 836 Iterator ports = inputPortList().iterator(); 837 838 while (ports.hasNext()) { 839 IOPort port = (IOPort) ports.next(); 840 841 if (port instanceof ParameterPort) { 842 PortParameter parameter = ((ParameterPort) port).getParameter(); 843 844 if (_debugging) { 845 _debug("** Updating PortParameter: " + port.getName()); 846 } 847 848 parameter.update(); 849 changeMade = true; 850 continue; 851 } 852 853 if (port.isOutsideConnected() && port.hasToken(0)) { 854 Token token = port.get(0); 855 856 if (_model != null) { 857 Attribute attribute = _model.getAttribute(port.getName()); 858 859 // Use the token directly rather than a string if possible. 860 if (attribute instanceof Variable) { 861 if (_debugging) { 862 _debug("** Transferring input to parameter: " 863 + port.getName()); 864 } 865 866 ((Variable) attribute).setToken(token); 867 changeMade = true; 868 } else if (attribute instanceof Settable) { 869 if (_debugging) { 870 _debug("** Transferring input as string to parameter: " 871 + port.getName()); 872 } 873 874 ((Settable) attribute).setExpression(token.toString()); 875 changeMade = true; 876 } 877 } 878 } 879 } 880 if (changeMade) { 881 // If a value in the referenced model has actually been 882 // changed, we need to validate the settable attributes in 883 // the model. See note in method comment. 884 _model.validateSettables(); 885 } 886 } 887 888 /////////////////////////////////////////////////////////////////// 889 //// private methods //// 890 891 /** Iterate over output ports and read any available values from 892 * the referenced model parameters and produce them on the outputs. 893 * @exception IllegalActionException If reading the parameters or 894 * writing to the ports causes it. 895 */ 896 private void _writeOutputs() throws IllegalActionException { 897 // NOTE: This is an essentially exact copy of the code in RunCompositeActor, 898 // but this class and that one can't easily share a common base class. 899 if (_debugging) { 900 _debug("** Writing outputs (if any)."); 901 } 902 903 Iterator ports = outputPortList().iterator(); 904 905 while (ports.hasNext()) { 906 IOPort port = (IOPort) ports.next(); 907 908 // Only write if the port has a connected channel. 909 if (port.isOutsideConnected()) { 910 Attribute attribute = _model.getAttribute(port.getName()); 911 912 // Use the token directly rather than a string if possible. 913 if (attribute instanceof Variable) { 914 if (_debugging) { 915 _debug("** Transferring parameter to output: " 916 + port.getName()); 917 } 918 919 port.send(0, ((Variable) attribute).getToken()); 920 } else if (attribute instanceof Settable) { 921 if (_debugging) { 922 _debug("** Transferring parameter as string to output: " 923 + port.getName()); 924 } 925 926 port.send(0, new StringToken( 927 ((Settable) attribute).getExpression())); 928 } 929 } 930 } 931 } 932 933 /////////////////////////////////////////////////////////////////// 934 //// protected variables //// 935 936 /** The model. */ 937 protected NamedObj _model; 938 939 /////////////////////////////////////////////////////////////////// 940 //// private variables //// 941 // Possible values for executionOnFiring. 942 private static int _DO_NOTHING = 0; 943 944 private static int _RUN_IN_CALLING_THREAD = 1; 945 946 private static int _RUN_IN_A_NEW_THREAD = 2; 947 948 /** The value of the executionOnFiring parameter. */ 949 private transient int _executionOnFiringValue = _RUN_IN_CALLING_THREAD; 950 951 // Flag indicating that the previous execution is in progress. 952 private volatile transient boolean _executing = false; 953 954 /** Reference to a thread that is lingering. */ 955 private Thread _lingeringThread = null; 956 957 /** The manager currently managing execution. */ 958 private Manager _manager = null; 959 960 /** The value of the postfireAction parameter. */ 961 private transient int _postfireActionValue = _DO_NOTHING; 962 963 // Possible values for postfireAction (plus _DO_NOTHING, 964 // which is defined above). 965 private static int _STOP_EXECUTING = 1; 966 967 // Error from a previous run. 968 private transient Throwable _throwable = null; 969 970 /** Semaphore used to synchronize a new thread a postfire if needed. */ 971 private Semaphore _semaphore; 972}