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}