001/* The provenance recording API.
002
003Copyright (c) 2007-2010 The Regents of the University of California.
004All rights reserved.
005Permission is hereby granted, without written agreement and without
006license or royalty fees, to use, copy, modify, and distribute this
007software and its documentation for any purpose, provided that the above
008copyright notice and the following two paragraphs appear in all copies
009of this software.
010
011IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015SUCH DAMAGE.
016
017THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022ENHANCEMENTS, OR MODIFICATIONS.
023
024*/
025
026package org.kepler.provenance;
027
028import java.io.File;
029import java.io.IOException;
030import java.io.Writer;
031import java.net.InetAddress;
032import java.net.UnknownHostException;
033import java.util.Date;
034import java.util.Map;
035
036import org.kepler.objectmanager.lsid.KeplerLSID;
037import org.kepler.tagging.TagEvent;
038import org.kepler.util.WorkflowRenameListener;
039
040import ptolemy.actor.Actor;
041import ptolemy.actor.Director;
042import ptolemy.actor.FiringEvent;
043import ptolemy.actor.IOPortEvent;
044import ptolemy.actor.IORelation;
045import ptolemy.actor.TypedIOPort;
046import ptolemy.data.StringToken;
047import ptolemy.data.expr.Parameter;
048import ptolemy.kernel.util.Attribute;
049import ptolemy.kernel.util.IllegalActionException;
050import ptolemy.kernel.util.NameDuplicationException;
051import ptolemy.kernel.util.Nameable;
052import ptolemy.kernel.util.NamedObj;
053
054/** The provenance recorder uses the API provided by this class to
055 * write provenance information. This base class does not store the
056 * provenance data, but should be sub-classed to record it to a specific
057 * medium. For examples, see SQLRecording, and TextFileRecording. 
058 * 
059 * The API roughly consists of three parts: specification, evolution,
060 * and execution.
061 * 
062 * <p> The workflow structure, or <em>specification</em>, is recorded
063 * using the regNNN() methods. specificationStart() and specificationStop()
064 * are called before and after workflow structure is recorded. 
065 * </p>
066 * <p>
067 * FIXME this is not all implemented
068 * An <em>evolution</em> is a group of changes to the workflow structure.
069 * evolutionStart() and evolutionStop() are called before and after
070 * each change or group of changes to the workflow. A change
071 * may be one of:
072 * <ul>
073 * <li>Addition to the workflow: the corresponding regNNN() method in
074 * the <i>Specification</i> interface is called.</li>
075 * <li>Removal from the workflow: the remove() or removeLink() method in
076 * this interface is called.</li>
077 * <li>Rename a NamedObj in the workflow: 
078 * <li>Parameter value change: regParameter() in <i>Specification</i>
079 * is called.</li>
080 * </ul>
081 * </p>
082 * <p>
083 * When a workflow <em>executes</em>, methods are called for actors firing,
084 * ports reading and writing, and if an error occurs. executionStart() and
085 * executionStop() are called before and after execution.
086 * </p>
087 *
088 * @author Daniel Crawl
089 * @version $Id: Recording.java 33385 2015-05-01 03:05:27Z crawl $
090 *
091 */
092    
093public class Recording
094{
095
096    /** Create a new provenance recording. */
097    public Recording() throws RecordingException
098    {
099    }
100
101    /** Stop recording. Called when the Recording type changes
102     *  or the provenance recorder is removed from canvas. 
103     *  NOTE: this is NOT called when the GUI exits or when the
104     *  workflow runs from the command line.
105     *  
106     *  This is the last method called to a Recording instance.
107     */
108    public void disconnect() throws RecordingException
109    {
110    }
111
112    // Specification
113    
114    /** Called before registering workflow contents. */
115    public void specificationStart() throws RecordingException
116    {
117        // by default, we only want the contents registered once.
118        // NOTE: we change _needWorkflowContents to false here
119        // instead of specificationStop() since regContents()
120        // may be called in the process of the ProvenanceRecorder
121        // registering workflow contents.
122        _needWorkflowContents = false; 
123
124        //_debug("spec stop needWorkflowContents = false");
125    }
126
127    /** Called when finished registering workflow contents. */
128    public void specificationStop() throws RecordingException
129    {
130    }
131
132    /** Returns true if provenance recorder should register workflow
133     *  contents should be registered. 
134     */
135    public boolean regContents() throws RecordingException
136    {
137        return _needWorkflowContents;
138    }
139
140    /** Register an actor. 
141     *
142     * @return if true, register contents of actor.
143     */
144    public boolean regActor(Actor actor) throws RecordingException
145    {
146        return false;
147    }
148
149    /** Register a director. 
150     * 
151     * @return if true, register contents of director.
152     */
153    public boolean regDirector(Director director) throws RecordingException
154    {
155        return false; 
156    }
157
158    /** Register a parameter. A parameter can be anything
159     * stored in the MoML that does not have its own
160     * <code>regNNN()</code> method. This can be user-level 
161     * parameters (e.g., Parameter, StringParameter, etc.) or
162     * internal to Kepler (e.g., _location, semanticType000, etc.).
163     * (A "parameter" corresponds to a property in the MoML).
164     *
165     * @return if true, register contents of parameter.
166     */
167    public boolean regParameter(NamedObj parameter) throws RecordingException
168    {
169        return false; 
170    }
171
172    /** Register a link between two endpoints. Each endpoint must already
173     * be registered as a port or relation. NOTE: in ptII, a link can 
174     * between a port and a relation, or between two relations.
175     *
176     * @param endPoint1 first endpoint.
177     * @param endPoint2 second endpoint.
178     * @return if true, register contents of link.
179     */
180    public boolean regLink(NamedObj endPoint1, NamedObj endPoint2)
181        throws RecordingException
182    {
183        return false; 
184    }
185
186    /** Register a port or portparameter.
187     *
188     * @return if true, register contents of port.
189     */
190    public boolean regPort(TypedIOPort port) throws RecordingException
191    {
192        return false; 
193    }
194
195    /** Register a relation.
196     *
197     * @return if true, register contents of relation.
198     */
199    public boolean regRelation(IORelation relation) throws RecordingException
200    {
201        return false; 
202    }
203
204    // Evolution
205   
206    /** Start an evolution. */
207    public void evolutionStart() throws RecordingException
208    {
209            
210    }
211    
212    /** Stop an evolution. */
213    public void evolutionStop() throws RecordingException
214    {
215            
216    }
217
218    /** A NamedObj was removed. */
219    public void remove(String name) throws RecordingException
220    {
221         
222    }
223
224    /** Remove a link between two endpoints. Links do not have names
225     *  (they are not NamedObjs, so remove() cannot be used.
226     */
227    public void removeLink(String endPoint1, String endPoint2)
228        throws RecordingException
229    {
230
231    }
232    
233    /** A NamedObj was renamed. */
234    public void rename(String oldName, String newName) 
235        throws RecordingException
236    {
237            
238    }
239
240    // Execution of workflow
241    
242  
243    /** Record the starting of workflow execution. */
244    public void executionStart() throws RecordingException
245    {
246        
247    }
248    
249    /** Record the starting of workflow execution at a specific time. */
250    public void executionStart(Date timestamp) throws RecordingException
251    {
252        executionStart();
253    }
254    
255    /**
256     * Record the starting of workflow execution.
257     * @param executionLSID
258     * @throws RecordingException
259     */
260        public void executionStart(KeplerLSID executionLSID) 
261                throws RecordingException
262        {
263                executionStart();
264        }
265
266    /**
267     * Record the starting of workflow execution at a specific time.
268     * @param executionLSID
269     * @throws RecordingException
270     */
271    public void executionStart(KeplerLSID executionLSID, Date timestamp) 
272        throws RecordingException
273    {
274        executionStart(executionLSID);
275    }
276
277    /** Record the stopping of workflow execution. */
278    public void executionStop() throws RecordingException
279    {
280        
281    }
282
283    /** Record the stopping of workflow execution at a specific time. */
284    public void executionStop(Date timestamp) throws RecordingException
285    {
286        executionStop();
287    }
288
289    /**
290     * Record the stopping of workflow execution.
291     * @param executionLSID
292     * @throws RecordingException
293     */
294        public void executionStop(KeplerLSID executionLSID) 
295                throws RecordingException
296        {
297                executionStop();
298        }
299
300    /**
301     * Record the stopping of workflow execution.
302     * @param executionLSID
303     * @throws RecordingException
304     */
305    public void executionStop(KeplerLSID executionLSID, Date timestamp) 
306        throws RecordingException
307    {
308        executionStop(executionLSID);
309    }
310
311    /** An execution was imported. */
312        public void executionImported() 
313        {
314        }
315        
316    /** An execution was imported. */
317        public void executionImported(KeplerLSID executionLSID) 
318        {
319                executionImported();
320        }
321    
322    /** An actor threw an exception.
323    *
324    * @param source the source of the error. This may be null.
325    * @param throwable the error.
326    */
327    public void executionError(Nameable source, Throwable throwable)
328        throws RecordingException
329    {
330
331    }
332    
333    /**
334     * An actor threw an exception.
335     * @param source
336     * @param throwable
337     * @param executionLSID
338     * @throws RecordingException
339     */
340        public void executionError(Nameable source, Throwable throwable,
341                        KeplerLSID executionLSID) throws RecordingException
342        {
343                executionError(source, throwable);
344        }
345    
346    /** Associate the contents of a file with the most recent workflow execution. */
347    public void addFileForLastExecution(Map<String,String> metadataMap, File file) 
348        throws RecordingException
349    {
350        
351    }
352
353    /** Record an actor firing at the current time. */
354    public void actorFire(FiringEvent event) throws RecordingException
355    {
356        actorFire(event, new Date());
357    }
358    
359    /** Record an actor firing at a specific time. */
360    public void actorFire(FiringEvent event, Date timestamp) throws RecordingException
361    {
362        
363    }
364
365    /** Record a port event at the current time. */
366    public void portEvent(IOPortEvent event) throws RecordingException
367    {
368        portEvent(event, new Date());
369    }
370
371    /** Record a port event at a specific time. */
372    public void portEvent(IOPortEvent event, Date timestamp) throws RecordingException
373    {
374        
375    }
376
377    /** Record a port event. Data contained in a token can be
378     * retrieved using Token.toString(). Application-specific
379     * token types must implement this method to serialize data.
380     */
381    public void refillPortEvent(IOPortRefillEvent event) throws RecordingException
382    {
383        
384    }
385
386    /** Record a custom provenance event. */
387    public void customProvEvent(ProvenanceEvent event) throws RecordingException
388    {
389        
390    }
391
392    /** Add Parameters to ProvenanceListener configuration GUI. */
393    public RecordingParameters generateParameters(NamedObj no) 
394        throws IllegalActionException, NameDuplicationException
395    {
396        return new RecordingParameters(no);
397    }
398
399    /** React to a change in an attribute. */
400    public void attributeChanged(Attribute attribute)
401        throws IllegalActionException
402    {
403        //_debug("Recording.attributeChanged: " + attribute);
404
405        if(attribute.getName().equals("Machine Name"))
406        {
407            _machineStr = 
408                ((StringToken)((Parameter)attribute).getToken()).stringValue();
409            
410            if(_machineStr.length() == 0)
411            {
412                // try to set default from InetAddress
413                try
414                {
415                    InetAddress addr = InetAddress.getLocalHost();
416                    _machineStr = addr.getHostName();
417                }
418                catch(UnknownHostException e)
419                {
420                    _machineStr = "127.0.0.1";
421                }
422            }
423        }
424    }
425
426    /** Get a Queryable connected to the Recording output.
427     *  @exception QueryException may be thrown if Queryable not implemented
428     *  for the Recording type.
429     * @throws RecordingException 
430     */
431    public Queryable getQueryable(boolean allowReconnectWF) throws QueryException, RecordingException
432    {
433        throw new QueryException("Queryable for recording " +
434                getClass().getName() + " is not implemented.");
435    }
436    
437    /** Get the container. */
438    public ProvenanceRecorder getContainer() {
439        return _recorder;
440    }
441    
442    /** Set the container. */
443    public void setContainer(ProvenanceRecorder container)
444    {
445        if(container == null)
446        {
447                _recorderContainer = null;
448        }
449        else
450        {
451                _recorderContainer = container.getContainer();
452        }
453        _updateContainerName();
454    }
455   
456    /** Set a Writer for debugging output. */
457    public void setDebugWriter(Writer writer)
458    {
459        _debugWriter = writer;
460    }
461    
462    /** Set the container LSID. This is used when a sub workflow is
463     *  ran separately at the top level.
464     */
465    public void setContainerLSID(KeplerLSID lsid)
466    {
467        _containerLSID = lsid;
468    }
469    
470    /** Set the container name. This is used when a sub workflow is
471     *  ran separately at the top level.
472     */
473    public void setContainerName(String name)
474    {
475        _containerName = name;
476    }
477 
478    /** A workflow was renamed.
479     * 
480     * @param namedObj the workflow
481     * @param oldLSID the previous LSID
482     * @param newLSID the new LSID
483     * @param oldName the previous name
484     * @param newName the new name
485     * @throws RecordingException 
486     * @see WorkflowRenameListener
487     */
488    public void renamedWorkflow(NamedObj namedObj, KeplerLSID oldLSID,
489        KeplerLSID newLSID, String oldName, String newName) throws RecordingException
490    {
491        
492    }
493    
494    /* A tag was added. */
495    public void tagAdded(TagEvent event) throws RecordingException
496    {
497        
498    }
499    
500    /* A tag was removed. */
501    public void tagRemoved(TagEvent event) throws RecordingException
502    {
503        
504    }
505
506        /**
507         * Change execution LSID for an execution Id. Does not change and returns
508         * false for attempts to change to an older or current revision, or if a
509         * QueryException, or if no such execution Id.
510         * 
511         * @param execId
512         * @param newExecLSID
513         * @return
514         * @throws RecordingException
515         */
516        public boolean changeExecutionLSID(int execId, KeplerLSID newExecLSID,
517                        Queryable q) throws RecordingException 
518        {
519                return false;
520        }
521    
522    /** Delete a list of workflow executions by lsid
523     * @return numRowsDeleted
524     */
525    //public int deleteExecutions(List<KeplerLSID> lsids) throws RecordingException{
526    //  return 0;
527    //}
528    
529    /** Set the state serializer. */
530    public void setStateSerializer(StateSerializer serializer)
531    {
532        _stateSerializer = serializer;
533    }
534    
535    /** Remove the state serializer. */
536    public void removeStateSerializer()
537    {
538        _stateSerializer = null;
539    }
540    
541    ////////////////////////////////////////////////////////////////////////
542    //// protected methods                                              ////
543
544    /** Debugging output. */
545    protected void _debug(String str)
546    {
547        if(_debugPrint)
548        {
549            System.out.println("DEBUG: " + str);
550            System.out.flush();
551        }
552    }
553
554    /** Debugging output. */
555    protected void _debug(Object object)
556    {
557        _debug(object.toString());
558    }
559
560    /** Warning output. */
561    protected void _warn(String str)
562    {
563        System.out.println("WARNING: " + str);
564        System.out.flush();
565    }
566
567    /** Error output. */
568    protected void _error(String str)
569    {
570        System.out.println("ERROR: " + str);
571        System.out.flush();
572    }
573
574    protected String _getExceptionMessage(Exception e)
575    {
576        e.printStackTrace();
577        return e.getClass().getName() + ": " + e.getMessage();
578    }
579
580    /** Write to any debug writer. */
581    protected void _debugWrite(String str) throws RecordingException
582    {
583        if(_debugWriter != null)
584        {
585            try
586            {
587                _debugWriter.write(str);
588                _debugWriter.write("\n");
589            }
590            catch(IOException e)
591            {
592                throw new RecordingException("Error writing to debug writer:",
593                    e);
594            }
595        }
596    }
597
598    /** Write to any debug writer. */
599    protected void _debugWrite(String str, Throwable t)
600        throws RecordingException
601    {
602        _debugWrite(str + t.getMessage());
603    }
604
605    /** Set the value for needing workflow contents to be registered. */
606    protected void _needWorkflowContents(boolean val)
607    {
608        _needWorkflowContents = val;
609        //_debug("needWorkflowContents changed to " + _needWorkflowContents);
610    }
611   
612    /** Update the container's name. */
613    protected void _updateContainerName()
614    {
615        if(_recorderContainer == null)
616        {
617            _containerFullName = null;
618        }
619        else
620        {
621            _containerFullName = _getNameableFullName(_recorderContainer);
622        }
623    }
624
625    /** Get a Nameable's full name including the name of the containing
626     *  workflow.
627     */
628    protected String _getNameableFullName(Nameable nameable)
629    {
630        if(_containerName != null)
631        {
632            return _containerName + nameable.getFullName();
633        }
634        else
635        {
636            return nameable.getFullName();
637        }
638    }
639
640    ////////////////////////////////////////////////////////////////////////
641    //// protected variables                                            ////
642
643    /** The full name of the provenance recorder's container. */
644    protected String _containerFullName;
645
646    /** If true, output debugging statements to stdout. */
647    protected boolean _debugPrint = true;
648
649    /** Used for debugging output. */
650    protected Writer _debugWriter;
651
652    /** The machine name */
653    protected String _machineStr = "unknown";
654
655    /** The container of the provenance recorder. */
656    protected NamedObj _recorderContainer;
657    
658    /** The containing provenance recorder. */
659    protected ProvenanceRecorder _recorder;
660    
661    /** The LSID of the containing workflow. */
662    protected KeplerLSID _containerLSID;
663    
664    /** The name of the containing workflow. */
665    protected String _containerName = null;
666    
667    /** A serializer for states. */
668    protected StateSerializer _stateSerializer;
669    
670    ////////////////////////////////////////////////////////////////////////
671    //// private variables                                              ////
672
673    /** If provenance recorder should register workflow contents. */
674    private boolean _needWorkflowContents = false;
675}