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}