001/* 002 * Copyright (c) 2007-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2015-04-20 20:58:30 +0000 (Mon, 20 Apr 2015) $' 007 * '$Revision: 33352 $' 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.provenance; 031 032import java.io.FileNotFoundException; 033import java.io.PrintStream; 034import java.text.SimpleDateFormat; 035import java.util.Date; 036 037import org.kepler.tagging.TagEvent; 038 039import ptolemy.actor.Actor; 040import ptolemy.actor.Director; 041import ptolemy.actor.FiringEvent; 042import ptolemy.actor.IOPortEvent; 043import ptolemy.actor.IORelation; 044import ptolemy.actor.LocalClock; 045import ptolemy.actor.TypedIOPort; 046import ptolemy.data.StringToken; 047import ptolemy.data.expr.FileParameter; 048import ptolemy.data.expr.Parameter; 049import ptolemy.kernel.Port; 050import ptolemy.kernel.util.AbstractSettableAttribute; 051import ptolemy.kernel.util.Attribute; 052import ptolemy.kernel.util.IllegalActionException; 053import ptolemy.kernel.util.NameDuplicationException; 054import ptolemy.kernel.util.Nameable; 055import ptolemy.kernel.util.NamedObj; 056 057/** A provenance Recording that writes to a text file. 058 * 059 * @author Daniel Crawl 060 * @version $Id: TextFileRecording.java 33352 2015-04-20 20:58:30Z crawl $ 061 * 062 */ 063 064public class TextFileRecording extends Recording 065{ 066 067 /** Create a new provenance recording. */ 068 public TextFileRecording() throws RecordingException 069 { 070 super(); 071 072 _dFormat = new SimpleDateFormat("HH:mm:ss"); 073 074 _needWorkflowContents(true); 075 } 076 077 /** Stop recording. This is the last method called to a 078 * Recording instance. 079 */ 080 @Override 081 public void disconnect() throws RecordingException 082 { 083 //_writeOutput("disconnect."); 084 _closeWriter(); 085 } 086 087 // Specification 088 089 /** Called before registering workflow contents. */ 090 @Override 091 public void specificationStart() throws RecordingException 092 { 093 super.specificationStart(); 094 095 if(_recordSpecVal) 096 { 097 _writeOutput("Specification started."); 098 } 099 } 100 101 /** Called when finished registering workflow contents. */ 102 @Override 103 public void specificationStop() throws RecordingException 104 { 105 super.specificationStop(); 106 107 if(_recordSpecVal) 108 { 109 _writeOutput("Specification stopped."); 110 } 111 } 112 113 /** Register an actor. */ 114 @Override 115 public boolean regActor(Actor actor) throws RecordingException 116 { 117 if(_recordSpecVal) 118 { 119 _writeOutput("Actor: " + _getNameableFullName(actor)); 120 } 121 return true; 122 } 123 124 /** Register a director. */ 125 @Override 126 public boolean regDirector(Director director) throws RecordingException 127 { 128 if(_recordSpecVal) 129 { 130 _writeOutput("Director: " + _getNameableFullName(director)); 131 } 132 return true; 133 } 134 135 /** Register a parameter. A parameter can be any <b>entity</b> 136 * stored in the MoML that does not have its own 137 * <code>regNNN()</code> method. This can be user-level 138 * parameters (e.g., Parameter, StringParameter, etc.) or 139 * internal to Kepler (e.g., _location, semanticType000, etc.). 140 * (A "parameter" corresponds to a property in the MoML). 141 * 142 */ 143 @Override 144 public boolean regParameter(NamedObj parameter) throws RecordingException 145 { 146 if(_recordSpecVal && !(parameter instanceof LocalClock)) 147 { 148 if(parameter instanceof AbstractSettableAttribute) 149 { 150 String output = "Parameter: " + _getNameableFullName(parameter) + 151 " = " + 152 ((AbstractSettableAttribute)parameter).getValueAsString(); 153 154 if(parameter instanceof org.kepler.moml.NamedObjId) 155 { 156 _writeOutput(output, false); 157 158 _debugWrite("Parameter: " + _getNameableFullName(parameter) + 159 " = lsid"); 160 } 161 else 162 { 163 _writeOutput(output); 164 } 165 } 166 else 167 { 168 _writeOutput("Parameter: " + _getNameableFullName(parameter)); 169 } 170 } 171 return true; 172 } 173 174 /** Register a link between two endpoints. */ 175 @Override 176 public boolean regLink(NamedObj endPoint1, NamedObj endPoint2) 177 throws RecordingException 178 { 179 if(_recordSpecVal) 180 { 181 _writeOutput("Link: " + _getNameableFullName(endPoint1) + " <---> " + 182 _getNameableFullName(endPoint2)); 183 } 184 return true; 185 } 186 187 /** Register a port or portparameter. */ 188 @Override 189 public boolean regPort(TypedIOPort port) throws RecordingException 190 { 191 if(_recordSpecVal) 192 { 193 _writeOutput("Port: " + _getNameableFullName(port)); 194 } 195 return true; 196 } 197 198 /** Register a relation. */ 199 @Override 200 public boolean regRelation(IORelation relation) throws RecordingException 201 { 202 if(_recordSpecVal) 203 { 204 _writeOutput("Relation: " + _getNameableFullName(relation)); 205 } 206 return true; 207 } 208 209 // Evolution of workflow structure. 210 211 @Override 212 public void evolutionStart() throws RecordingException 213 { 214 if(_recordSpecVal) 215 { 216 _writeOutput("Evolution started."); 217 } 218 } 219 220 @Override 221 public void evolutionStop() throws RecordingException 222 { 223 if(_recordSpecVal) 224 { 225 _writeOutput("Evolution stopped."); 226 } 227 } 228 229 @Override 230 public void remove(String name) throws RecordingException 231 { 232 if(_recordSpecVal) 233 { 234 _writeOutput("Removed: " + name); 235 } 236 } 237 238 @Override 239 public void removeLink(String endPoint1, String endPoint2) 240 throws RecordingException 241 { 242 if(_recordSpecVal) 243 { 244 _writeOutput("Removed link: " + endPoint1 + " <---> " + 245 endPoint2); 246 } 247 } 248 249 250 /** A NamedObj was renamed. */ 251 @Override 252 public void rename(String oldName, String newName) 253 throws RecordingException 254 { 255 if(_recordSpecVal) 256 { 257 _writeOutput("Rename: " + oldName + " ---> " + newName); 258 } 259 } 260 261 // Execution of workflow 262 263 /** Record the starting of workflow execution. */ 264 @Override 265 public void executionStart() throws RecordingException 266 { 267 _needWorkflowContents(false); 268 _writeOutput("Execution started."); 269 } 270 271 /** Record the stopping of workflow execution. */ 272 @Override 273 public void executionStop() throws RecordingException 274 { 275 _writeOutput("Execution stopped."); 276 } 277 278 /** An actor threw an exception. */ 279 @Override 280 public void executionError(Nameable source, Throwable throwable) 281 throws RecordingException 282 { 283 if(source != null) 284 { 285 _writeOutput("Execution error from " + source.getFullName() + 286 ": " + throwable.getMessage()); 287 } 288 else 289 { 290 _writeOutput("Execution error: " + throwable.getMessage()); 291 } 292 } 293 294 /** Record starting an actor fire event. */ 295 @Override 296 public void actorFire(FiringEvent event, Date timestamp) throws RecordingException 297 { 298 String outputStr; 299 if(_containerName != null) 300 { 301 String eventStr = event.toString(); 302 Actor actor = event.getActor(); 303 String fullName = actor.getFullName(); 304 outputStr = eventStr.replaceAll(fullName, _getNameableFullName(actor)); 305 } 306 else 307 { 308 outputStr = event.toString(); 309 } 310 311 _writeOutput(outputStr, timestamp); 312 } 313 314 /** Record a port event. */ 315 @Override 316 public void portEvent(IOPortEvent event, Date timestamp) throws RecordingException 317 { 318 String outputStr; 319 if(_containerName != null) 320 { 321 String eventStr = event.toString(); 322 Port port = event.getPort(); 323 String fullName = port.getFullName(); 324 outputStr = eventStr.replaceAll(fullName, _getNameableFullName(port)); 325 } 326 else 327 { 328 outputStr = event.toString(); 329 } 330 331 _writeOutput(outputStr, timestamp); 332 } 333 334 /** Record a custom provenance event. */ 335 @Override 336 public void customProvEvent(ProvenanceEvent event) throws RecordingException 337 { 338 _writeOutput(event.toString()); 339 } 340 341 /** Add Parameters for ProvenanceListener. */ 342 @Override 343 public RecordingParameters generateParameters(NamedObj no) 344 throws IllegalActionException, NameDuplicationException 345 { 346 _params = new TextFileRecordingParameters(no); 347 return _params; 348 } 349 350 /** React to a change in an attribute. */ 351 @Override 352 public void attributeChanged(Attribute attribute) 353 throws IllegalActionException 354 { 355 String name = attribute.getName(); 356 357 if(name.equals(TextFileRecordingParameters._recordSpecStr)) 358 { 359 boolean oldVal = _recordSpecVal; 360 _recordSpecVal = _params.getRecordSpecValue(); 361 362 // see if we now want the specfication 363 if(!oldVal && _recordSpecVal) 364 { 365 _needWorkflowContents(true); 366 } 367 else if(!_recordSpecVal) 368 { 369 _needWorkflowContents(false); 370 } 371 } 372 else if(name.equals(TextFileRecordingParameters._filenameStr)) 373 { 374 _resetWriter(); 375 } 376 else if(name.equals(TextFileRecordingParameters._alwaysFlushStr)) 377 { 378 _alwaysFlushVal = _params.getAlwaysFlushValue(); 379 380 // if should always flush and have existing writer, 381 // flush any pending output. 382 if(_alwaysFlushVal && _textWriter != null) 383 { 384 _textWriter.flush(); 385 } 386 387 } 388 else if(name.equals(TextFileRecordingParameters._addTimestampStr)) 389 { 390 _addTimestampVal = _params.getAddTimestampValue(); 391 } 392 else 393 { 394 super.attributeChanged(attribute); 395 } 396 } 397 398 /** A tag was added. */ 399 @Override 400 public void tagAdded(TagEvent event) throws RecordingException 401 { 402 _writeOutput("Tag added: " + event.toString()); 403 } 404 405 /** A tag was removed. */ 406 @Override 407 public void tagRemoved(TagEvent event) throws RecordingException 408 { 409 _writeOutput("Tag removed: " + event.toString()); 410 } 411 412 /** Set if the output should have a timestamp. */ 413 public void setAddTimestamp(boolean addTimestamp) { 414 _addTimestampVal = addTimestamp; 415 } 416 417 //////////////////////////////////////////////////////////////////////// 418 //// protected methods //// 419 420 /** Output a string. */ 421 protected void _write(String str) throws RecordingException 422 { 423 _write(str, true); 424 } 425 426 /** Output a string. */ 427 protected void _write(String str, boolean outputDebug) throws RecordingException 428 { 429 if(_textWriter != null) 430 { 431 _textWriter.println(str); 432 if(_alwaysFlushVal) 433 { 434 _textWriter.flush(); 435 } 436 } 437 438 if(outputDebug && _debugWriter != null) 439 { 440 _debugWrite(str); 441 } 442 } 443 444 /** Output a string, optionally adding a timestamp. */ 445 protected void _writeOutput(String str) throws RecordingException 446 { 447 _writeOutput(str, true, null); 448 } 449 450 /** Output a string, optionally adding a timestamp. */ 451 protected void _writeOutput(String str, Date timestamp) throws RecordingException 452 { 453 _writeOutput(str, true, timestamp); 454 } 455 456 /** Output a string, optionally adding a timestamp. */ 457 protected void _writeOutput(String str, boolean outputDebug) 458 throws RecordingException { 459 _writeOutput(str, outputDebug, null); 460 } 461 462 /** Output a string, optionally adding a timestamp. */ 463 protected void _writeOutput(String str, boolean outputDebug, Date timestamp) 464 throws RecordingException 465 { 466 if(_addTimestampVal) 467 { 468 Date date; 469 if(timestamp == null) { 470 date = new Date(); 471 } else { 472 date = timestamp; 473 } 474 String dateStr = _dFormat.format(date); 475 _write(dateStr + ": " + str, outputDebug); 476 } 477 else 478 { 479 _write(str, outputDebug); 480 } 481 } 482 483 //////////////////////////////////////////////////////////////////////// 484 //// protected classes //// 485 486 /** Configuration Parameters for TextFileRecording. */ 487 protected class TextFileRecordingParameters extends RecordingParameters 488 { 489 TextFileRecordingParameters(NamedObj no) 490 throws IllegalActionException, NameDuplicationException 491 { 492 super(no); 493 addBooleanParameter(_recordSpecStr, _recordSpecVal); 494 addFileParameter(_filenameStr, "System.out"); 495 addBooleanParameter(_alwaysFlushStr, _alwaysFlushVal); 496 addBooleanParameter(_addTimestampStr, _addTimestampVal); 497 } 498 499 /** Get the "Record Specification" check-box value. */ 500 boolean getRecordSpecValue() throws IllegalActionException 501 { 502 return getBooleanValue(_recordSpecStr); 503 } 504 505 /** Get the output FileParameter. */ 506 FileParameter getFileParameter() throws IllegalActionException 507 { 508 return (FileParameter)_params.get(_filenameStr); 509 } 510 511 /** Get "Always Flush Output" check-box value. */ 512 boolean getAlwaysFlushValue() throws IllegalActionException 513 { 514 return getBooleanValue(_alwaysFlushStr); 515 } 516 517 /** Get "Timestamp Output" check-box value. */ 518 boolean getAddTimestampValue() throws IllegalActionException 519 { 520 return getBooleanValue(_addTimestampStr); 521 } 522 523 /** Replace a Parameter. */ 524 @Override 525 public void replaceParameter(String name, Parameter parameter) 526 throws IllegalActionException 527 { 528 // if replacing the filename, close the file. 529 if(name.equals(_filenameStr)) 530 { 531 FileParameter p = (FileParameter)_params.get(_filenameStr); 532 p.close(); 533 } 534 535 super.replaceParameter(name, parameter); 536 } 537 538 private static final String _recordSpecStr = "Record Specification"; 539 private static final String _filenameStr = "Filename"; 540 private static final String _alwaysFlushStr = "Always Flush Output"; 541 private static final String _addTimestampStr = "Timestamp Output"; 542 } 543 544 //////////////////////////////////////////////////////////////////////// 545 //// protected variables //// 546 547 /** Whether to record workflow specification. */ 548 protected boolean _recordSpecVal = true; 549 550 /** Output writer. */ 551 protected PrintStream _textWriter = null; 552 553 /** If true, flush after writing to output. */ 554 protected boolean _alwaysFlushVal = false; 555 556 /** If true, add timestamp to output. */ 557 protected boolean _addTimestampVal = true; 558 559 /** Format timestamps. */ 560 protected SimpleDateFormat _dFormat = null; 561 562 /** Parameters for TextFileRecording */ 563 protected TextFileRecordingParameters _params = null; 564 565 /** Output name. */ 566 protected String _outputName = null; 567 568 //////////////////////////////////////////////////////////////////////// 569 //// private methods //// 570 571 /** Reset the output writer. */ 572 private void _resetWriter() throws IllegalActionException 573 { 574 FileParameter fp = _params.getFileParameter(); 575 String name = ((StringToken)fp.getToken()).stringValue(); 576 577 // see if the output name has changed or was never set 578 if(_outputName == null || !name.equals(_outputName)) 579 { 580 _closeWriter(); 581 582 // see if we should write to stdout 583 if(name.equals("System.out")) 584 { 585 _textWriter = System.out; 586 } 587 // see if we should write to stderr 588 else if(name.equals("System.err")) 589 { 590 _textWriter = System.err; 591 } 592 else if(name.equals("NULL")) 593 { 594 _textWriter = null; 595 } 596 else 597 { 598 // remove possible "file:" prefix 599 if(name.startsWith("file:")) 600 { 601 name = name.substring("file:".length()); 602 } 603 604 try 605 { 606 _textWriter = new PrintStream(name); 607 } 608 catch(FileNotFoundException e) 609 { 610 throw new IllegalActionException("Could not find file " + 611 name + " : " + e.getMessage()); 612 } 613 } 614 615 // save the name 616 _outputName = name; 617 } 618 619 // if we're configured to recording workflow structure, 620 // tell listener to re-send the structure since the file 621 // changed. 622 _needWorkflowContents(_recordSpecVal); 623 } 624 625 /** Close the writer unless it is stdout or stderr. */ 626 private void _closeWriter() 627 { 628 if(_textWriter != null) 629 { 630 // don't close stdout or stderr 631 if(_outputName == null || 632 (!_outputName.equals("System.out") && 633 !_outputName.equals("System.err"))) 634 { 635 _textWriter.close(); 636 } 637 else 638 { 639 _textWriter.flush(); 640 } 641 } 642 } 643}