001/* 002 * Copyright (c) 2003-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2013-03-27 18:58:38 +0000 (Wed, 27 Mar 2013) $' 007 * '$Revision: 31776 $' 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.objectmanager.cache; 031 032import java.io.File; 033import java.io.FileInputStream; 034import java.io.FileNotFoundException; 035import java.io.FileOutputStream; 036import java.io.IOException; 037import java.io.ObjectInputStream; 038import java.io.ObjectOutputStream; 039import java.sql.Connection; 040import java.sql.Date; 041import java.sql.PreparedStatement; 042import java.sql.ResultSet; 043import java.sql.SQLException; 044import java.sql.Statement; 045import java.util.Collection; 046import java.util.Hashtable; 047import java.util.Iterator; 048import java.util.LinkedList; 049import java.util.List; 050import java.util.Vector; 051 052import org.apache.commons.logging.Log; 053import org.apache.commons.logging.LogFactory; 054import org.kepler.build.modules.ModuleTree; 055import org.kepler.kar.KAREntryHandler; 056import org.kepler.kar.KAREntryHandlerFactory; 057import org.kepler.objectmanager.lsid.KeplerLSID; 058import org.kepler.util.DotKeplerManager; 059import org.kepler.util.sql.DatabaseFactory; 060 061import ptolemy.actor.gui.Configuration; 062import ptolemy.util.MessageHandler; 063 064/** 065 * This class represents a disk cache of CacheObjects. The cache manages cache 066 * objects by calling their lifecycle event handlers and serializing every time 067 * a change is made. Objects in the cache each have a unique lsid. Once an 068 * object is in the cache, it will not be updated unless the lsid changes. 069 * 070 * this class uses hsql to keep track of cache entries. 071 */ 072public class CacheManager { 073 074 private static final Log log = LogFactory.getLog(CacheManager.class 075 .getName()); 076 private static final boolean isDebugging = log.isDebugEnabled(); 077 078 private static CacheManager cache = null; 079 080 private Hashtable<String, CacheObjectInterface> objectHash = new Hashtable<String, CacheObjectInterface>(); 081 082 private Vector<CacheListener> listeners = new Vector<CacheListener>(); 083 private Vector<KAREntryHandler> karEntryHandlers = new Vector<KAREntryHandler>(); 084 085 protected static final String cachePath = DotKeplerManager.getInstance() 086 .getCacheDirString(); 087 /** The directory containing serialized java objects in the cache. */ 088 private static final String objectPath = cachePath + "objects" 089 + File.separator + getDatabaseSchemaName(); 090 public static final String tmpPath = cachePath + "tmp"; 091 public static final String CACHETABLENAME = "cacheContentTable"; 092 public static final String CACHE_SEMTYPES_TABLE_NAME = "CACHE_SEMTYPES"; 093 094 private Connection _conn; 095 private Statement _stmt; 096 097 private PreparedStatement _cacheInsertStatement; 098 private PreparedStatement _cacheSemTypesInsertStmt; 099 private PreparedStatement _cacheUpdateStatement; 100 private PreparedStatement _getLsidForLsidPrepStmt; 101 private PreparedStatement _getSemTypeForLsidPrepStmt; 102 private PreparedStatement _getLsidsForClassPrepStmt; 103 104 /** 105 * Construct a new CacheManager 106 */ 107 protected CacheManager() throws CacheException { 108 if (isDebugging) 109 log.debug("new CacheManager()"); 110 111 try { 112 _conn = DatabaseFactory.getDBConnection(); 113 _stmt = _conn.createStatement(); 114 } catch (Exception e) { 115 e.printStackTrace(); 116 } 117 118 try { 119 if (isDebugging) log.debug(countDB() + " Items Cached."); 120 121 _cacheInsertStatement = _conn 122 .prepareStatement("insert into " 123 + CACHETABLENAME 124 + " (name, lsid, date, file, type, classname) values ( ?, ?, ?, ?, ?, ? )"); 125 _cacheSemTypesInsertStmt = _conn.prepareStatement("insert into " 126 + CACHE_SEMTYPES_TABLE_NAME 127 + " (LSID,SEMTYPE) values ( ?, ?)"); 128 _cacheUpdateStatement = _conn.prepareStatement("update " 129 + CACHETABLENAME + " set name = ?," // 1 130 + " date = ?," // 2 131 + " file = ?," // 3 132 + " type = ? " // 4 133 + " where lsid = ?"); // 5 134 135 _getLsidForLsidPrepStmt = _conn.prepareStatement("select LSID from " 136 + CACHETABLENAME + " where lsid = ?"); 137 138 _getSemTypeForLsidPrepStmt = _conn.prepareStatement("SELECT SEMTYPE FROM " 139 + CACHE_SEMTYPES_TABLE_NAME + " WHERE LSID = ?"); 140 141 _getLsidsForClassPrepStmt = _conn.prepareStatement("select LSID from " 142 + CACHETABLENAME + " where classname = ?"); 143 144 } catch (SQLException e) { 145 e.printStackTrace(); 146 } 147 148 // create the directory for the serialized java objects if it 149 // does not exist 150 File objectDir = new File(objectPath); 151 if(!objectDir.exists() && !objectDir.mkdirs()) { 152 MessageHandler.error("Could not create directories " + objectDir); 153 } 154 155 } 156 157 /** 158 * create a new singleton instance of CacheManager 159 */ 160 public static synchronized CacheManager getInstance() throws CacheException { 161 if (cache == null) { 162 cache = new CacheManager(); 163 cache.initKAREntryHandlers(); 164 } 165 return cache; 166 } 167 168 /** Shutdown the cache manager. */ 169 public static void shutdown() { 170 171 if(cache != null) { 172 173 cache.objectHash.clear(); 174 cache.listeners.clear(); 175 cache.karEntryHandlers.clear(); 176 177 try { 178 cache._stmt.close(); 179 cache._stmt = null; 180 cache._conn.close(); 181 cache._conn = null; 182 } catch(SQLException e) { 183 MessageHandler.error("Error closing database connection.", e); 184 } 185 186 cache = null; 187 } 188 } 189 190 /** 191 * returns a temp file that is guaranteed to be around for one kepler 192 * session but could be deleted if the cache gets too full. 193 */ 194 public synchronized File getTempFile() { 195 File f = new File(tmpPath); 196 f.mkdirs(); 197 f = new File(tmpPath, "tmp" + System.currentTimeMillis()); 198 // TODO: register this file somewhere so it can get deleted later 199 return f; 200 } 201 202 /** 203 * @param keh 204 */ 205 public void addKAREntryHandler(KAREntryHandler keh) { 206 if (isDebugging) 207 log.debug("addKAREntryHandler(" + keh.getClass().getName() + ")"); 208 karEntryHandlers.add(keh); 209 } 210 211 public Collection<KAREntryHandler> getKAREntryHandlers() { 212 return karEntryHandlers; 213 } 214 215 /** 216 * Initialize all KAREntryHandlers that have been specified in 217 * configuration. 218 */ 219 private void initKAREntryHandlers() { 220 if (isDebugging) 221 log.debug("initKAREntryHandlers"); 222 223 List configsList = Configuration.configurations(); 224 Configuration config = null; 225 for (Iterator it = configsList.iterator(); it.hasNext();) { 226 config = (Configuration) it.next(); 227 if (config != null) { 228 break; 229 } 230 } 231 if (config != null) { 232 // KAREntryHandlerFactory KEHFactory = (KAREntryHandlerFactory) 233 // config 234 // .getAttribute("karEntryHandlerFactory"); 235 try { 236 KAREntryHandlerFactory KEHFactory = new KAREntryHandlerFactory( 237 config, "KarEntryHandlerFactory"); 238 239 if (KEHFactory != null) { 240 boolean success = KEHFactory.registerKAREntryHandlers(); 241 if (!success) { 242 System.out 243 .println("error: karEntryHandlerFactory is null. " 244 + "This " 245 + "problem can be fixed by adding a karEntryHandlerFactory " 246 + "property in the configuration.xml file."); 247 } 248 } else { 249 System.out 250 .println("error: KAREntryHandlerFactory is null. This " 251 + "problem can be fixed by adding a karEntryHandlerFactory " 252 + "property in the configuration.xml file."); 253 } 254 } catch (ptolemy.kernel.util.NameDuplicationException nde) { 255 // do nothing. the property is already there. 256 System.out.println("#######################"); 257 } catch (Exception e) { 258 System.out.println("Could not create KarEntryHandlerFactory: " 259 + e.getMessage()); 260 e.printStackTrace(); 261 } 262 } 263 } 264 265 /** 266 * insert a new CacheObjectInterface into the cache. 267 * 268 * @param co 269 * the cache object to insert 270 */ 271 public synchronized void insertObject(CacheObjectInterface co) 272 throws CacheException { 273 if (co == null) 274 return; 275 // get the critical info 276 String name = co.getName(); 277 KeplerLSID klsid = co.getLSID(); 278 if (klsid == null) { 279 log.warn("KAREntry has no lsid: " + name); 280 return; 281 } 282 String lsid = klsid.toString(); 283 String date = String.valueOf(System.currentTimeMillis()); 284 String filename = co.getLSID().createFilename(); 285 if (isDebugging) 286 log.debug(name + " " + lsid + " " + filename); 287 if (isDebugging) 288 log.debug(co.getClass().getName()); 289 290 // save the entry to the DB 291 try { 292 _cacheInsertStatement.clearParameters(); 293 _cacheInsertStatement.setString(1, name); 294 _cacheInsertStatement.setString(2, lsid.toString()); 295 _cacheInsertStatement.setString(3, date); 296 _cacheInsertStatement.setString(4, filename); 297 _cacheInsertStatement.setString(5, co.getClass().getName()); 298 if (co instanceof ActorCacheObject) { 299 String className = ((ActorCacheObject) co).getClassName(); 300 _cacheInsertStatement.setString(6, className); 301 } else { 302 _cacheInsertStatement.setNull(6, java.sql.Types.VARCHAR); 303 } 304 objectHash.put(lsid, co); 305 serializeObjectInFile(co, filename); 306 _cacheInsertStatement.executeUpdate(); 307 308 // insert the semantic types for this object 309 if (co instanceof CacheObject) { 310 Vector<String> semTypes = ((CacheObject) co).getSemanticTypes(); 311 for (String semType : semTypes) { 312 _cacheSemTypesInsertStmt.clearParameters(); 313 _cacheSemTypesInsertStmt.setString(1, lsid.toString()); 314 _cacheSemTypesInsertStmt.setString(2, semType); 315 _cacheSemTypesInsertStmt.executeUpdate(); 316 } 317 } 318 319 _conn.commit(); 320 } catch (Exception sqle) { 321 log.error(sqle.getMessage()); 322 try { 323 _conn.rollback(); 324 } catch (Exception e) { 325 throw new CacheException( 326 "Could not roll back the database after error " 327 + sqle.getMessage(), e); 328 } 329 // sqle.printStackTrace(); 330 log 331 .error("Could not insert entry into cache: " + name + " " 332 + lsid); 333 throw new CacheException( 334 "Could not create hsql entry for new CacheObjectInterface: ", 335 sqle); 336 } 337 notifyListeners(co, "add"); 338 } 339 340 /** 341 * update a CacheObjectInterface in the cache. 342 */ 343 public synchronized void updateObject(CacheObjectInterface co) 344 throws CacheException { 345 // get the critical info 346 String name = co.getName(); 347 String lsid = co.getLSID().toString(); 348 String date = String.valueOf(System.currentTimeMillis()); 349 String filename = co.getLSID().createFilename(); 350 String coType = co.getClass().getName(); 351 // save the entry to the DB 352 try { 353 _cacheUpdateStatement.setString(1, name); 354 _cacheUpdateStatement.setString(2, date); 355 _cacheUpdateStatement.setString(3, filename); 356 _cacheUpdateStatement.setString(4, coType); 357 _cacheUpdateStatement.setString(5, lsid.toString()); 358 _cacheUpdateStatement.executeUpdate(); 359 _cacheUpdateStatement.clearParameters(); 360 serializeObjectInFile(co, filename); 361 _conn.commit(); 362 } catch (Exception sqle) { 363 try { 364 _conn.rollback(); 365 } catch (Exception e) { 366 throw new CacheException( 367 "Could not roll back the database after error " 368 + sqle.getMessage(), e); 369 } 370 throw new CacheException( 371 "Could not create hsql entry for new CacheObjectInterface: ", 372 sqle); 373 } 374 notifyListeners(co, "update"); 375 } 376 377 /** 378 * remove the CacheObjectInterface with the specified lsid. 379 * 380 * @param lsid 381 */ 382 public void removeObject(KeplerLSID lsid) throws CacheException { 383 // grab a copy from the hash to use for calling listeners. There will be 384 // no listeners on an object returned from the db. 385 CacheObjectInterface co = (CacheObjectInterface) objectHash.get(lsid 386 .toString()); 387 try { 388 String sql = "SELECT file FROM " + CACHETABLENAME + " WHERE lsid='" 389 + lsid.toString() + "'"; 390 if (isDebugging) 391 log.debug(sql); 392 ResultSet rs = _stmt.executeQuery(sql); 393 if (rs == null) 394 throw new SQLException("Query Failed: " + sql); 395 if (rs.next()) { 396 File f = new File(objectPath, rs.getString("file")); 397 if (isDebugging) 398 log.debug(f.toString()); 399 if (f.exists()) { 400 f.delete(); 401 } 402 } 403 rs.close(); 404 405 try { 406 sql = "DELETE FROM " + CACHETABLENAME + " WHERE lsid='" 407 + lsid.toString() + "'"; 408 if (isDebugging) { 409 // log.debug(showDB()); 410 log.debug(sql); 411 } 412 _stmt.execute(sql); 413 } catch (Exception e) { 414 log.error(lsid.toString() + " did not exist in " 415 + CACHETABLENAME); 416 log.error(e.getMessage()); 417 } 418 _conn.commit(); 419 objectHash.remove(lsid.toString()); 420 } catch (Exception e) { 421 try { 422 _conn.rollback(); 423 } catch (Exception e1) { 424 throw new CacheException( 425 "Could not roll back the database after error " 426 + e.getMessage(), e1); 427 } 428 throw new CacheException("Error removing object " + lsid 429 + " from the cache", e); 430 } 431 if (co != null) { 432 notifyListeners(co, "remove"); 433 } 434 } 435 436 /** 437 * debug method to show the contents of the table 438 * 439 * @throws CacheException, SQLException 440 */ 441 public String showDB() throws CacheException, SQLException { 442 StringBuilder buf = new StringBuilder(); 443 PreparedStatement selectAll; 444 // Create the prepared statement. 445 try { 446 // FIXME reuse this prepared statement 447 selectAll = _conn 448 .prepareStatement("select name, lsid, file, type from " 449 + CACHETABLENAME); 450 } catch (SQLException e) { 451 e.printStackTrace(); 452 throw new CacheException("Unable to create prepared statement."); 453 } 454 try { 455 ResultSet rs = selectAll.executeQuery(); 456 while (rs.next()) { 457 // name, lsid, file, type 458 String name = rs.getString("name"); 459 String lsid = rs.getString("lsid"); 460 String file = rs.getString("file"); 461 String coType = rs.getString("type"); 462 buf.append("name: "); 463 buf.append(name); 464 buf.append(" lsid: "); 465 buf.append(lsid); 466 buf.append(" file: "); 467 buf.append(file); 468 buf.append(" type: "); 469 buf.append(coType); 470 buf.append("\n"); 471 } 472 } catch (Exception e) { 473 System.out.println("error showing db: " + e.getMessage()); 474 } finally{ 475 selectAll.close(); 476 } 477 return buf.toString(); 478 } 479 480 /** 481 * debug method to count the contents of the table * @throws CacheException 482 */ 483 public int countDB() throws CacheException, SQLException { 484 PreparedStatement selectAll = null; 485 // Create the prepared statements. 486 try { 487 selectAll = _conn.prepareStatement("select lsid from " 488 + CACHETABLENAME); 489 } catch (SQLException e) { 490 e.printStackTrace(); 491 throw new CacheException("Unable to create prepared statements."); 492 } 493 int cnt = 0; 494 try { 495 ResultSet rs = selectAll.executeQuery(); 496 while (rs.next()) { 497 cnt++; 498 } 499 } catch (Exception e) { 500 System.out.println("error counting db: " + e.getMessage()); 501 } finally{ 502 selectAll.close(); 503 } 504 return cnt; 505 506 } 507 508 /** 509 * return the CacheObjectInterface with the specified lsid. Returns null if 510 * the object is not in the cache and cannot be resolved. 511 * 512 * @param lsid 513 * @throws CacheException 514 * when there is an issue with the cache. 515 */ 516 public CacheObjectInterface getObject(KeplerLSID lsid) 517 throws CacheException { 518 if (isDebugging) 519 log.debug(lsid.toString()); 520 // first look in the hash table 521 CacheObjectInterface co = (CacheObjectInterface) objectHash.get(lsid 522 .toString()); 523 524 // Found it in the hash - return it. 525 if (co != null) { 526 /* 527 * using if (isDebugging) here is bad since it cause getObject to be called 528 * when it is not supposed to, thus invoking the momlparser 529 log.debug(" was found in the hash"); 530 if (co.getObject() != null) { 531 if (co.getObject() instanceof NamedObj) { 532 log.debug("NamedObj: " 533 + ((NamedObj) co.getObject()).getName()); 534 } else { 535 log.debug("Object: " 536 + co.getObject().getClass().getName()); 537 } 538 } else { 539 log 540 .debug("the object contained by this cache object is NULL"); 541 } 542 */ 543 return co; 544 } 545 546 // Now look in the database: 547 try { 548 String query = "select name, lsid, file from " + CACHETABLENAME 549 + " where lsid='" + lsid.toString() + "'"; 550 ResultSet rs = null; 551 try { 552 rs = _stmt.executeQuery(query); 553 if (rs == null) 554 throw new SQLException("Query Failed: " + query); 555 if (rs.next()) { 556 // found it in the database. 557 String name = rs.getString("name"); 558 String file = rs.getString("file"); 559 if (isDebugging) 560 log.debug(name + " " + file); 561 File theObjectFile = new File(objectPath, file); 562 if (isDebugging) 563 log.debug(theObjectFile.toString()); 564 FileInputStream fileInputStream = null; 565 ObjectInputStream ois = null; 566 try { 567 fileInputStream = new FileInputStream(theObjectFile); 568 ois = new ObjectInputStream(fileInputStream); 569 // deserialize the CacheObjectInterface 570 co = (CacheObjectInterface) ois.readObject(); 571 co.setName(name); 572 co.setLSID(lsid); 573 // add the CacheObjectInterface to the hashtable for easier 574 // access next time 575 objectHash.put(lsid.toString(), co); 576 return co; 577 } finally { 578 if(ois != null) { 579 ois.close(); 580 } 581 if(fileInputStream != null) { 582 fileInputStream.close(); 583 } 584 } 585 } 586 } finally { 587 if(rs != null) { 588 rs.close(); 589 } 590 } 591 } catch (SQLException sqle) { 592 sqle.printStackTrace(); 593 throw new CacheException("SQL exception when getting object", sqle); 594 } catch (Exception e) { 595 throw new CacheException( 596 "Exception occurred while deserializing object", e); 597 } 598 return co; 599 } 600 601 public Vector<CacheContent> getCachedContents() { 602 return getCachedContents(""); 603 } 604 605 /** 606 * @param type 607 * @return 608 */ 609 public Vector<CacheContent> getCachedContents(String type) { 610 611 Vector<CacheContent> contents = new Vector<CacheContent>(); 612 613 String query = "SELECT NAME,LSID,DATE,FILE,TYPE,CLASSNAME FROM " 614 + CACHETABLENAME; 615 if (!type.trim().equals("")) { 616 query += " WHERE TYPE = '" + type + "'"; 617 } 618 try { 619 ResultSet rs = null; 620 try { 621 rs = _stmt.executeQuery(query); 622 if (rs == null) 623 throw new SQLException("Query Failed: " + query); 624 while (rs.next()) { 625 CacheContent cc = new CacheContent(); 626 cc.setName(rs.getString(1)); 627 try { 628 KeplerLSID lsid = new KeplerLSID(rs.getString(2)); 629 cc.setLsid(lsid); 630 Long l = Long.parseLong(rs.getString(3)); 631 Date d = new Date(l); 632 cc.setDateChanged(d); 633 File f = new File(rs.getString(4)); 634 cc.setFile(f); 635 cc.setType(rs.getString(5)); 636 cc.setClassName(rs.getString(6)); 637 contents.add(cc); 638 } catch (Exception e) { 639 e.printStackTrace(); 640 } 641 } 642 } finally { 643 if(rs != null) { 644 rs.close(); 645 } 646 } 647 } catch (SQLException e1) { 648 e1.printStackTrace(); 649 contents = new Vector<CacheContent>(); 650 } 651 652 return contents; 653 654 } 655 656 /** 657 * Return a complete list of KeplerLSIDs for everything that is in the 658 * cache. 659 * 660 * @return 661 */ 662 public Vector<KeplerLSID> getCachedLsids() { 663 return getCachedLsids(null); 664 } 665 666 /** 667 * @param lsid 668 * @return 669 */ 670 public Vector<KeplerLSID> getCachedLsids(KeplerLSID lsid) { 671 Vector<KeplerLSID> lsids = new Vector<KeplerLSID>(); 672 673 String query = "SELECT LSID FROM " + CACHETABLENAME; 674 if (lsid != null) { 675 query += " WHERE LSID like '" + lsid.toStringWithoutRevision() + "%'"; 676 } 677 if (isDebugging) log.debug(query); 678 ResultSet rs; 679 try { 680 rs = _stmt.executeQuery(query); 681 if (rs == null) 682 throw new SQLException("Query Failed: " + query); 683 while (rs.next()) { 684 String lsidStr = rs.getString(1); 685 try { 686 KeplerLSID cachedLsid = new KeplerLSID(lsidStr); 687 lsids.add(cachedLsid); 688 } catch (Exception e) { 689 e.printStackTrace(); 690 } 691 } 692 rs.close(); 693 } catch (SQLException e1) { 694 e1.printStackTrace(); 695 lsids = new Vector<KeplerLSID>(); 696 } 697 698 return lsids; 699 } 700 701 /** Get a list of LSIDs for a class name. */ 702 public List<KeplerLSID> getCachedLsidsForClass(String className) throws Exception { 703 List<KeplerLSID> retval = new LinkedList<KeplerLSID>(); 704 ResultSet result = null; 705 try { 706 _getLsidsForClassPrepStmt.setString(1, className); 707 result = _getLsidsForClassPrepStmt.executeQuery(); 708 while (result.next()) { 709 retval.add(new KeplerLSID(result.getString(1))); 710 } 711 } finally { 712 if (result != null) { 713 result.close(); 714 } 715 } 716 return retval; 717 } 718 719 /** 720 * @param lsid 721 * @return 722 */ 723 public Long getHighestCachedLsidRevision(KeplerLSID lsid) { 724 Long highestRev = 0L; 725 Vector<KeplerLSID> cachedLsids = getCachedLsids(lsid); 726 for (KeplerLSID cachedLsid : cachedLsids) { 727 Long thisRev = cachedLsid.getRevision(); 728 if (thisRev > highestRev) { 729 highestRev = thisRev; 730 } 731 } 732 return highestRev; 733 } 734 735 /** 736 * Return a list of Semantic Types for the given LSID. 737 * 738 * @param lsid 739 * @return 740 */ 741 public Vector<KeplerLSID> getSemanticTypesFor(KeplerLSID lsid) { 742 743 Vector<KeplerLSID> semTypes = new Vector<KeplerLSID>(); 744 745 ResultSet rs; 746 try { 747 _getSemTypeForLsidPrepStmt.setString(1, lsid.toString()); 748 rs = _getSemTypeForLsidPrepStmt.executeQuery(); 749 if (rs == null) 750 throw new SQLException("Query Failed: " + _getSemTypeForLsidPrepStmt); 751 while (rs.next()) { 752 String lsidStr = rs.getString(1); 753 try { 754 KeplerLSID semType = new KeplerLSID(lsidStr); 755 semTypes.add(semType); 756 } catch (Exception e) { 757 e.printStackTrace(); 758 } 759 } 760 rs.close(); 761 } catch (SQLException e1) { 762 e1.printStackTrace(); 763 semTypes = new Vector<KeplerLSID>(); 764 } 765 766 return semTypes; 767 768 } 769 770 /** 771 * return an iterator of all of the CacheObjectInterfaces in the cache 772 */ 773 public Iterator<CacheObjectInterface> getCacheObjectIterator() 774 throws CacheException { 775 return getCacheObjectIterator(""); 776 } 777 778 /** 779 * Return the CacheObject that has the highest revision number and matches 780 * the given LSID, return null if not found. 781 * 782 * @param anLsid 783 * @return 784 * @throws CacheException 785 */ 786 public CacheObject getHighestCacheObjectRevision(KeplerLSID anLsid) 787 throws CacheException { 788 789 CacheObject coWithHighestLSID = null; 790 try { 791 long highestRev = 0; 792 Vector<KeplerLSID> cachedLsids = getCachedLsids(); 793 for (KeplerLSID lsid : cachedLsids) { 794 if (lsid.equalsWithoutRevision(anLsid)) { 795 if (lsid.getRevision() > highestRev) { 796 highestRev = lsid.getRevision(); 797 } 798 } 799 } 800 801 String highestRevLsidStr = anLsid.toStringWithoutRevision() + ":" 802 + highestRev; 803 KeplerLSID highestRevLsid = new KeplerLSID(highestRevLsidStr); 804 805 coWithHighestLSID = (CacheObject) getObject(highestRevLsid); 806 } catch (Exception e) { 807 log.error(e.getMessage()); 808 e.printStackTrace(); 809 } 810 return coWithHighestLSID; 811 } 812 813 /** 814 * Read the Java Object Serialized in the given file and add it to the 815 * objectHash. 816 * 817 * @param file 818 * @return 819 * @throws Exception 820 */ 821 private CacheObjectInterface deserializeCacheObject(String file) 822 throws Exception { 823 // deserialize the cache object and put it in the hash 824 File f = new File(objectPath, file); 825 try { 826 FileInputStream fis = new FileInputStream(f); 827 ObjectInputStream ois = null; 828 try { 829 ois = new ObjectInputStream(fis); 830 CacheObjectInterface coi = null; 831 coi = (CacheObjectInterface) ois.readObject(); 832 if (coi != null) { 833 // TODO do all items need to be added to objectHash here? 834 // for most cacheObjects getLSID() returns null, which was 835 // giving an NPE here. 836 // ActorCacheObject seems to work because it implements 837 // readExternal which 838 // sets its lsid. 839 if (coi.getLSID() != null) { 840 objectHash.put(coi.getLSID().toString(), coi); 841 } 842 return coi; 843 } 844 } finally { 845 if(ois != null) { 846 ois.close(); 847 } 848 } 849 } catch (FileNotFoundException fnfe) { 850 log.warn(file + " could not be found:" + fnfe); 851 } catch (ClassNotFoundException cnfe) { 852 log.warn(file + " could not be instantiated:"); 853 log.warn(" No class definition found for " + cnfe.getMessage()); 854 } 855 return null; 856 } 857 858 /** 859 * @param type 860 * @return all cache objects that are of the specified type 861 * @throws CacheException 862 */ 863 public Iterator<CacheObjectInterface> getCacheObjectIterator(String type) 864 throws CacheException { 865 if (isDebugging) 866 log.debug("getCacheObjectIterator(" + type + ")"); 867 try { 868 Vector<CacheObjectInterface> items = new Vector<CacheObjectInterface>(); 869 String sql = "select name, lsid, file, type from " + CACHETABLENAME; 870 if (!type.trim().equals("")) { 871 sql += " WHERE type = '" + type + "'"; 872 } 873 if (isDebugging) 874 log.debug(sql); 875 ResultSet rs = _stmt.executeQuery(sql); 876 while (rs.next()) { 877 String name = rs.getString("name"); 878 String file = rs.getString("file"); 879 String lsidString = rs.getString("lsid"); 880 String coType = rs.getString("type"); 881 if (isDebugging) { 882 log.debug("name: " + name); 883 log.debug("file: " + file); 884 log.debug("lsidString: " + lsidString); 885 log.debug("type: " + coType); 886 } 887 888 CacheObjectInterface coi = (CacheObjectInterface) objectHash 889 .get(lsidString); 890 if (coi == null) { 891 deserializeCacheObject(file); 892 } 893 items.add(coi); 894 } 895 rs.close(); 896 return items.iterator(); 897 } catch (Exception e) { 898 e.printStackTrace(); 899 throw new CacheException( 900 "Error creating CacheObjectInterface iterator. " 901 + "Try removing the ~/.kepler directory?: " 902 + e.getMessage()); 903 } 904 } 905 906 /** 907 * return true of the given lsid has an associated object in the cache 908 * 909 * @param lsid 910 */ 911 public boolean isContained(KeplerLSID lsid) throws CacheException { 912 boolean foundIt = false; 913 try { 914 _getLsidForLsidPrepStmt.setString(1, lsid.toString()); 915 ResultSet rs = _getLsidForLsidPrepStmt.executeQuery(); 916 if (rs.next()) { 917 foundIt = true; 918 } 919 rs.close(); 920 } catch (Exception e) { 921 throw new CacheException("Error determining contents of cache: " 922 + e.getMessage()); 923 } 924 925 /* 926 if(foundIt && !_isObjectSerializedInFile(lsid)) { 927 System.out.println("WARNING: in cache database, but not on file system: " + lsid); 928 } 929 */ 930 931 return foundIt; 932 } 933 934 /** 935 * clear the cache of all contents 936 */ 937 public void clearCache() throws SQLException, CacheException { 938 // Remove the data files. 939 CacheUtil.cleanUpDir(new File(objectPath)); 940 // Clear our objectHash. 941 objectHash.clear(); 942 943 String sql = "delete from " + CACHETABLENAME; 944 _stmt.execute(sql); 945 _conn.commit(); 946 947 } 948 949 /** 950 * add a CacheListener to listen for cache events 951 */ 952 public void addCacheListener(CacheListener listener) { 953 listeners.add(listener); 954 } 955 956 /** 957 * notifies any listeners as to an action taking place. 958 */ 959 private void notifyListeners(CacheObjectInterface co, String op) { 960 CacheEvent ce = new CacheEvent(co); 961 962 if (op.equals("add")) { 963 for (int i = 0; i < listeners.size(); i++) { 964 CacheListener cl = (CacheListener) listeners.elementAt(i); 965 cl.objectAdded(ce); 966 } 967 co.objectAdded(); 968 } else if (op.equals("remove")) { 969 for (int i = 0; i < listeners.size(); i++) { 970 CacheListener cl = (CacheListener) listeners.elementAt(i); 971 cl.objectRemoved(ce); 972 } 973 co.objectRemoved(); 974 } else if (op.equals("purge")) { 975 for (int i = 0; i < listeners.size(); i++) { 976 CacheListener cl = (CacheListener) listeners.elementAt(i); 977 cl.objectPurged(ce); 978 } 979 co.objectPurged(); 980 } 981 } 982 983 private void serializeObjectInFile(CacheObjectInterface co, String filename) 984 throws CacheException { 985 File outputFile = new File(""); 986 try { 987 outputFile = new File(CacheManager.objectPath, filename); 988 if (isDebugging) 989 log.debug(outputFile.toString()); 990 FileOutputStream fos = new FileOutputStream(outputFile); 991 ObjectOutputStream oos = new ObjectOutputStream(fos); 992 oos.writeObject(co); // serialize the CacheObjectInterface itself 993 oos.flush(); 994 oos.close(); 995 } catch (IOException e) { 996 log.error("Serializing object to cache has failed: "+outputFile.toString()); 997 e.printStackTrace(); 998 throw new CacheException("Unable to serialize " + co.getName(), e); 999 } 1000 1001 } 1002 1003 /** Returns true if the serialized object for an LSID exists 1004 * in cache objects directory. 1005 */ 1006 private boolean _isObjectSerializedInFile(KeplerLSID lsid) { 1007 File file = new File(CacheManager.objectPath, lsid.toString()); 1008 return file.exists() && file.isFile(); 1009 } 1010 1011 /** Get the name of the cache database schema. */ 1012 public static String getDatabaseSchemaName() { 1013 // set the schema based on the the contents of the suite. 1014 try { 1015 // put "S" in front of the schema name: HSQL will not accept 1016 // schema names beginning with a digit. 1017 return "S" + ModuleTree.instance().getModuleConfigurationMD5(); 1018 } catch(Exception e) { 1019 MessageHandler.error("Error getting cache database schema name; using PUBLIC instead.", e); 1020 return "PUBLIC"; 1021 } 1022 } 1023}