001/* 002 * Copyright (c) 2008-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2015-04-03 22:12:00 +0000 (Fri, 03 Apr 2015) $' 007 * '$Revision: 33311 $' 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.sql; 031 032import java.sql.PreparedStatement; 033import java.sql.ResultSet; 034import java.sql.SQLException; 035import java.sql.Timestamp; 036import java.util.Date; 037import java.util.Stack; 038 039import org.kepler.provenance.RecordingException; 040import org.kepler.util.sql.Schema; 041 042import ptolemy.actor.LocalClock; 043import ptolemy.actor.gui.ColorAttribute; 044import ptolemy.actor.gui.SizeAttribute; 045import ptolemy.actor.gui.WindowPropertiesAttribute; 046import ptolemy.actor.gui.style.LineStyle; 047import ptolemy.actor.gui.style.TextStyle; 048import ptolemy.actor.parameters.PortParameter; 049import ptolemy.kernel.undo.UndoStackAttribute; 050import ptolemy.kernel.util.ConfigurableAttribute; 051import ptolemy.kernel.util.Location; 052import ptolemy.kernel.util.NamedObj; 053import ptolemy.kernel.util.Settable; 054import ptolemy.vergil.basic.KeplerDocumentationAttribute; 055import ptolemy.vergil.icon.BoxedValueIcon; 056import ptolemy.vergil.icon.EditIconTableau; 057import ptolemy.vergil.icon.EditorIcon; 058import ptolemy.vergil.icon.TextIcon; 059import ptolemy.vergil.icon.UpdatedValueIcon; 060import ptolemy.vergil.icon.ValueIcon; 061import ptolemy.vergil.icon.XMLIcon; 062 063 064/** SQL Implementation of Recording using SDM SPA schema. 065 * 066 * This uses version 7 of the schema. The difference between the 067 * previous version is tracking changes in the workflow structure. 068 * 069 * @author Daniel Crawl 070 * @version $Id: SQLRecordingV7.java 33311 2015-04-03 22:12:00Z crawl $ 071 * 072 */ 073 074public class SQLRecordingV7 extends SQLRecording //Recording 075{ 076 077 public SQLRecordingV7() throws RecordingException 078 { 079 super(); 080 081 //_debug("sql recording constructor"); 082 083 _workflowChangeTimeStack = new Stack<Timestamp>(); 084 085 _dbReset(); 086 } 087 088 //////////////////////////////////////////////////////////////////////// 089 //// Specification interface //// 090 091 /** Called before registering workflow contents. */ 092 @Override 093 public void specificationStart() throws RecordingException 094 { 095 super.specificationStart(); 096 097 _possibleWorkflowChangeStart(); 098 } 099 100 /** Called when finished registering workflow contents. */ 101 @Override 102 public void specificationStop() throws RecordingException 103 { 104 super.specificationStop(); 105 106 _possibleWorkflowChangeStop(); 107 } 108 109 /** Register a parameter. A parameter can be any <b>entity</b> 110 * stored in the MoML that does not have its own 111 * <code>regNNN()</code> method. This can be user-level 112 * parameters (e.g., Parameter, StringParameter, etc.) or 113 * internal to Kepler (e.g., _location, semanticType000, etc.). 114 * (A "parameter" corresponds to a property in the MoML). 115 * 116 */ 117 @Override 118 public boolean regParameter(NamedObj parameter) throws RecordingException 119 { 120 boolean ignore = false; 121 122 String name = parameter.getName(); 123 124 if(parameter instanceof WindowPropertiesAttribute || 125 parameter instanceof ConfigurableAttribute || 126 parameter instanceof Location || 127 parameter instanceof KeplerDocumentationAttribute || 128 parameter instanceof SizeAttribute || 129 parameter instanceof ColorAttribute || 130 parameter instanceof TextIcon || 131 parameter instanceof EditIconTableau.Factory || 132 parameter instanceof TextStyle || 133 parameter instanceof ValueIcon || 134 parameter instanceof XMLIcon || 135 parameter instanceof LineStyle || 136 parameter instanceof UndoStackAttribute || 137 parameter instanceof BoxedValueIcon || 138 parameter instanceof UpdatedValueIcon || 139 parameter instanceof EditorIcon || 140 parameter instanceof org.kepler.moml.NamedObjIdReferralList || 141 parameter instanceof LocalClock || 142 name.equals("bold") || 143 name.equals("italic") || 144 name.equals("fontFamily") || 145 name.equals("textSize") || 146 name.equals("_notDraggable") || 147 name.indexOf("_vergil") == 0 || 148 name.indexOf("_hide") == 0 || 149 name.equals("_showName")) 150 { 151 ignore = true; 152 } 153 else 154 { 155 RegEntity re; 156 String fullNameStr = parameter.getFullName(); 157 RegEntity.EntityType type = null; 158 159 // if it's a PortParameter, we'll need to register both the 160 // port and parameter. 161 if(parameter instanceof PortParameter) 162 { 163 type = RegEntity.EntityType.PortParameter; 164 } 165 else 166 { 167 type = RegEntity.EntityType.Parameter; 168 } 169 re = _checkEntity(parameter, type); 170 171 172 // see if the parameter has been recorded 173 if(re.isNew()) 174 { 175 // register the parameter 176 _regParameterReal(parameter, re); 177 178 // if a port parameter, register the port 179 if(type == RegEntity.EntityType.PortParameter) 180 { 181 _regPortReal(((PortParameter)parameter).getPort(), re); 182 } 183 } 184 // see if the parameter has a value 185 else if(parameter instanceof Settable) 186 { 187 String value = ((Settable)parameter).getValueAsString(); 188 189 try 190 { 191 int id = re.getId(); 192 193 // see if paramater's value has changed 194 if(_isChangedParam(id, value)) 195 { 196 //_debug("SQL changed param: " + fullNameStr + 197 // " new value =" + value); 198 199 String changedName = _changeEntityFullName(fullNameStr); 200 201 // add a new row in the entity table with a new id 202 RegEntity newRe = _addEntity(re.getContainerId(), 203 type, changedName, parameter.getDisplayName(), 204 id); 205 206 // add a new row in the parameter table with the new value 207 _regParameterReal(parameter, newRe); 208 209 // if the parameter is a port parameter, we need to 210 // create a new row in the port table with the new 211 // id. the new id might be used by new rows in the 212 // token_flow table. 213 if(parameter instanceof PortParameter) 214 { 215 _regPortReal(((PortParameter)parameter).getPort(), 216 newRe); 217 } 218 219 // update the entity cache table with the new RegEntity 220 // since it has the new id. 221 _entityCacheTable.put(parameter, newRe); 222 } 223 } 224 catch(SQLException e) 225 { 226 _errorReset(); 227 throw new RecordingException("SQL ERROR: " + e.getMessage(), e); 228 } 229 } 230 } 231 232 return (! ignore); 233 } 234 235 //////////////////////////////////////////////////////////////////////// 236 //// Evolution interface //// 237 238 /** Start an evolution. */ 239 @Override 240 public void evolutionStart() throws RecordingException 241 { 242 super.evolutionStart(); 243 244 _possibleWorkflowChangeStart(); 245 } 246 247 /** Stop an evolution. */ 248 @Override 249 public void evolutionStop() throws RecordingException 250 { 251 super.evolutionStop(); 252 253 _possibleWorkflowChangeStop(); 254 } 255 256 //////////////////////////////////////////////////////////////////////// 257 //// protected methods //// 258 259 /** Add a new row to the entity table. */ 260 @Override 261 protected RegEntity _addEntity(int containerId, RegEntity.EntityType type, 262 String fullName, String displayName, int prevId) 263 throws RecordingException, SQLException 264 { 265 // see if we've recording the workflow change 266 if(_evolId == RegEntity.UNKNOWN_ID) 267 { 268 _addWorkflowChange(); 269 } 270 271 String typeStr = type.toString(); 272 273 //(wf_change_id, container_id, type, name, prev_id, wf_id) " + 274 _psEntityInsert.setInt(1, _evolId); 275 _psEntityInsert.setInt(2, containerId); 276 _psEntityInsert.setString(3, typeStr); 277 _psEntityInsert.setString(4, fullName); 278 _psEntityInsert.setInt(5, prevId); 279 _psEntityInsert.setInt(6, _wfId); 280 int newId = _dbType.insert(_psEntityInsert, "entity", "id"); 281 RegEntity retval = new RegEntity(newId, true, containerId, type); 282 283 // cache the new entry 284 //_entityCacheTable.put(fullName, retval); 285 286 if(_debugWriter != null) 287 { 288 _debugWrite("INSERT INTO ENTITY (" + _evolId + ", " + 289 containerId + ", " + type + ", " + fullName + ", " + prevId + 290 ", " + _wfId + ")"); 291 } 292 293 return retval; 294 } 295 296 /** Add a new row to the workflow table. */ 297 @Override 298 protected void _addWorkflow() throws RecordingException 299 { 300 try 301 { 302 synchronized(_psWorkflowInsert) 303 { 304 _psWorkflowInsert.setString(1, _wfNameStr); 305 _wfId = _dbType.insert(_psWorkflowInsert, "workflow", "id"); 306 _wfReset(); 307 } 308 309 if(_debugWriter != null) 310 { 311 _debugWrite("INSERT INTO WORKFLOW(" + _wfNameStr + ")"); 312 } 313 } 314 catch(SQLException e) 315 { 316 throw new RecordingException("Error adding row to workflow:", e); 317 } 318 } 319 320 /** Create a new row in the workflow_change table. */ 321 protected void _addWorkflowChange() throws RecordingException 322 { 323 if(_wfUserStr == null) 324 { 325 throw new RecordingException("Need workflow user name"); 326 } 327 328 try 329 { 330 synchronized(_psWorkflowChangeInsert) 331 { 332 _psWorkflowChangeInsert.setString(1, _wfUserStr); 333 _psWorkflowChangeInsert.setTimestamp(2, 334 _workflowChangeTimeStack.peek()); 335 _psWorkflowChangeInsert.setInt(3, _wfId); 336 _evolId = _dbType.insert(_psWorkflowChangeInsert, 337 "workflow_change", "id"); 338 339 if(_debugWriter != null) 340 { 341 _debugWrite("INSERT INTO WORKFLOW_CHANGE(" + 342 _wfUserStr + ", wfChangeTime, " + _wfId + ")"); 343 } 344 } 345 } 346 catch(SQLException e) 347 { 348 throw new RecordingException("Error adding to workflow_change: ", 349 e); 350 } 351 } 352 353 /** Initialize the prepared statements. */ 354 @Override 355 protected void _createPreparedStatements() throws SQLException 356 { 357 if(_psWorkflowInsert == null && _schema.containsTable("workflow")) 358 { 359 _psWorkflowInsert = _dbType.getSQLInsert("workflow", "id", "name", 360 "?"); 361 } 362 363 if(_psWorkflowChangeInsert == null && 364 _schema.containsTable("workflow_change")) 365 { 366 _psWorkflowChangeInsert = _dbType.getSQLInsert("workflow_change", 367 "id", "user, time, wf_id", "?, ?, ?"); 368 } 369 370 if(_psEntityInsert == null && _schema.containsTable("entity")) 371 { 372 _psEntityInsert = _dbType.getSQLInsert("entity", "id", 373 "wf_change_id, container_id, type, name, prev_id, wf_id", 374 "?, ?, ?, ?, ?, ?"); 375 } 376 377 if(_psEntityQuery == null && _schema.containsTable("entity") && 378 _schema.containsTable("workflow_change")) 379 { 380 String entityName = _dbType.getTableName("entity"); 381 String wfChangeName = _dbType.getTableName("workflow_change"); 382 String queryStr = "SELECT * FROM " + 383 entityName + " e , " + wfChangeName + " w " + 384 " WHERE e.wf_change_id = w.id AND w.wf_id = ? AND " + 385 "e.name = ? and e.type = ? ORDER BY e.id DESC"; 386 // NOTE: the following is not supported by hsql 387 //"entity.name = ? ORDER BY entity.id DESC LIMIT 1"; 388 _psEntityQuery = _dbType.getPrepStatement(queryStr); 389 } 390 391 if(_psParameterValueQuery == null && _schema.containsTable("parameter")) 392 { 393 _psParameterValueQuery = _dbType.getSQLSelect("parameter", "value", 394 "id = ?"); 395 } 396 397 if(_psWorkflowExecStart == null && 398 _schema.containsTable("workflow_exec")) 399 { 400 String defaultTimeStr = _dbType.getDefaultTimeStr(); 401 402 // workflow_id -> wf_id 403 _psWorkflowExecStart = _dbType.getSQLInsert("workflow_exec", "id", 404 "wf_id, user, start_time, end_time", "?, ?, ?, " + 405 defaultTimeStr); 406 } 407 408 // create the remainder prepared statements. 409 super._createPreparedStatements(); 410 } 411 412 /** Create a Schema to reflect the v7 schema. */ 413 @Override 414 protected Schema _createSchema() 415 { 416 return Schemas.createSchemaV7(); 417 } 418 419 /** Reset when we encounter an error. */ 420 @Override 421 protected void _errorReset() throws RecordingException 422 { 423 super._errorReset(); 424 _workflowChangeTimeStack.clear(); 425 } 426 427 /** Set our prepared statements to null. */ 428 @Override 429 protected void _nullPreparedStatements() 430 { 431 super._nullPreparedStatements(); 432 433 _psWorkflowChangeInsert = null; 434 _psParameterValueQuery = null; 435 } 436 437 /** Reset when we use a different workflow. */ 438 @Override 439 protected void _wfReset() 440 { 441 super._wfReset(); 442 _evolId = RegEntity.UNKNOWN_ID; 443 //System.out.println("reset evolId"); 444 } 445 446 //////////////////////////////////////////////////////////////////////// 447 //// protected variables //// 448 449 protected PreparedStatement _psWorkflowChangeInsert; 450 protected PreparedStatement _psParameterValueQuery; 451 452 /** Timestamp of current change to workflow structure. */ 453 protected Stack<Timestamp> _workflowChangeTimeStack; 454 455 /** ID of current change to workflow structure. */ 456 protected int _evolId; 457 458 //////////////////////////////////////////////////////////////////////// 459 //// private methods //// 460 461 /** See if a parameter changed its value. */ 462 private boolean _isChangedParam(int id, String value) 463 throws RecordingException, SQLException 464 { 465 boolean retval = false; 466 467 synchronized(_psParameterValueQuery) 468 { 469 _psParameterValueQuery.setInt(1, id); 470 try(ResultSet rs = _psParameterValueQuery.executeQuery();) { 471 472 if(!rs.next()) 473 { 474 throw new RecordingException("ERROR: could not find " + 475 "entity " + id + " in parameter table"); 476 } 477 478 String oldVal = rs.getString(1); 479 480 // if the old value was null, see if the new value is not. 481 // otherwise, compare the values. 482 if((oldVal == null && value.length() > 0) || 483 (oldVal != null && !oldVal.equals(value))) 484 { 485 retval = true; 486 } 487 } 488 } 489 return retval; 490 } 491 492 /** A workflow change may be starting. */ 493 protected void _possibleWorkflowChangeStart() throws RecordingException 494 { 495 // save the time 496 _workflowChangeTimeStack.push(new Timestamp(new Date().getTime())); 497 } 498 499 /** A possible workflow change is stopping. */ 500 protected void _possibleWorkflowChangeStop() throws RecordingException 501 { 502 // make sure change has started. 503 Timestamp curChangeTime = _workflowChangeTimeStack.pop(); 504 if(curChangeTime == null) 505 { 506 throw new RecordingException("ERROR: workflow change not in " + 507 "progress."); 508 } 509 510 _evolId = RegEntity.UNKNOWN_ID; 511 } 512}