src/org/objectweb/cjdbc/controller/cache/result/ResultCache.java

説明を見る。
00001 00025 package org.objectweb.cjdbc.controller.cache.result; 00026 00027 import java.util.ArrayList; 00028 import java.util.HashMap; 00029 import java.util.HashSet; 00030 import java.util.Iterator; 00031 00032 import org.objectweb.cjdbc.common.i18n.Translate; 00033 import org.objectweb.cjdbc.common.log.Trace; 00034 import org.objectweb.cjdbc.common.sql.AbstractWriteRequest; 00035 import org.objectweb.cjdbc.common.sql.CreateRequest; 00036 import org.objectweb.cjdbc.common.sql.ParsingGranularities; 00037 import org.objectweb.cjdbc.common.sql.RequestType; 00038 import org.objectweb.cjdbc.common.sql.SelectRequest; 00039 import org.objectweb.cjdbc.common.sql.UpdateRequest; 00040 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema; 00041 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags; 00042 import org.objectweb.cjdbc.controller.cache.CacheException; 00043 import org.objectweb.cjdbc.controller.cache.CacheStatistics; 00044 import org.objectweb.cjdbc.controller.cache.result.entries.CacheEntry; 00045 import org.objectweb.cjdbc.controller.cache.result.entries.ResultCacheEntry; 00046 import org.objectweb.cjdbc.controller.cache.result.entries.ResultCacheEntryNoCache; 00047 import org.objectweb.cjdbc.controller.cache.result.entries.ResultCacheEntryRelaxed; 00048 import org.objectweb.cjdbc.controller.cache.result.schema.CacheDatabaseSchema; 00049 import org.objectweb.cjdbc.controller.cache.result.schema.CacheDatabaseTable; 00050 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet; 00051 import org.objectweb.cjdbc.driver.Field; 00052 00077 public abstract class ResultCache extends AbstractResultCache 00078 { 00079 // 00080 // How the code is organized? 00081 // 00082 // 1. Member variables 00083 // 2. Constructor 00084 // 3. Cache management 00085 // 4. Transaction management 00086 // 5. Debug/Monitoring 00087 // 00088 00089 // Max number of cache entries 00090 private int maxEntries; 00092 private long pendingQueryTimeout = 0; 00093 // queries: SQL -> ResultCacheEntry 00094 private HashMap queries; 00095 // Pending SQL requests (String) 00096 private HashSet pendingQueries; 00097 // The rules to apply for this cache 00098 private HashSet cachingRules; 00099 private ResultCacheRule defaultRule; 00100 private ArrayList relaxedCache; 00101 00102 // LRU (head) of cache entries for replacement 00103 private CacheEntry lruHead; 00104 // LRU (tail) of cache entries for replacement 00105 private CacheEntry lruTail; 00106 00107 // Database schema 00108 protected CacheDatabaseSchema cdbs; 00109 00110 private CacheStatistics stats; 00111 00112 private long threadWakeUpTime; 00113 private RelaxedCacheThread relaxedThread; 00114 private static final boolean[] TRUE_TRUE = new boolean[]{true, true}; 00115 00116 /* 00117 * Constructor 00118 */ 00119 00126 public ResultCache(int maxEntries, int pendingTimeout) 00127 { 00128 this.maxEntries = maxEntries; 00129 this.pendingQueryTimeout = pendingTimeout; 00130 cdbs = null; 00131 stats = new CacheStatistics(); 00132 queries = new HashMap(1000, (float) 0.75); 00133 pendingQueries = new HashSet(); 00134 cachingRules = new HashSet(); 00135 relaxedCache = new ArrayList(); 00136 lruHead = null; 00137 lruTail = null; 00138 defaultRule = null; 00139 threadWakeUpTime = 0; 00140 relaxedThread = new RelaxedCacheThread(); 00141 relaxedThread.setPriority(9); 00142 relaxedThread.start(); 00143 } 00144 00151 public int getPendingQueryTimeout() 00152 { 00153 return (int) (pendingQueryTimeout / 1000); 00154 } 00155 00162 public void setPendingQueryTimeout(int pendingQueryTimeout) 00163 { 00164 this.pendingQueryTimeout = pendingQueryTimeout * 1000L; 00165 } 00166 00172 public HashMap getQueries() 00173 { 00174 return this.queries; 00175 } 00176 00183 public void setDatabaseSchema(DatabaseSchema dbs) 00184 { 00185 if (cdbs == null) 00186 { 00187 logger.info(Translate.get("resultcache.setting.database.schema")); 00188 cdbs = new CacheDatabaseSchema(dbs); 00189 } 00190 else 00191 { // Schema is updated, compute the diff ! 00192 CacheDatabaseSchema newSchema = new CacheDatabaseSchema(dbs); 00193 ArrayList tables = cdbs.getTables(); 00194 ArrayList newTables = newSchema.getTables(); 00195 if (newTables == null) 00196 { // New schema is empty (no backend is active anymore) 00197 logger.info(Translate.get("resultcache.flusing.whole.cache")); 00198 flushCache(); 00199 cdbs = null; 00200 return; 00201 } 00202 00203 // Remove extra-tables 00204 for (int i = 0; i < tables.size(); i++) 00205 { 00206 CacheDatabaseTable t = (CacheDatabaseTable) tables.get(i); 00207 if (!newSchema.hasTable(t.getName())) 00208 { 00209 t.invalidateAll(); 00210 cdbs.removeTable(t); 00211 if (logger.isInfoEnabled()) 00212 logger.info(Translate 00213 .get("resultcache.removing.table", t.getName())); 00214 } 00215 } 00216 00217 // Add missing tables 00218 int size = newTables.size(); 00219 for (int i = 0; i < size; i++) 00220 { 00221 CacheDatabaseTable t = (CacheDatabaseTable) newTables.get(i); 00222 if (!cdbs.hasTable(t.getName())) 00223 { 00224 cdbs.addTable(t); 00225 if (logger.isInfoEnabled()) 00226 logger.info(Translate.get("resultcache.adding.table", t.getName())); 00227 } 00228 } 00229 } 00230 } 00231 00238 public void mergeDatabaseSchema(DatabaseSchema dbs) 00239 { 00240 try 00241 { 00242 logger.info(Translate.get("resultcache.merging.new.database.schema")); 00243 cdbs.mergeSchema(new CacheDatabaseSchema(dbs)); 00244 } 00245 catch (Exception e) 00246 { 00247 logger.error(Translate.get("resultcache.error.while.merging", e)); 00248 } 00249 } 00250 00257 public void addCachingRule(ResultCacheRule rule) 00258 { 00259 cachingRules.add(rule); 00260 } 00261 00265 public ResultCacheRule getDefaultRule() 00266 { 00267 return defaultRule; 00268 } 00269 00273 public void setDefaultRule(ResultCacheRule defaultRule) 00274 { 00275 this.defaultRule = defaultRule; 00276 } 00277 00286 private CacheBehavior getCacheBehavior(SelectRequest request) 00287 { 00288 CacheBehavior behavior = null; 00289 for (Iterator iter = cachingRules.iterator(); iter.hasNext();) 00290 { 00291 behavior = ((ResultCacheRule) iter.next()).matches(request); 00292 if (behavior != null) 00293 { 00294 break; 00295 } 00296 } 00297 if (behavior == null) 00298 behavior = defaultRule.getCacheBehavior(); 00299 if (logger.isDebugEnabled()) 00300 logger.debug(Translate.get("resultcache.behavior.for.request", 00301 new String[]{request.getSQL(), behavior.getType()})); 00302 return behavior; 00303 } 00304 00305 /* 00306 * Cache Management 00307 */ 00308 00319 public boolean[] needInvalidate(ControllerResultSet result, 00320 UpdateRequest request) 00321 { 00322 HashMap updatedValues = request.getUpdatedValues(); 00323 boolean needInvalidate = false; 00324 boolean needToSendQuery = false; 00325 String value; 00326 String columnName; 00327 try 00328 { 00329 // If we don't have exactly one row, we don't handle the optimization 00330 if ((result == null) || (result.getData() == null) 00331 || (result.getData().size() != 1)) 00332 return TRUE_TRUE; 00333 } 00334 catch (Exception e) 00335 { 00336 return TRUE_TRUE; 00337 } 00338 Field[] fields = result.getFields(); 00339 ArrayList data = result.getData(); 00340 int size = fields.length; 00341 for (Iterator iter = updatedValues.keySet().iterator(); iter.hasNext();) 00342 { 00343 columnName = (String) iter.next(); 00344 value = (String) updatedValues.get(columnName); 00345 for (int i = 0; i < size; i++) 00346 { // Find the corresponding column in the ResultSet by comparing column 00347 // names 00348 00349 // We can have something like: 00350 // FIRSTNAME and ADDRESS.FIRSTNAME 00351 if (columnName.equals(fields[i].getFieldName())) 00352 { 00353 Object o = ((Object[]) data.get(0))[i]; 00354 if (!value.equals(o)) 00355 { 00356 // The value from the cache entry is different we need to update 00357 // the 00358 // cache and the database 00359 return TRUE_TRUE; 00360 } 00361 else 00362 break; 00363 } 00364 } 00365 // We don't need to invalidate the cache because the columns affected are 00366 // different but we need to send the query to the database. 00367 needToSendQuery = true; 00368 // We don't stop here though because other columns could be updated and 00369 // we 00370 // could need invalidation 00371 } 00372 return new boolean[]{needInvalidate, needToSendQuery}; 00373 } 00374 00383 public void addToCache(SelectRequest request, ControllerResultSet result) 00384 throws CacheException 00385 { 00386 String sqlQuery = request.getSQL(); 00387 00388 // Sanity checks 00389 if (request.getCacheAbility() == RequestType.UNCACHEABLE) 00390 throw new CacheException(Translate.get("resultcache.uncacheable.request", 00391 sqlQuery)); 00392 00393 if (result == null) 00394 throw new CacheException(Translate.get("resultcache.null.result", 00395 sqlQuery)); 00396 00397 synchronized (pendingQueries) 00398 { 00399 // Remove the pending query from the list and wake up 00400 // all waiting queries 00401 if (pendingQueries.remove(sqlQuery)) 00402 { 00403 if (logger.isDebugEnabled()) 00404 logger.debug(Translate.get("resultcache.removing.pending.query", 00405 sqlQuery)); 00406 pendingQueries.notifyAll(); 00407 } 00408 else 00409 logger.warn(Translate.get("resultcache.removing.pending.query.failed", 00410 sqlQuery)); 00411 00412 // Check against streamable ResultSets 00413 if (result.hasMoreData()) 00414 { 00415 logger.info(Translate.get("resultcache.streamed.resultset", request 00416 .getSQLShortForm(20))); 00417 return; 00418 } 00419 00420 if (logger.isDebugEnabled()) 00421 logger.debug(Translate.get("resultcache.adding.query", sqlQuery)); 00422 00423 CacheEntry ce; 00424 synchronized (queries) 00425 { 00426 // Check first that the query is not already in the cache 00427 ce = (ResultCacheEntry) queries.get(sqlQuery); 00428 if (ce == null) 00429 { 00430 // Not in cache, add this entry 00431 // check the rule 00432 CacheBehavior behavior = getCacheBehavior(request); 00433 ce = behavior.getCacheEntry(request, result, this); 00434 if (ce instanceof ResultCacheEntryNoCache) 00435 return; 00436 00437 // Test size of cache 00438 if (maxEntries > 0) 00439 { 00440 int size = queries.size(); 00441 if (size >= maxEntries) 00442 // LRU replacement policy: Remove the oldest cache entry 00443 removeOldest(); 00444 } 00445 // Add to the cache 00446 queries.put(sqlQuery, ce); 00447 if (ce instanceof ResultCacheEntryRelaxed) 00448 { 00449 ResultCacheEntryRelaxed qcer = (ResultCacheEntryRelaxed) ce; 00450 synchronized (relaxedThread) 00451 { 00452 relaxedCache.add(qcer); 00453 if (qcer.getDeadline() < threadWakeUpTime 00454 || threadWakeUpTime == 0) 00455 { 00456 relaxedThread.notify(); 00457 } 00458 } 00459 } 00460 } 00461 else 00462 { // Oh, oh, already in cache ... 00463 if (ce.isValid()) 00464 logger.warn(Translate.get( 00465 "resultcache.modifying.result.valid.entry", sqlQuery)); 00466 ce.setResult(result); 00467 } 00468 00469 // Update LRU 00470 if (lruHead != null) 00471 { 00472 lruHead.setPrev(ce); 00473 ce.setNext(lruHead); 00474 ce.setPrev(null); 00475 } 00476 if (lruTail == null) 00477 lruTail = ce; 00478 lruHead = ce; // This is also fine if LRUHead == null 00479 } 00480 processAddToCache(ce); 00481 } 00482 } 00483 00489 protected abstract void processAddToCache(CacheEntry qe); 00490 00506 public CacheEntry getFromCache(SelectRequest request, 00507 boolean addToPendingQueries) 00508 { 00509 stats.addSelect(); 00510 00511 if (request.getCacheAbility() == RequestType.UNCACHEABLE) 00512 { 00513 stats.addUncacheable(); 00514 return null; 00515 } 00516 00517 String sqlQuery = request.getSQL(); 00518 00519 // Check if we have the same query pending 00520 synchronized (pendingQueries) 00521 { 00522 if (addToPendingQueries) 00523 { 00524 long timeout = pendingQueryTimeout; 00525 // Yes, wait for the result 00526 // As we use a single lock for all pending queries, we use a 00527 // while to re-check that this wake-up was for us! 00528 while (pendingQueries.contains(sqlQuery)) 00529 { 00530 try 00531 { 00532 if (logger.isDebugEnabled()) 00533 logger.debug(Translate.get("resultcache.waiting.pending.query", 00534 sqlQuery)); 00535 00536 if (timeout > 0) 00537 { 00538 long start = System.currentTimeMillis(); 00539 pendingQueries.wait(pendingQueryTimeout); 00540 long end = System.currentTimeMillis(); 00541 timeout = timeout - (end - start); 00542 if (timeout <= 0) 00543 { 00544 logger.warn(Translate.get("resultcache.pending.query.timeout")); 00545 break; 00546 } 00547 } 00548 else 00549 pendingQueries.wait(); 00550 } 00551 catch (InterruptedException e) 00552 { 00553 logger.warn(Translate.get("resultcache.pending.query.timeout")); 00554 break; 00555 } 00556 } 00557 } 00558 00559 // Check the cache 00560 ResultCacheEntry ce; 00561 synchronized (queries) 00562 { 00563 ce = (ResultCacheEntry) queries.get(sqlQuery); 00564 if (ce == null) 00565 //if ((ce == null) || !ce.isValid()) 00566 { // Cache miss or dirty entry 00567 if (addToPendingQueries) 00568 { 00569 pendingQueries.add(sqlQuery); 00570 // Add this query to the pending queries 00571 if (logger.isDebugEnabled()) 00572 { 00573 logger.debug(Translate.get("resultcache.cache.miss")); 00574 logger.debug(Translate.get( 00575 "resultcache.adding.to.pending.queries", sqlQuery)); 00576 } 00577 } 00578 return null; 00579 } 00580 else 00581 { // Cache hit (must update LRU) 00582 // Move cache entry to head of LRU 00583 CacheEntry before = ce.getPrev(); 00584 if (before != null) 00585 { 00586 CacheEntry after = ce.getNext(); 00587 before.setNext(after); 00588 if (after != null) 00589 after.setPrev(before); 00590 else 00591 // We were the tail, update the tail 00592 lruTail = before; 00593 ce.setNext(lruHead); 00594 ce.setPrev(null); 00595 if (lruHead != ce) 00596 lruHead.setPrev(ce); 00597 lruHead = ce; 00598 } 00599 // else it was already the LRU head 00600 } 00601 } 00602 00603 if (ce.getResult() == null) 00604 { 00605 if (addToPendingQueries) 00606 { 00607 pendingQueries.add(sqlQuery); 00608 // Add this query to the pending queries 00609 if (logger.isDebugEnabled()) 00610 { 00611 logger.debug(Translate.get("resultcache.cache.miss")); 00612 logger.debug(Translate.get("resultcache.adding.to.pending.queries", 00613 sqlQuery)); 00614 } 00615 } 00616 if (ce.isValid() && logger.isInfoEnabled()) 00617 logger.info(Translate.get("resultcache.valid.entry.without.result", 00618 ce.getRequest().getSQL())); 00619 } 00620 else 00621 { 00622 if (logger.isDebugEnabled()) 00623 logger.debug(Translate.get("resultcache.cache.hit", sqlQuery)); 00624 stats.addHits(); 00625 } 00626 00627 return ce; 00628 } 00629 } 00630 00638 public void removeFromCache(SelectRequest request) 00639 { 00640 String sqlQuery = request.getSQL(); 00641 00642 if (logger.isDebugEnabled()) 00643 logger.debug("Removing from cache: " + sqlQuery); 00644 00645 synchronized (queries) 00646 { 00647 // Remove from the cache 00648 ResultCacheEntry ce = (ResultCacheEntry) queries.remove(sqlQuery); 00649 if (ce == null) 00650 return; // Was not in the cache! 00651 else 00652 { 00653 // Update result set 00654 ce.setResult(null); 00655 // Update LRU 00656 CacheEntry before = ce.getPrev(); 00657 CacheEntry after = ce.getNext(); 00658 if (before != null) 00659 { 00660 before.setNext(after); 00661 if (after != null) 00662 after.setPrev(before); 00663 else 00664 // We were the tail, update the tail 00665 lruTail = before; 00666 } 00667 else 00668 { // We are the LRUHead 00669 lruHead = ce.getNext(); 00670 if (after != null) 00671 after.setPrev(null); 00672 else 00673 // We were the tail, update the tail 00674 lruTail = before; 00675 } 00676 } 00677 } 00678 } 00679 00685 public void removeFromPendingQueries(SelectRequest request) 00686 { 00687 String sqlQuery = request.getSQL(); 00688 00689 synchronized (pendingQueries) 00690 { 00691 // Remove the pending query from the list and wake up 00692 // all waiting queries 00693 if (pendingQueries.remove(sqlQuery)) 00694 pendingQueries.notifyAll(); 00695 } 00696 } 00697 00701 public abstract boolean isUpdateNecessary(UpdateRequest request) 00702 throws CacheException; 00703 00712 public void writeNotify(AbstractWriteRequest request) throws CacheException 00713 { 00714 // Update the stats 00715 if (request.isInsert()) 00716 stats.addInsert(); 00717 else if (request.isUpdate()) 00718 stats.addUpdate(); 00719 else if (request.isDelete()) 00720 stats.addDelete(); 00721 else if (request.isCreate()) 00722 { 00723 stats.addCreate(); 00724 // Create: we only need to update the schema 00725 if (parsingGranularity != ParsingGranularities.NO_PARSING) 00726 cdbs.addTable(new CacheDatabaseTable(((CreateRequest) request) 00727 .getDatabaseTable())); 00728 return; 00729 } 00730 else if (request.isDrop()) 00731 { 00732 stats.addDrop(); 00733 // Drop: we need to update the schema 00734 if (parsingGranularity != ParsingGranularities.NO_PARSING) 00735 { 00736 // Invalidate the cache entries associated with this table 00737 CacheDatabaseTable cdt = cdbs.getTable(request.getTableName()); 00738 if (cdt != null) 00739 { 00740 cdt.invalidateAll(); 00741 cdbs.removeTable(cdt); 00742 return; 00743 } 00744 // else: the table was not previously cached 00745 // (no previous 'select' requests on the table). 00746 } 00747 } 00748 else 00749 { 00750 stats.addUnknown(); 00751 } 00752 if (logger.isDebugEnabled()) 00753 logger.debug("Notifying write " + request.getSQL()); 00754 00755 processWriteNotify(request); 00756 } 00757 00763 protected abstract void processWriteNotify(AbstractWriteRequest request); 00764 00768 public void flushCache() 00769 { 00770 synchronized (queries) 00771 { // Invalidate the whole cache until it is empty 00772 while (!queries.isEmpty()) 00773 { 00774 Iterator iter = queries.values().iterator(); 00775 ((ResultCacheEntry) iter.next()).invalidate(); 00776 } 00777 } 00778 00779 synchronized (pendingQueries) 00780 { // Clean pending queries to unblock everyone if some queries/backends 00781 // remained in an unstable state. 00782 pendingQueries.clear(); 00783 pendingQueries.notifyAll(); 00784 } 00785 00786 if (logger.isDebugEnabled()) 00787 logger.debug(Translate.get("resultcache.cache.flushed")); 00788 } 00789 00795 public long getCacheSize() 00796 { 00797 // No need to synchronize, the implementation returns an int 00798 return queries.size(); 00799 } 00800 00807 private void removeOldest() 00808 { 00809 if (lruTail == null) 00810 return; 00811 // Update the LRU 00812 ResultCacheEntry oldce = (ResultCacheEntry) lruTail; 00813 lruTail = lruTail.getPrev(); 00814 if (lruTail != null) 00815 lruTail.setNext(null); 00816 00817 if (logger.isDebugEnabled()) 00818 logger.debug(Translate.get("resultcache.removing.oldest.cache.entry", 00819 oldce.getRequest().getSQL())); 00820 00821 /* 00822 * We remove the query from the hashtable so that the garbage collector can 00823 * do its job. We need to remove the query from the queries HashTable first 00824 * in case we invalidate an eager cache entry that will call removeFromCache 00825 * (and will try to update the LRU is the entry is still in the queries 00826 * HashTable). So, to be compatible with all type of cache entries: 1. 00827 * queries.remove(ce) 2. ce.invalidate 00828 */ 00829 queries.remove(oldce.getRequest().getSQL()); 00830 00831 if (oldce.isValid()) 00832 { 00833 oldce.setResult(null); 00834 oldce.invalidate(); 00835 } 00836 00837 stats.addRemove(); 00838 } 00839 00845 public int getParsingGranularity() 00846 { 00847 return this.parsingGranularity; 00848 } 00849 00855 public abstract String getName(); 00856 00857 // 00858 // Transaction management 00859 // 00860 00867 public void commit(long transactionId) throws CacheException 00868 { 00869 // Ok, the transaction has commited, nothing to do 00870 } 00871 00878 public void rollback(long transactionId) throws CacheException 00879 { 00880 logger.info(Translate.get("resultcache.flushing.cache.cause.rollback", 00881 transactionId)); 00882 flushCache(); 00883 } 00884 00885 /* 00886 * Debug/Monitoring 00887 */ 00888 00892 public String[][] getCacheData() throws CacheException 00893 { 00894 try 00895 { 00896 synchronized (queries) 00897 { 00898 String[][] data = new String[queries.size()][]; 00899 int count = 0; 00900 for (Iterator iter = queries.values().iterator(); iter.hasNext(); count++) 00901 { 00902 ResultCacheEntry qe = (ResultCacheEntry) iter.next(); 00903 if (qe != null) 00904 { 00905 data[count] = qe.toStringTable(); 00906 } 00907 } 00908 return data; 00909 } 00910 } 00911 catch (Exception e) 00912 { 00913 logger.error(Translate.get("resultcache.error.retrieving.cache.data", e)); 00914 throw new CacheException(e.getMessage()); 00915 } 00916 } 00917 00921 public String[][] getCacheStatsData() throws CacheException 00922 { 00923 String[][] data = new String[1][]; 00924 String[] stat = stats.getCacheStatsData(); 00925 data[0] = new String[stat.length + 1]; 00926 for (int i = 0; i < stat.length; i++) 00927 data[0][i] = stat[i]; 00928 data[0][data[0].length - 1] = "" + queries.size(); 00929 return data; 00930 } 00931 00935 public CacheStatistics getCacheStatistics() 00936 { 00937 return stats; 00938 } 00939 00945 protected String getXmlImpl() 00946 { 00947 StringBuffer info = new StringBuffer(); 00948 info.append("<" + DatabasesXmlTags.ELT_ResultCache + " " 00949 + DatabasesXmlTags.ATT_pendingTimeout + "=\"" + pendingQueryTimeout 00950 + "\" " + DatabasesXmlTags.ATT_maxNbOfEntries + "=\"" + maxEntries 00951 + "\" " + DatabasesXmlTags.ATT_granularity + "=\"" + getName() + "\">"); 00952 info.append("<" + DatabasesXmlTags.ELT_DefaultResultCacheRule + " " 00953 + DatabasesXmlTags.ATT_timestampResolution + "=\"" 00954 + defaultRule.getTimestampResolution() / 1000 + "\">"); 00955 info.append(defaultRule.getCacheBehavior().getXml()); 00956 info.append("</" + DatabasesXmlTags.ELT_DefaultResultCacheRule + ">"); 00957 for (Iterator iter = cachingRules.iterator(); iter.hasNext();) 00958 info.append(((ResultCacheRule) iter.next()).getXml()); 00959 info.append("</" + DatabasesXmlTags.ELT_ResultCache + ">"); 00960 return info.toString(); 00961 } 00962 00966 private final class RelaxedCacheThread extends Thread 00967 { 00968 int refreshCacheRate = 60; 00969 int refreshCacheTime = 60 / refreshCacheRate; 00970 private Trace logger = Trace 00971 .getLogger("org.objectweb.cjdbc.controller.cache.cache.RelaxedThread"); 00972 00973 private boolean isKilled = false; 00974 00978 public RelaxedCacheThread() 00979 { 00980 super("RelaxedCacheThread"); 00981 } 00982 00988 public RelaxedCacheThread(int refreshCacheRate) 00989 { 00990 this(); 00991 this.refreshCacheRate = refreshCacheRate; 00992 } 00993 00997 public void run() 00998 { 00999 ResultCacheEntryRelaxed entry; 01000 long now; 01001 long sleep; 01002 // Keep trace of relaxed cache entries to delete 01003 ArrayList toRemoveFromRelaxedCache = new ArrayList(); 01004 while (!isKilled) 01005 { 01006 synchronized (this) 01007 { 01008 try 01009 { 01010 threadWakeUpTime = 0; 01011 if (relaxedCache.isEmpty()) 01012 { // Nothing in the cache, just sleep! 01013 if (logger.isDebugEnabled()) 01014 logger.debug(Translate 01015 .get("relaxedcachethread.cache.empty.sleeping")); 01016 wait(); 01017 } 01018 else 01019 { // Look for first deadline 01020 now = System.currentTimeMillis(); 01021 clean : for (Iterator iter = relaxedCache.iterator(); iter 01022 .hasNext();) 01023 { 01024 entry = (ResultCacheEntryRelaxed) iter.next(); 01025 if (entry.getDeadline() < now) 01026 { 01027 if (entry.isDirty() || !entry.getKeepIfNotDirty()) 01028 { 01029 { 01030 toRemoveFromRelaxedCache.add(entry); 01031 } 01032 continue clean; 01033 } 01034 else 01035 entry.setDeadline(now + entry.getTimeout()); 01036 } 01037 if (threadWakeUpTime == 0 01038 || (entry.getDeadline() < threadWakeUpTime)) 01039 threadWakeUpTime = entry.getDeadline(); 01040 } 01041 01042 // Clean up all the entries from the relaxed cache 01043 for (int i = 0; i < toRemoveFromRelaxedCache.size(); i++) 01044 { 01045 entry = (ResultCacheEntryRelaxed) toRemoveFromRelaxedCache 01046 .get(i); 01047 if (logger.isDebugEnabled()) 01048 logger.debug(Translate.get( 01049 "relaxedcachethread.remove.entry.from.cache", entry 01050 .getRequest().getSQL())); 01051 removeFromCache(entry.getRequest()); 01052 relaxedCache.remove(toRemoveFromRelaxedCache.get(i)); 01053 toRemoveFromRelaxedCache.remove(i); 01054 } 01055 if (threadWakeUpTime == 0) 01056 { // All entries were dirty and not kept in the cache, therefore 01057 // there is no next deadline. (and no cache entry to wait for) 01058 continue; 01059 } 01060 else 01061 { // Sleep until the next deadline 01062 sleep = (threadWakeUpTime - now) / 1000 + refreshCacheTime; 01063 if (logger.isDebugEnabled()) 01064 { 01065 logger.debug(Translate.get("relaxedcachethread.sleeping", 01066 sleep)); 01067 } 01068 sleep = (sleep) * 1000; 01069 wait(sleep); 01070 } 01071 } 01072 } 01073 catch (Exception e) 01074 { 01075 logger.warn(e.getMessage(), e); 01076 } 01077 } 01078 } 01079 } 01080 } 01081 01082 }

CJDBCversion1.0.4に対してTue Oct 12 15:16:00 2004に生成されました。 doxygen 1.3.8