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}