001/** 002 * '$RCSfile$' 003 * '$Author: crawl $' 004 * '$Date: 2018-01-15 20:27:04 +0000 (Mon, 15 Jan 2018) $' 005 * '$Revision: 34649 $' 006 * 007 * For Details: 008 * http://www.kepler-project.org 009 * 010 * Copyright (c) 2010 The Regents of the 011 * University of California. All rights reserved. Permission is hereby granted, 012 * without written agreement and without license or royalty fees, to use, copy, 013 * modify, and distribute this software and its documentation for any purpose, 014 * provided that the above copyright notice and the following two paragraphs 015 * appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF 016 * CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, 017 * OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 018 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE 019 * POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY 020 * DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 022 * SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 023 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 024 * ENHANCEMENTS, OR MODIFICATIONS. 025 */ 026 027package org.kepler.kar; 028 029import java.io.File; 030import java.sql.Connection; 031import java.sql.Date; 032import java.sql.PreparedStatement; 033import java.sql.ResultSet; 034import java.sql.SQLException; 035import java.sql.Statement; 036import java.util.Vector; 037 038import org.apache.commons.logging.Log; 039import org.apache.commons.logging.LogFactory; 040import org.kepler.objectmanager.cache.CacheContent; 041import org.kepler.objectmanager.cache.CacheManager; 042import org.kepler.objectmanager.cache.LocalRepositoryManager; 043import org.kepler.objectmanager.lsid.KeplerLSID; 044import org.kepler.util.sql.DatabaseFactory; 045 046import ptolemy.util.MessageHandler; 047 048/** 049 * This is a singleton class to help manage KARs as they are stored in the 050 * cache. 051 * 052 * @author Aaron Schultz 053 * 054 */ 055public class KARCacheManager { 056 private static final Log log = LogFactory.getLog(KARCacheManager.class 057 .getName()); 058 private static final boolean isDebugging = log.isDebugEnabled(); 059 060 /** Table Names **/ 061 public static final String KARS_CACHED_TABLE_NAME = "KARS_CACHED"; 062 public static final String KAR_ERRORS_TABLE_NAME = "KAR_ERRORS"; 063 public static final String KAR_CONTENTS_TABLE_NAME = "KAR_CONTENTS"; 064 065 private Connection _conn; 066 private Statement _stmt; 067 private PreparedStatement _insertPrepStmt; 068 private PreparedStatement _insContentsPrepStmt; 069 private PreparedStatement _insErrorsPrepStmt; 070 private PreparedStatement _karsLastModifiedPrepStmt; 071 private PreparedStatement _fileForFilePrepStmt; 072 private PreparedStatement _allKarsInCache; 073 074 public KARCacheManager() { 075 076 try { 077 _conn = DatabaseFactory.getDBConnection(); 078 if (isDebugging) { 079 log.debug(_conn.toString()); 080 } 081 } catch (Exception e) { 082 e.printStackTrace(); 083 } 084 085 try { 086 // By creating the statement and keeping it around 087 // make sure to close your resultsets to save memory 088 _stmt = _conn.createStatement(); 089 _insertPrepStmt = _conn.prepareStatement("insert into " 090 + KARS_CACHED_TABLE_NAME 091 + " (file, lsid, version, reponame, lastmodified)" 092 + " values ( ?, ?, ?, ?, ? )"); 093 _insContentsPrepStmt = _conn.prepareStatement("insert into " 094 + KAR_CONTENTS_TABLE_NAME + " (file, lsid, name, type)" 095 + " values ( ?, ?, ?, ? ) "); 096 _insErrorsPrepStmt = _conn.prepareStatement("insert into " 097 + KAR_ERRORS_TABLE_NAME + " (file, lsid, version, reponame, dependencies, lastmodified)" 098 + " values ( ?, ?, ?, ?, ?, ? ) "); 099 _karsLastModifiedPrepStmt = _conn.prepareStatement("SELECT LASTMODIFIED FROM " 100 + KARS_CACHED_TABLE_NAME + " WHERE FILE = ? " 101 + "UNION SELECT LASTMODIFIED FROM "+ KAR_ERRORS_TABLE_NAME + " WHERE FILE = ?"); 102 _fileForFilePrepStmt = _conn.prepareStatement("SELECT FILE FROM " 103 + KARS_CACHED_TABLE_NAME + " WHERE FILE = ? " 104 + " UNION SELECT FILE FROM " + KAR_ERRORS_TABLE_NAME + " WHERE FILE = ?"); 105 _allKarsInCache = _conn.prepareStatement("SELECT FILE FROM " + KARS_CACHED_TABLE_NAME 106 + " UNION SELECT FILE FROM " + KAR_ERRORS_TABLE_NAME); 107 108 109 110 } catch (SQLException e) { 111 e.printStackTrace(); 112 } 113 } 114 115 public Vector<KARCacheError> getKARCacheErrors() { 116 117 Vector<KARCacheError> errors = new Vector<KARCacheError>(); 118 119 String query = "SELECT " 120 + "FILE,LSID,VERSION,REPONAME,DEPENDENCIES " + " FROM " 121 + KAR_ERRORS_TABLE_NAME; 122 ResultSet rs; 123 try { 124 if (isDebugging) 125 log.debug(query); 126 rs = _stmt.executeQuery(query); 127 if (rs == null) 128 throw new SQLException("Query Failed: " + query); 129 while (rs.next()) { 130 KARCacheError kce = new KARCacheError(); 131 try { 132 String fileStr = rs.getString(1); 133 File file = new File(fileStr); 134 String lsidStr = rs.getString(2); 135 KeplerLSID lsid = new KeplerLSID(lsidStr); 136 String version = rs.getString(3); 137 String reponame = rs.getString(4); 138 String depStr = rs.getString(5); 139 Vector<String> deps = ModuleDependencyUtil 140 .parseDependencyString(depStr); 141 142 kce.setFile(file); 143 kce.setLsid(lsid); 144 kce.setVersion(version); 145 kce.setRepoName(reponame); 146 kce.setDependencies(deps); 147 148 errors.add(kce); 149 150 } catch (Exception e) { 151 e.printStackTrace(); 152 } 153 } 154 rs.close(); 155 } catch (SQLException e1) { 156 e1.printStackTrace(); 157 errors = new Vector<KARCacheError>(); 158 } 159 160 return errors; 161 162 } 163 164 /** 165 * Return all of the KARCacheContents. 166 * 167 * @return 168 */ 169 public Vector<KARCacheContent> getKARCacheContents() { 170 return getKarCacheContents(""); 171 } 172 173 /** 174 * Return all of the KARCacheContents matching the specified type. 175 * 176 * @param type 177 * @return 178 */ 179 public Vector<KARCacheContent> getKARCacheContents(String type) { 180 String whereClause = " WHERE " + KAR_CONTENTS_TABLE_NAME + ".TYPE = '" 181 + type + "'"; 182 return getKarCacheContents(whereClause); 183 } 184 185 /** 186 * Return all of the KARCacheContents matching the specified lsid. 187 * 188 * @param type 189 * @return 190 */ 191 public Vector<KARCacheContent> getKARCacheContents(KeplerLSID lsid) { 192 String whereClause = " WHERE " + KAR_CONTENTS_TABLE_NAME + ".LSID = '" 193 + lsid.toString() + "'"; 194 return getKarCacheContents(whereClause); 195 } 196 197 /** 198 * Return all of the KARCacheContents in the specified KAR file. 199 * 200 * @param type 201 * @return 202 */ 203 public Vector<KARCacheContent> getKARCacheContents(File karFile) { 204 String whereClause = " WHERE " + KAR_CONTENTS_TABLE_NAME + ".FILE = '" 205 + karFile.toString() + "'"; 206 return getKarCacheContents(whereClause); 207 } 208 209 /** 210 * Return the contents of the KAR_CONTENTS table that match the given type 211 * or every row if the type given is the empty string. 212 * 213 * @param type 214 * @return 215 */ 216 private Vector<KARCacheContent> getKarCacheContents(String whereClause) { 217 218 Vector<KARCacheContent> contents = new Vector<KARCacheContent>(); 219 220 String query = "SELECT "; 221 query += KAR_CONTENTS_TABLE_NAME + ".NAME, "; 222 query += KAR_CONTENTS_TABLE_NAME + ".TYPE, "; 223 query += KARS_CACHED_TABLE_NAME + ".FILE, "; 224 query += KARS_CACHED_TABLE_NAME + ".LSID, "; 225 query += KARS_CACHED_TABLE_NAME + ".VERSION, "; 226 query += KARS_CACHED_TABLE_NAME + ".REPONAME, "; 227 query += CacheManager.CACHETABLENAME + ".NAME, "; 228 query += CacheManager.CACHETABLENAME + ".LSID, "; 229 query += CacheManager.CACHETABLENAME + ".DATE, "; 230 query += CacheManager.CACHETABLENAME + ".FILE, "; 231 query += CacheManager.CACHETABLENAME + ".TYPE, "; 232 query += CacheManager.CACHETABLENAME + ".CLASSNAME"; 233 query += " FROM " + KAR_CONTENTS_TABLE_NAME; 234 query += " INNER JOIN " + KARS_CACHED_TABLE_NAME; 235 query += " ON " + KAR_CONTENTS_TABLE_NAME + ".FILE = " 236 + KARS_CACHED_TABLE_NAME + ".FILE"; 237 query += " INNER JOIN " + CacheManager.CACHETABLENAME; 238 query += " ON " + KAR_CONTENTS_TABLE_NAME + ".LSID = " 239 + CacheManager.CACHETABLENAME + ".LSID"; 240 241 if (!whereClause.trim().equals("")) { 242 query += " " + whereClause; 243 } 244 ResultSet rs; 245 try { 246 if (isDebugging) 247 log.debug(query); 248 rs = _stmt.executeQuery(query); 249 if (rs == null) 250 throw new SQLException("Query Failed: " + query); 251 while (rs.next()) { 252 253 // Instantiate the KARCacheContent object that we'll return 254 KARCacheContent kcc = new KARCacheContent(); 255 try { 256 kcc.setName(rs.getString(1)); 257 kcc.setType(rs.getString(2)); 258 259 // Populate the Foreign Key data from the KARS_CACHED table 260 KARCached kc = new KARCached(); 261 File karFile = new File(rs.getString(3)); 262 kc.setFile(karFile); 263 KeplerLSID karLsid = new KeplerLSID(rs.getString(4)); 264 kc.setLsid(karLsid); 265 kc.setVersion(rs.getString(5)); 266 kc.setRepoName(rs.getString(6)); 267 kcc.setKarCached(kc); 268 269 // Populate the Foreign Key data from the CacheContent table 270 CacheContent cc = new CacheContent(); 271 cc.setName(rs.getString(7)); 272 KeplerLSID ccLsid = new KeplerLSID(rs.getString(8)); 273 cc.setLsid(ccLsid); 274 Date changed = new Date(rs.getLong(9)); 275 cc.setDateChanged(changed); 276 File file = new File(rs.getString(10)); 277 cc.setFile(file); 278 cc.setType(rs.getString(11)); 279 cc.setClassName(rs.getString(12)); 280 kcc.setCacheContent(cc); 281 282 // Populate the Semantic types 283 kcc.populateSemanticTypes(_stmt); 284 285 contents.add(kcc); 286 } catch (Exception e) { 287 e.printStackTrace(); 288 } 289 } 290 rs.close(); 291 } catch (SQLException e1) { 292 e1.printStackTrace(); 293 contents = new Vector<KARCacheContent>(); 294 } 295 296 return contents; 297 } 298 299 public void clearKARCache() { 300 301 try { 302 String deleteAll = "delete from " + KARS_CACHED_TABLE_NAME; 303 _stmt.executeUpdate(deleteAll); 304 deleteAll = "delete from " + KAR_ERRORS_TABLE_NAME; 305 _stmt.executeUpdate(deleteAll); 306 } catch (SQLException sqle) { 307 log.error(sqle.getMessage()); 308 sqle.printStackTrace(); 309 } 310 311 } 312 313 /** 314 * Update the KAR cache tables to reflect the kar files on disk. 315 * 316 * @return boolean true if the cache was changed because of the 317 * synchronization. 318 */ 319 public boolean synchronizeKARCacheWithLocalRepositories() { 320 boolean contentsOnDiskHaveChanged = false; 321 322 LocalRepositoryManager lrm = LocalRepositoryManager.getInstance(); 323 lrm.scanReposForKarFiles(); 324 Vector<File> karFiles = lrm.getKarFiles(); 325 326 if (isDebugging) 327 log 328 .debug("loop through all the kar files and make sure they are in the cache"); 329 for (int k = 0; k < karFiles.size(); k++) { 330 331 try { 332 File f = karFiles.elementAt(k); 333 334 final long time = getLastModifiedTimeOfFileInCache(f.toString()); 335 336 // see if the last modified time is different than the time 337 // when added to the cache 338 if(time != f.lastModified()) { 339 340 // they are different, so update the cache. 341 342 // see if the file was previously cached 343 if(time > 0) { 344 log.debug(f + " has a different modified time than in cache; will update cache."); 345 // remove previous version 346 removeKARFromCache(f); 347 } else { 348 log.debug(f + " is not in cache."); 349 } 350 351 KARFile kFile = new KARFile(f); 352 // This KAR is not cached, go ahead and cache it 353 boolean allDependenciesSatisfied = kFile.areAllModuleDependenciesSatisfied(); 354 if (allDependenciesSatisfied) { 355 kFile.cacheKARContents(); 356 } else { 357 insertKARError(kFile); 358 } 359 contentsOnDiskHaveChanged = true; 360 } 361 } catch (Exception e) { 362 log.warn("Unable to process kar file \"" 363 + karFiles.elementAt(k).toString() + "\".", e); 364 } 365 } 366 367 if (isDebugging) 368 log 369 .debug("loop through the cache and make sure there are matching KAR files"); 370 try { 371 372 ResultSet rs = _allKarsInCache.executeQuery(); 373 if (rs != null) { 374 while (rs.next()) { 375 String cachedFileStr = rs.getString(1); 376 File cachedKar = new File(cachedFileStr); 377 if (!karFiles.contains(cachedKar)) { 378 removeKARFromCache(cachedKar); 379 contentsOnDiskHaveChanged = true; 380 } 381 } 382 rs.close(); 383 } 384 } catch (Exception sqle) { 385 sqle.printStackTrace(); 386 } 387 388 // free up the kar files 389 karFiles = null; 390 391 if (contentsOnDiskHaveChanged) { 392 log 393 .info("The Cache was out of sync with KAR files in Local Repositories."); 394 log 395 .info("The Cache has been synchronized with KAR files in Local Repositories."); 396 } else { 397 log 398 .info("The Cache is in sync with KAR files in Local Repositories."); 399 } 400 return contentsOnDiskHaveChanged; 401 } 402 403 /** 404 * Remove a KAR file from the cache. The contents of the KAR are also 405 * removed only if the objects don't exist in another KAR. 406 * 407 * @param karLSID 408 * the LSID of the kar to remove 409 * @return boolean true if and only if the kar file was removed 410 */ 411 public boolean removeKARFromCache(File karFile) { 412 boolean success = false; 413 try { 414 415 // Store all of the LSIDs that this KAR contained 416 Vector<KARCacheContent> karContents = getKARCacheContents(karFile); 417 418 // Remove the KAR, which will cascade removal of the contents 419 String delQuery = "DELETE FROM " + KARS_CACHED_TABLE_NAME 420 + " WHERE file = '" + karFile.toString() + "'"; 421 if (isDebugging) 422 log.debug(delQuery); 423 int rowsDel = _stmt.executeUpdate(delQuery); 424 if (rowsDel >= 1) { 425 success = true; 426 } 427 428 // Remove the KAR, which will cascade removal of the contents 429 String delErrQuery = "DELETE FROM " + KAR_ERRORS_TABLE_NAME 430 + " WHERE file = '" + karFile.toString() + "'"; 431 if (isDebugging) 432 log.debug(delErrQuery); 433 int rowsErrDel = _stmt.executeUpdate(delErrQuery); 434 if (rowsErrDel >= 1) { 435 success = true; 436 } 437 438 if (success) { 439 // Check to see if the LSIDs are in any other KARS 440 // If they aren't then remove them from the Cache completely 441 for (KARCacheContent entry : karContents) { 442 Vector<KARCacheContent> otherEntries = getKARCacheContents(entry 443 .getLsid()); 444 if (otherEntries.size() == 0) { 445 CacheManager.getInstance() 446 .removeObject(entry.getLsid()); 447 } 448 } 449 } 450 } catch (Exception sqle) { 451 sqle.printStackTrace(); 452 } 453 return success; 454 } 455 456 /** 457 * 458 * @param karFile 459 * @return boolean 460 */ 461 public boolean insertKARError(KARFile karFile) { 462 if (isDebugging) 463 log.debug("insertKARError(" + karFile.toString() + ")"); 464 465 File karFileLocation = karFile.getFileLocation(); 466 KeplerLSID lsid = karFile.getLSID(); 467 String version = karFile.getKARVersion(); 468 String reponame = karFile.getLocalRepoName(); 469 Vector<String> modDeps = karFile.getModuleDependencies(); 470 String depStrs = ModuleDependencyUtil.generateDependencyString(modDeps); 471 472 try { 473 _insErrorsPrepStmt.setString(1, karFileLocation.toString()); 474 _insErrorsPrepStmt.setString(2, lsid.toString()); 475 _insErrorsPrepStmt.setString(3, version); 476 if (reponame == null) { 477 _insErrorsPrepStmt.setNull(4, java.sql.Types.VARCHAR); 478 } else { 479 _insErrorsPrepStmt.setString(4, reponame); 480 } 481 _insErrorsPrepStmt.setString(5, depStrs); 482 _insErrorsPrepStmt.setLong(6, karFileLocation.lastModified()); 483 int rows = _insErrorsPrepStmt.executeUpdate(); 484 if (isDebugging) 485 log.debug(rows + " rows affected on insert"); 486 487 _conn.commit(); 488 _insErrorsPrepStmt.clearParameters(); 489 if (isDebugging) 490 log.debug("insert succeeded"); 491 return true; 492 } catch (Exception sqle) { 493 try { 494 _conn.rollback(); 495 } catch (Exception e) { 496 e.printStackTrace(); 497 } 498 if (isDebugging) 499 log.debug(sqle.getMessage()); 500 // sqle.printStackTrace(); 501 } 502 503 return false; 504 } 505 506 /** 507 * Insert a KARFile into the KARS_CACHED table. Called from KARFile class. 508 * This method DOES NOT insert any of the entries into the cache. Use 509 * insertEntryIntoCache and CacheManager.insertObject 510 * 511 * @param karFile 512 * @return true if and only if the kar was inserted false if already exists 513 * or error 514 */ 515 public boolean insertIntoCache(KARFile karFile) { 516 if (isDebugging) 517 log.debug("insertIntoCache(" + karFile.toString() + ")"); 518 519 File karFileLocation = karFile.getFileLocation(); 520 KeplerLSID lsid = karFile.getLSID(); 521 String version = karFile.getKARVersion(); 522 String reponame = karFile.getLocalRepoName(); 523 524 try { 525 _insertPrepStmt.setString(1, karFileLocation.toString()); 526 _insertPrepStmt.setString(2, lsid.toString()); 527 _insertPrepStmt.setString(3, version); 528 if (reponame == null) { 529 _insertPrepStmt.setNull(4, java.sql.Types.VARCHAR); 530 } else { 531 _insertPrepStmt.setString(4, reponame); 532 } 533 _insertPrepStmt.setLong(5, karFile.getFileLocation().lastModified()); 534 int rows = _insertPrepStmt.executeUpdate(); 535 if (isDebugging) 536 log.debug(rows + " rows affected on insert"); 537 538 _conn.commit(); 539 _insertPrepStmt.clearParameters(); 540 if (isDebugging) 541 log.debug("insert succeeded"); 542 return true; 543 } catch (Exception sqle) { 544 log.error("Failed to insert KAR " + karFile.getPath() + " into database: " + sqle.getMessage()); 545 try { 546 _conn.rollback(); 547 } catch (Exception e) { 548 e.printStackTrace(); 549 } 550 if (isDebugging) 551 log.debug(sqle.getMessage()); 552 // sqle.printStackTrace(); 553 } 554 555 return false; 556 } 557 558 public void insertEntryIntoCache(File karFile, KeplerLSID entryLSID, 559 String entryName, String entryType) { 560 if (isDebugging) 561 log.debug("insertEntryIntoCache(" + karFile.toString() + "," 562 + entryLSID + "," + entryName + "," + entryType + ")"); 563 564 // we log the fact that this lsid is found in this KAR 565 try { 566 _insContentsPrepStmt.setString(1, karFile.toString()); 567 _insContentsPrepStmt.setString(2, entryLSID.toString()); 568 _insContentsPrepStmt.setString(3, entryName); 569 _insContentsPrepStmt.setString(4, entryType); 570 _insContentsPrepStmt.executeUpdate(); 571 _conn.commit(); 572 _insContentsPrepStmt.clearParameters(); 573 } catch (SQLException sqle) { 574 // if this entry is not found in the CACHECONTENTTABLE 575 // then we can not add it to the KAR_CONTENTS table 576 // because it violates the foreign key. So just ignore it 577 if (isDebugging) { 578 log.debug(sqle.getMessage()); 579 sqle.printStackTrace(); 580 } 581 } 582 } 583 584 /** 585 * Determine if the KAR represented by the supplied LSID has already been 586 * cached. 587 * 588 * @param karLSID 589 * @deprecated Cached Kars now use the File as the primary key Use 590 * isCached(File) instead 591 * */ 592 public boolean isCached(KeplerLSID karLSID) { 593 if (isDebugging) 594 log.debug("isCached(" + karLSID.toString() + ")"); 595 if (karLSID == null) 596 return false; 597 try { 598 ResultSet rs = _stmt.executeQuery("SELECT count(lsid) FROM " 599 + KARS_CACHED_TABLE_NAME + " where lsid = '" 600 + karLSID.toString() + "'"); 601 if (rs == null) { 602 return false; 603 } 604 if (rs.next()) { 605 Integer cnt = rs.getInt(1); 606 if (isDebugging) 607 log.debug(cnt); 608 if (cnt >= 1) { 609 rs.close(); 610 return true; 611 } 612 } 613 rs.close(); 614 } catch (Exception sqle) { 615 sqle.printStackTrace(); 616 } 617 return false; 618 } 619 620 /** 621 * Determine if the KAR represented by the supplied File has already been 622 * cached. NOTE: this method does not check if the File's contents have 623 * changed since cached. 624 * 625 * @param File karFile 626 * 627 */ 628 public boolean isCached(File karFile) { 629 if (karFile != null){ 630 if (isDebugging) 631 log.debug("isCached(" + karFile.toString() + ")"); 632 } 633 if (karFile == null){ 634 return false; 635 } 636 try { 637 String name = karFile.toString(); 638 _fileForFilePrepStmt.setString(1, name); 639 _fileForFilePrepStmt.setString(2, name); 640 ResultSet rs = _fileForFilePrepStmt.executeQuery(); 641 if (rs == null) { 642 return false; 643 } 644 if (rs.next()) { 645 rs.close(); 646 return true; 647 } 648 rs.close(); 649 } catch (Exception sqle) { 650 sqle.printStackTrace(); 651 } 652 return false; 653 } 654 655 /** Get the last modified time of a file in the cache. Returns 656 * < 0 if the file is not in the cache. 657 */ 658 public long getLastModifiedTimeOfFileInCache(String filename) { 659 660 try { 661 ResultSet result = null; 662 try { 663 _karsLastModifiedPrepStmt.setString(1, filename); 664 _karsLastModifiedPrepStmt.setString(2, filename); 665 result = _karsLastModifiedPrepStmt.executeQuery(); 666 if(result.next()) { 667 return result.getLong(1); 668 } 669 670 } finally { 671 if(result != null) { 672 result.close(); 673 } 674 } 675 } catch (Exception e) { 676 MessageHandler.error("Error checking if " + filename + " is in cache.", e); 677 } 678 679 return -1; 680 } 681 682 /** 683 * Method for getting an instance of this singleton class. 684 */ 685 public static KARCacheManager getInstance() { 686 return KARCacheManagerHolder.INSTANCE; 687 } 688 689 private static class KARCacheManagerHolder { 690 private static final KARCacheManager INSTANCE = new KARCacheManager(); 691 } 692 693}