001/*
002 * Copyright (c) 1997-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.kepler.domains;
031
032import java.util.Iterator;
033
034import org.sdm.spa.WebService;
035
036import ptolemy.actor.Actor;
037import ptolemy.actor.FiringEvent;
038import ptolemy.actor.sched.Firing;
039import ptolemy.actor.sched.Schedule;
040import ptolemy.actor.sched.Scheduler;
041import ptolemy.domains.sdf.kernel.SDFDirector;
042import ptolemy.gui.GraphicalMessageHandler;
043import ptolemy.kernel.ComponentEntity;
044import ptolemy.kernel.CompositeEntity;
045import ptolemy.kernel.util.IllegalActionException;
046import ptolemy.kernel.util.InvalidStateException;
047import ptolemy.kernel.util.NameDuplicationException;
048import ptolemy.kernel.util.Workspace;
049
050//////////////////////////////////////////////////////////////////////////
051//// SDFDirector for Web Services
052
053/**
054 * The SDF4WS is an SDFDirector for the domain of WebServices. SDF4WS provides
055 * the abilty to monitor WebService actors in a workflow, catching their
056 * exceptions and error messages. Additional domain-specific operations can be
057 * "three re-trials" of failing web services before finally switching to another
058 * server providing the same service (if available) OR determining if the web
059 * service operation failure was caused due to invalid user input or internal
060 * web service errors.
061 * 
062 * Based on the Director for the synchronous dataflow (SDF) model of
063 * computation.
064 * 
065 * <h1>SDF4WS overview</h1> The Synchronous Dataflow(SDF) for Web Services
066 * director, like its parent SDF Director supports the efficient execution of
067 * Dataflow graphs that lack control structures. SDF4WS is a director made
068 * specifically for web services with added functionality. This director catches
069 * possible exception from Web Service incase of a scenario and hence failures
070 * in web service access.
071 * 
072 * <h2>More SDF Director Information</h2> Dataflow graphs that contain control
073 * structures should be executed using the Process Networks(PN) domain instead.
074 * SDF allows efficient execution, with very little overhead at runtime. It
075 * requires that the rates on the ports of all actors be known before hand. SDF
076 * also requires that the rates on the ports not change during execution. In
077 * addition, in some cases (namely systems with feedback) delays, which are
078 * represented by initial tokens on relations must be explicitly noted. SDF uses
079 * this rate and delay information to determine the execution sequence of the
080 * actors before execution begins. <h3>Schedule Properties</h3>
081 * <ul>
082 * <li>The number of tokens accumulated on every relation is bounded, given an
083 * infinite number of executions of the schedule.
084 * <li>Deadlock will never occur, given an infinite number of executions of the
085 * schedule.
086 * </ul>
087 * <h1>Class comments</h1> An SDFDirector is the class that controls execution
088 * of actors under the SDF domain. By default, actor scheduling is handled by
089 * the SDFScheduler class. Furthermore, the newReceiver method creates Receivers
090 * of type SDFReceiver, which extends QueueReceiver to support optimized gets
091 * and puts of arrays of tokens.
092 * <p>
093 * Actors are assumed to consume and produce exactly one token per channel on
094 * each firing. Actors that do not follow this convention should set the
095 * appropriate parameters on input and output ports to declare the number of
096 * tokens they produce or consume. See the
097 * 
098 * @link ptolemy.domains.sdf.kernel.SDFScheduler for more information. The @link
099 *       ptolemy.domains.sdf.lib.SampleDelay actor is usually used in a model to
100 *       specify the delay across a relation.
101 *       <p>
102 *       The <i>allowDisconnectedGraphs</i> parameter of this director
103 *       determines whether disconnected graphs are permitted. A model may have
104 *       two or more graphs of actors that are not connected. The schedule can
105 *       jump from one graph to another among the disconnected graphs. There is
106 *       nothing to force the scheduler to finish executing all actors on one
107 *       graph before firing actors on another graph. However, the order of
108 *       execution within an graph should be correct. Usually, disconnected
109 *       graphs in an SDF model indicates an error. The default value of the
110 *       allowDisconnectedGraphs parameter is a BooleanToken with the value
111 *       false.
112 *       <p>
113 *       The <i>iterations</i> parameter of this director corresponds to a limit
114 *       on the number of times the director will fire its hierarchy before it
115 *       returns false in postfire. If this number is not greater than zero,
116 *       then no limit is set and postfire will always return true. The default
117 *       value of the iterations parameter is an IntToken with value zero.
118 *       <p>
119 *       The <i>vectorizationFactor</i> parameter of this director sets the
120 *       number of times that the basic schedule is executed during each firing
121 *       of this director. This might allow the director to execute the model
122 *       more efficiently, by combining multiple firings of each actor. The
123 *       default value of the vectorizationFactor parameter is an IntToken with
124 *       value one.
125 * @see ptolemy.domains.sdf.kernel.SDFScheduler
126 * @see ptolemy.domains.sdf.kernel.SDFReceiver
127 * @author Nandita Mangal
128 * @version $Id: SDF4WS.java 24234 2010-05-06 05:21:26Z welker $
129 * @since Ptolemy II 0.2
130 * @Pt.ProposedRating Green (neuendor)
131 * @Pt.AcceptedRating Green (neuendor)
132 */
133public class SDF4WS extends SDFDirector {
134        /**
135         * Construct a director in the default workspace with an empty string as its
136         * name. The director is added to the list of objects in the workspace.
137         * Increment the version number of the workspace.
138         * 
139         * The SDFD4WS will have a default scheduler of type SDFScheduler.
140         * 
141         * @exception IllegalActionException
142         *                If the name has a period in it, or the director is not
143         *                compatible with the specified container.
144         * @exception NameDuplicationException
145         *                If the container already contains an entity with the
146         *                specified name.
147         */
148        public SDF4WS() throws IllegalActionException, NameDuplicationException {
149
150                super();
151                // Parameters inherited from SDF Director.
152
153        }
154
155        /**
156         * Construct a director in the workspace with an empty name. The director is
157         * added to the list of objects in the workspace. Increment the version
158         * number of the workspace. The SDFDirector will have a default scheduler of
159         * type SDFScheduler.
160         * 
161         * @param workspace
162         *            The workspace for this object.
163         * @exception IllegalActionException
164         *                If the name has a period in it, or the director is not
165         *                compatible with the specified container.
166         * @exception NameDuplicationException
167         *                If the container already contains an entity with the
168         *                specified name.
169         */
170        public SDF4WS(Workspace workspace) throws IllegalActionException,
171                        NameDuplicationException {
172
173                super(workspace);
174
175        }
176
177        /**
178         * Construct a director in the given container with the given name. The
179         * container argument must not be null, or a NullPointerException will be
180         * thrown. If the name argument is null, then the name is set to the empty
181         * string. Increment the version number of the workspace. The SDFD4WS
182         * Director will have a default scheduler of type SDFScheduler.
183         * 
184         * @param container
185         *            Container of the director.
186         * @param name
187         *            Name of this director.
188         * @exception IllegalActionException
189         *                If the director is not compatible with the specified
190         *                container. May be thrown in a derived class.
191         * @exception NameDuplicationException
192         *                If the container is not a CompositeActor and the name
193         *                collides with an entity in the container.
194         */
195        public SDF4WS(CompositeEntity container, String name)
196                        throws IllegalActionException, NameDuplicationException {
197
198                super(container, name);
199        }
200
201        // ///////////////////////////////////////////////////////////////
202        // // parameters ////
203
204        // all parameters extended from SDF Directors.
205
206        // /////////////////////////////////////////////////////////////////
207        // // public methods ////
208
209        /**
210         * BASIC SDF director functionality :Calculate the current schedule, if
211         * necessary, and iterate the contained actors in the order given by the
212         * schedule. No internal state of the director is updated during fire, so it
213         * may be used with domains that require this property, such as CT.
214         * <p>
215         * Iterating an actor involves calling the actor's iterate() method, which
216         * is equivalent to calling the actor's prefire(), fire() and postfire()
217         * methods in succession. If iterate() returns NOT_READY, indicating that
218         * the actor is not ready to execute, then an IllegalActionException will be
219         * thrown. The values returned from iterate() are recorded and are used to
220         * determine the value that postfire() will return at the end of the
221         * director's iteration.
222         * <p>
223         * 
224         * SDF4WS : This method overridden by web service to perform additional
225         * domain-specific operations such as three re-trials of failing web
226         * services before finally switching to another server providing the same
227         * service (if available).
228         * 
229         * @exception IllegalActionException
230         *                If any actor executed by this actor return false in
231         *                prefire.
232         * @exception InvalidStateException
233         *                If this director does not have a container.
234         */
235        public void fire() throws IllegalActionException {
236
237                int returnValue = 0;
238
239                Scheduler scheduler = getScheduler();
240                if (scheduler == null) {
241                        throw new IllegalActionException("Attempted to fire "
242                                        + "system with no scheduler");
243                }
244                // This will throw IllegalActionException if this director
245                // does not have a container.
246                Schedule schedule = scheduler.getSchedule();
247                Iterator firings = schedule.firingIterator();
248
249                while (firings.hasNext() && !_stopRequested) {
250
251                        Firing firing = (Firing) firings.next();
252                        Actor actor = (Actor) firing.getActor();
253                        int iterationCount = firing.getIterationCount();
254
255                        if (_debugging) {
256                                _debug(new FiringEvent(this, actor, FiringEvent.BEFORE_ITERATE,
257                                                iterationCount));
258                        }
259
260                        // get the current actor in the iteration.
261                        actorCurrent = actor;
262
263                        // iterate the actor, catching any exceptions due to failed web
264                        // service access.
265                        try {
266
267                                returnValue = actor.iterate(iterationCount);
268
269                        } catch (Exception ex) {
270                                if (actorCurrent instanceof WebService
271                                                && ex.getMessage().equals(
272                                                                "\nWebService WSDL Not Responding.")) {
273
274                                        GraphicalMessageHandler
275                                                        .message("\nSDF4WS re-trying web service access");
276
277                                        // re-try accessing the web service three times.
278                                        int reTrialCount = 3;
279                                        boolean webServiceSuccess = _reFireWebService(3);
280                                        if (webServiceSuccess == false) {
281                                                GraphicalMessageHandler
282                                                                .message("\nWebService WSDL failed to respond in "
283                                                                                + reTrialCount
284                                                                                + " trials."
285                                                                                + "\nSDF4WS will try to access web service via different server.");
286
287                                                // TO BE DONE: SDF4WS will try to access web service
288                                                // from another server if available.
289
290                                        } else {
291                                                GraphicalMessageHandler
292                                                                .message("\nWebService WSDL access successfull!");
293                                                returnValue = actorCurrentReturnValue;
294
295                                        }
296
297                                }
298                        }
299
300                        if (returnValue == STOP_ITERATING) {
301
302                                // _postfireReturns = false;
303                        } else if (returnValue == NOT_READY) {
304                                throw new IllegalActionException(this, (ComponentEntity) actor,
305                                                "Actor " + "is not ready to fire.");
306                        }
307
308                        if (_debugging) {
309                                _debug(new FiringEvent(this, actor, FiringEvent.AFTER_ITERATE,
310                                                iterationCount));
311                        }
312
313                }
314
315        }// end of fire
316
317        // ///////////////////////////////////////////////////////////////////////////
318        // // private methods ////
319
320        private boolean _reFireWebService(int retrialCount) {
321                int counter = 0;
322                boolean webServiceSuccess = false;
323                for (counter = 0; counter < retrialCount; counter++) {
324                        // iterate the actor retrialCount number of times.
325                        try {
326
327                                actorCurrent.stopFire();
328                                actorCurrent.prefire();
329                                actorCurrent.fire();
330                                // actor was iterated successfully w/o exception.
331                                // get actor's return Value
332                                boolean returnValue = actorCurrent.postfire();
333                                if (returnValue == false)
334                                        actorCurrentReturnValue = STOP_ITERATING;
335
336                                webServiceSuccess = true;
337
338                                return webServiceSuccess;
339                        } catch (Exception e) {
340                                // do nothing but try again
341                                webServiceSuccess = false;
342                        }
343
344                }
345                return webServiceSuccess;
346
347        }
348
349        // /////////////////////////////////////////////////////////////////
350        // // private variables ////
351        private int _iterationCount = 0;
352        private Actor actorCurrent;
353        private int actorCurrentReturnValue = 0;
354
355}