00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
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.sql.AbstractWriteRequest;
00034 import org.objectweb.cjdbc.common.sql.CreateRequest;
00035 import org.objectweb.cjdbc.common.sql.ParsingGranularities;
00036 import org.objectweb.cjdbc.common.sql.RequestType;
00037 import org.objectweb.cjdbc.common.sql.SelectRequest;
00038 import org.objectweb.cjdbc.common.sql.UpdateRequest;
00039 import org.objectweb.cjdbc.common.sql.schema.DatabaseSchema;
00040 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00041 import org.objectweb.cjdbc.controller.cache.CacheException;
00042 import org.objectweb.cjdbc.controller.cache.CacheStatistics;
00043 import org.objectweb.cjdbc.controller.cache.result.entries.CacheEntry;
00044 import org.objectweb.cjdbc.controller.cache.result.entries.ResultCacheEntry;
00045 import org.objectweb.cjdbc.controller.cache.result.entries.ResultCacheEntryEager;
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.cache.result.threads.EagerCacheThread;
00051 import org.objectweb.cjdbc.controller.cache.result.threads.RelaxedCacheThread;
00052 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
00053 import org.objectweb.cjdbc.driver.Field;
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079 public abstract class ResultCache extends AbstractResultCache
00080 {
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092 private int maxEntries;
00093
00094 private long pendingQueryTimeout = 0;
00095
00096 private HashMap queries;
00097
00098 private HashSet pendingQueries;
00099
00100 private HashSet cachingRules;
00101 private ResultCacheRule defaultRule;
00102 private ArrayList relaxedCache;
00103
00104
00105 private CacheEntry lruHead;
00106
00107 private CacheEntry lruTail;
00108
00109
00110 protected CacheDatabaseSchema cdbs;
00111
00112 private CacheStatistics stats;
00113
00114 private RelaxedCacheThread relaxedThread;
00115 private static final boolean[] TRUE_TRUE = new boolean[]{true, true};
00116 private boolean flushingCache;
00117 private EagerCacheThread eagerThread;
00118 private ArrayList eagerCache;
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130 public ResultCache(int maxEntries, int pendingTimeout)
00131 {
00132 this.maxEntries = maxEntries;
00133 this.pendingQueryTimeout = pendingTimeout;
00134 cdbs = null;
00135 stats = new CacheStatistics();
00136 queries = new HashMap(1000, (float) 0.75);
00137 pendingQueries = new HashSet();
00138 cachingRules = new HashSet();
00139 relaxedCache = new ArrayList();
00140 eagerCache = new ArrayList();
00141 lruHead = null;
00142 lruTail = null;
00143 defaultRule = null;
00144 relaxedThread = new RelaxedCacheThread(this);
00145 relaxedThread.setPriority(9);
00146 relaxedThread.start();
00147 eagerThread = new EagerCacheThread(this);
00148 eagerThread.setPriority(9);
00149 eagerThread.start();
00150 }
00151
00152
00153
00154
00155
00156
00157
00158 public int getPendingQueryTimeout()
00159 {
00160 return (int) (pendingQueryTimeout / 1000);
00161 }
00162
00163
00164
00165
00166
00167
00168
00169 public void setPendingQueryTimeout(int pendingQueryTimeout)
00170 {
00171 this.pendingQueryTimeout = pendingQueryTimeout * 1000L;
00172 }
00173
00174
00175
00176
00177
00178
00179 public HashMap getQueries()
00180 {
00181 return this.queries;
00182 }
00183
00184
00185
00186
00187
00188
00189
00190 public void setDatabaseSchema(DatabaseSchema dbs)
00191 {
00192 if (cdbs == null)
00193 {
00194 logger.info(Translate.get("resultcache.setting.database.schema"));
00195 cdbs = new CacheDatabaseSchema(dbs);
00196 }
00197 else
00198 {
00199 CacheDatabaseSchema newSchema = new CacheDatabaseSchema(dbs);
00200 ArrayList tables = cdbs.getTables();
00201 ArrayList newTables = newSchema.getTables();
00202 if (newTables == null)
00203 {
00204 logger.info(Translate.get("resultcache.flusing.whole.cache"));
00205 flushCache();
00206 cdbs = null;
00207 return;
00208 }
00209
00210
00211 for (int i = 0; i < tables.size(); i++)
00212 {
00213 CacheDatabaseTable t = (CacheDatabaseTable) tables.get(i);
00214 if (!newSchema.hasTable(t.getName()))
00215 {
00216 t.invalidateAll();
00217 cdbs.removeTable(t);
00218 if (logger.isInfoEnabled())
00219 logger.info(Translate
00220 .get("resultcache.removing.table", t.getName()));
00221 }
00222 }
00223
00224
00225 int size = newTables.size();
00226 for (int i = 0; i < size; i++)
00227 {
00228 CacheDatabaseTable t = (CacheDatabaseTable) newTables.get(i);
00229 if (!cdbs.hasTable(t.getName()))
00230 {
00231 cdbs.addTable(t);
00232 if (logger.isInfoEnabled())
00233 logger.info(Translate.get("resultcache.adding.table", t.getName()));
00234 }
00235 }
00236 }
00237 }
00238
00239
00240
00241
00242
00243
00244
00245 public void mergeDatabaseSchema(DatabaseSchema dbs)
00246 {
00247 try
00248 {
00249 logger.info(Translate.get("resultcache.merging.new.database.schema"));
00250 cdbs.mergeSchema(new CacheDatabaseSchema(dbs));
00251 }
00252 catch (Exception e)
00253 {
00254 logger.error(Translate.get("resultcache.error.while.merging", e));
00255 }
00256 }
00257
00258
00259
00260
00261
00262
00263
00264 public void addCachingRule(ResultCacheRule rule)
00265 {
00266 cachingRules.add(rule);
00267 }
00268
00269
00270
00271
00272 public ResultCacheRule getDefaultRule()
00273 {
00274 return defaultRule;
00275 }
00276
00277
00278
00279
00280 public void setDefaultRule(ResultCacheRule defaultRule)
00281 {
00282 this.defaultRule = defaultRule;
00283 }
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293 private CacheBehavior getCacheBehavior(SelectRequest request)
00294 {
00295 CacheBehavior behavior = null;
00296 for (Iterator iter = cachingRules.iterator(); iter.hasNext();)
00297 {
00298 behavior = ((ResultCacheRule) iter.next()).matches(request);
00299 if (behavior != null)
00300 {
00301 break;
00302 }
00303 }
00304 if (behavior == null)
00305 behavior = defaultRule.getCacheBehavior();
00306 if (logger.isDebugEnabled())
00307 logger.debug(Translate.get("resultcache.behavior.for.request",
00308 new String[]{request.getSQL(), behavior.getType()}));
00309 return behavior;
00310 }
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326 public boolean[] needInvalidate(ControllerResultSet result,
00327 UpdateRequest request)
00328 {
00329 HashMap updatedValues = request.getUpdatedValues();
00330 boolean needInvalidate = false;
00331 boolean needToSendQuery = false;
00332 String value;
00333 String columnName;
00334 try
00335 {
00336
00337 if ((result == null) || (result.getData() == null)
00338 || (result.getData().size() != 1))
00339 return TRUE_TRUE;
00340 }
00341 catch (Exception e)
00342 {
00343 return TRUE_TRUE;
00344 }
00345 Field[] fields = result.getFields();
00346 ArrayList data = result.getData();
00347 int size = fields.length;
00348 for (Iterator iter = updatedValues.keySet().iterator(); iter.hasNext();)
00349 {
00350 columnName = (String) iter.next();
00351 value = (String) updatedValues.get(columnName);
00352 for (int i = 0; i < size; i++)
00353 {
00354
00355
00356
00357
00358 if (columnName.equals(fields[i].getFieldName()))
00359 {
00360 Object o = ((Object[]) data.get(0))[i];
00361 if (!value.equals(o))
00362 {
00363
00364
00365
00366 return TRUE_TRUE;
00367 }
00368 else
00369 break;
00370 }
00371 }
00372
00373
00374 needToSendQuery = true;
00375
00376
00377
00378 }
00379 return new boolean[]{needInvalidate, needToSendQuery};
00380 }
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390 public void addToCache(SelectRequest request, ControllerResultSet result)
00391 throws CacheException
00392 {
00393 String sqlQuery = request.getSQL();
00394
00395
00396 if (request.getCacheAbility() == RequestType.UNCACHEABLE)
00397 throw new CacheException(Translate.get("resultcache.uncacheable.request",
00398 sqlQuery));
00399
00400 if (result == null)
00401 throw new CacheException(Translate.get("resultcache.null.result",
00402 sqlQuery));
00403
00404 boolean notifyThread = false;
00405
00406 try
00407 {
00408 synchronized (pendingQueries)
00409 {
00410
00411
00412 if (pendingQueries.remove(sqlQuery))
00413 {
00414 if (logger.isDebugEnabled())
00415 logger.debug(Translate.get("resultcache.removing.pending.query",
00416 sqlQuery));
00417 pendingQueries.notifyAll();
00418 }
00419 else
00420 logger.warn(Translate.get(
00421 "resultcache.removing.pending.query.failed", sqlQuery));
00422
00423
00424 if (result.hasMoreData())
00425 {
00426 logger.info(Translate.get("resultcache.streamed.resultset", request
00427 .getSQLShortForm(20)));
00428 return;
00429 }
00430
00431 if (logger.isDebugEnabled())
00432 logger.debug(Translate.get("resultcache.adding.query", sqlQuery));
00433
00434 CacheEntry ce;
00435 synchronized (queries)
00436 {
00437
00438 ce = (ResultCacheEntry) queries.get(sqlQuery);
00439 if (ce == null)
00440 {
00441
00442
00443 CacheBehavior behavior = getCacheBehavior(request);
00444 ce = behavior.getCacheEntry(request, result, this);
00445 if (ce instanceof ResultCacheEntryNoCache)
00446 return;
00447
00448
00449 if (maxEntries > 0)
00450 {
00451 int size = queries.size();
00452 if (size >= maxEntries)
00453
00454 removeOldest();
00455 }
00456
00457 queries.put(sqlQuery, ce);
00458
00459 notifyThread = true;
00460
00461 }
00462 else
00463 {
00464 if (ce.isValid())
00465 logger.warn(Translate.get(
00466 "resultcache.modifying.result.valid.entry", sqlQuery));
00467 ce.setResult(result);
00468 }
00469
00470
00471 if (lruHead != null)
00472 {
00473 lruHead.setPrev(ce);
00474 ce.setNext(lruHead);
00475 ce.setPrev(null);
00476 }
00477 if (lruTail == null)
00478 lruTail = ce;
00479 lruHead = ce;
00480 }
00481 processAddToCache(ce);
00482
00483
00484
00485
00486 if (notifyThread)
00487 {
00488
00489 if (ce instanceof ResultCacheEntryRelaxed)
00490 {
00491 ResultCacheEntryRelaxed qcer = (ResultCacheEntryRelaxed) ce;
00492 synchronized (relaxedThread)
00493 {
00494 relaxedCache.add(qcer);
00495 if (qcer.getDeadline() < relaxedThread.getThreadWakeUpTime()
00496 || relaxedThread.getThreadWakeUpTime() == 0)
00497 {
00498 relaxedThread.notify();
00499 }
00500 }
00501 }
00502 else if (ce instanceof ResultCacheEntryEager)
00503 {
00504
00505 ResultCacheEntryEager qcee = (ResultCacheEntryEager) ce;
00506 if (qcee.getDeadline() != ResultCacheEntry.NO_DEADLINE)
00507 {
00508 synchronized (eagerThread)
00509 {
00510 eagerCache.add(qcee);
00511 if (qcee.getDeadline() < eagerThread.getThreadWakeUpTime()
00512 || eagerThread.getThreadWakeUpTime() == 0)
00513 {
00514 eagerThread.notify();
00515 }
00516 }
00517 }
00518 }
00519 }
00520
00521 }
00522 }
00523 catch (OutOfMemoryError oome)
00524 {
00525 flushCache();
00526 System.gc();
00527 logger.warn(Translate.get("cache.memory.error.cache.flushed", this
00528 .getClass()));
00529 }
00530 }
00531
00532
00533
00534
00535
00536
00537 protected abstract void processAddToCache(CacheEntry qe);
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554 public CacheEntry getFromCache(SelectRequest request,
00555 boolean addToPendingQueries)
00556 {
00557 stats.addSelect();
00558
00559 if (request.getCacheAbility() == RequestType.UNCACHEABLE)
00560 {
00561 stats.addUncacheable();
00562 return null;
00563 }
00564
00565 String sqlQuery = request.getSQL();
00566
00567
00568 synchronized (pendingQueries)
00569 {
00570 if (addToPendingQueries)
00571 {
00572 long timeout = pendingQueryTimeout;
00573
00574
00575
00576 while (pendingQueries.contains(sqlQuery))
00577 {
00578 try
00579 {
00580 if (logger.isDebugEnabled())
00581 logger.debug(Translate.get("resultcache.waiting.pending.query",
00582 sqlQuery));
00583
00584 if (timeout > 0)
00585 {
00586 long start = System.currentTimeMillis();
00587 pendingQueries.wait(pendingQueryTimeout);
00588 long end = System.currentTimeMillis();
00589 timeout = timeout - (end - start);
00590 if (timeout <= 0)
00591 {
00592 logger.warn(Translate.get("resultcache.pending.query.timeout"));
00593 break;
00594 }
00595 }
00596 else
00597 pendingQueries.wait();
00598 }
00599 catch (InterruptedException e)
00600 {
00601 logger.warn(Translate.get("resultcache.pending.query.timeout"));
00602 break;
00603 }
00604 }
00605 }
00606
00607
00608 ResultCacheEntry ce;
00609 synchronized (queries)
00610 {
00611 ce = (ResultCacheEntry) queries.get(sqlQuery);
00612 if (ce == null)
00613
00614 {
00615 if (addToPendingQueries)
00616 {
00617 pendingQueries.add(sqlQuery);
00618
00619 if (logger.isDebugEnabled())
00620 {
00621 logger.debug(Translate.get("resultcache.cache.miss"));
00622 logger.debug(Translate.get(
00623 "resultcache.adding.to.pending.queries", sqlQuery));
00624 }
00625 }
00626 return null;
00627 }
00628 else
00629 {
00630
00631 CacheEntry before = ce.getPrev();
00632 if (before != null)
00633 {
00634 CacheEntry after = ce.getNext();
00635 before.setNext(after);
00636 if (after != null)
00637 after.setPrev(before);
00638 else
00639
00640 lruTail = before;
00641 ce.setNext(lruHead);
00642 ce.setPrev(null);
00643 if (lruHead != ce)
00644 lruHead.setPrev(ce);
00645 lruHead = ce;
00646 }
00647
00648 }
00649 }
00650
00651 if (ce.getResult() == null)
00652 {
00653 if (addToPendingQueries)
00654 {
00655 pendingQueries.add(sqlQuery);
00656
00657 if (logger.isDebugEnabled())
00658 {
00659 logger.debug(Translate.get("resultcache.cache.miss"));
00660 logger.debug(Translate.get("resultcache.adding.to.pending.queries",
00661 sqlQuery));
00662 }
00663 }
00664 if (ce.isValid() && logger.isInfoEnabled())
00665 logger.info(Translate.get("resultcache.valid.entry.without.result",
00666 ce.getRequest().getSQL()));
00667 }
00668 else
00669 {
00670 if (logger.isDebugEnabled())
00671 logger.debug(Translate.get("resultcache.cache.hit", sqlQuery));
00672 stats.addHits();
00673 }
00674
00675 return ce;
00676 }
00677 }
00678
00679
00680
00681
00682
00683
00684
00685
00686 public void removeFromCache(SelectRequest request)
00687 {
00688 String sqlQuery = request.getSQL();
00689
00690 if (logger.isDebugEnabled())
00691 logger.debug("Removing from cache: " + sqlQuery);
00692
00693 synchronized (queries)
00694 {
00695
00696 ResultCacheEntry ce = (ResultCacheEntry) queries.remove(sqlQuery);
00697 if (ce == null)
00698 return;
00699 else
00700 {
00701
00702 ce.setResult(null);
00703
00704 CacheEntry before = ce.getPrev();
00705 CacheEntry after = ce.getNext();
00706 if (before != null)
00707 {
00708 before.setNext(after);
00709 if (after != null)
00710 after.setPrev(before);
00711 else
00712
00713 lruTail = before;
00714 }
00715 else
00716 {
00717 lruHead = ce.getNext();
00718 if (after != null)
00719 after.setPrev(null);
00720 else
00721
00722 lruTail = before;
00723 }
00724
00725 ce.setNext(null);
00726 ce.setPrev(null);
00727 }
00728 }
00729 }
00730
00731
00732
00733
00734
00735
00736 public void removeFromPendingQueries(SelectRequest request)
00737 {
00738 String sqlQuery = request.getSQL();
00739
00740 synchronized (pendingQueries)
00741 {
00742
00743
00744 if (pendingQueries.remove(sqlQuery))
00745 pendingQueries.notifyAll();
00746 }
00747 }
00748
00749
00750
00751
00752 public abstract boolean isUpdateNecessary(UpdateRequest request)
00753 throws CacheException;
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763 public void writeNotify(AbstractWriteRequest request) throws CacheException
00764 {
00765
00766 if (request.isInsert())
00767 stats.addInsert();
00768 else if (request.isUpdate())
00769 stats.addUpdate();
00770 else if (request.isDelete())
00771 stats.addDelete();
00772 else if (request.isCreate())
00773 {
00774 stats.addCreate();
00775
00776 if (parsingGranularity != ParsingGranularities.NO_PARSING)
00777 cdbs.addTable(new CacheDatabaseTable(((CreateRequest) request)
00778 .getDatabaseTable()));
00779 return;
00780 }
00781 else if (request.isDrop())
00782 {
00783 stats.addDrop();
00784
00785 if (parsingGranularity != ParsingGranularities.NO_PARSING)
00786 {
00787
00788 CacheDatabaseTable cdt = cdbs.getTable(request.getTableName());
00789 if (cdt != null)
00790 {
00791 cdt.invalidateAll();
00792 cdbs.removeTable(cdt);
00793 return;
00794 }
00795
00796
00797 }
00798 }
00799 else
00800 {
00801 stats.addUnknown();
00802 }
00803 if (logger.isDebugEnabled())
00804 logger.debug("Notifying write " + request.getSQL());
00805
00806 processWriteNotify(request);
00807 }
00808
00809
00810
00811
00812
00813
00814 protected abstract void processWriteNotify(AbstractWriteRequest request);
00815
00816
00817
00818
00819 public void flushCache()
00820 {
00821
00822 synchronized (this)
00823 {
00824 if (flushingCache)
00825 return;
00826 flushingCache = true;
00827 }
00828
00829 try
00830 {
00831 synchronized (queries)
00832 {
00833 while (!queries.isEmpty())
00834 {
00835 Iterator iter = queries.values().iterator();
00836 ((ResultCacheEntry) iter.next()).invalidate();
00837 }
00838 }
00839
00840 synchronized (pendingQueries)
00841 {
00842
00843 pendingQueries.clear();
00844 pendingQueries.notifyAll();
00845 }
00846 }
00847 finally
00848 {
00849 synchronized (this)
00850 {
00851 flushingCache = false;
00852 }
00853 if (logger.isDebugEnabled())
00854 logger.debug(Translate.get("resultcache.cache.flushed"));
00855 }
00856 }
00857
00858
00859
00860
00861
00862
00863 public long getCacheSize()
00864 {
00865
00866 return queries.size();
00867 }
00868
00869
00870
00871
00872
00873
00874
00875 private void removeOldest()
00876 {
00877 if (lruTail == null)
00878 return;
00879
00880 ResultCacheEntry oldce = (ResultCacheEntry) lruTail;
00881 lruTail = lruTail.getPrev();
00882 if (lruTail != null)
00883 lruTail.setNext(null);
00884
00885 if (logger.isDebugEnabled())
00886 logger.debug(Translate.get("resultcache.removing.oldest.cache.entry",
00887 oldce.getRequest().getSQL()));
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897 queries.remove(oldce.getRequest().getSQL());
00898
00899 if (oldce.isValid())
00900 {
00901 oldce.setResult(null);
00902 oldce.invalidate();
00903 }
00904
00905 stats.addRemove();
00906 }
00907
00908
00909
00910
00911
00912
00913 public int getParsingGranularity()
00914 {
00915 return this.parsingGranularity;
00916 }
00917
00918
00919
00920
00921
00922
00923 public abstract String getName();
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935 public void commit(long transactionId) throws CacheException
00936 {
00937
00938 }
00939
00940
00941
00942
00943
00944
00945
00946 public void rollback(long transactionId) throws CacheException
00947 {
00948 logger.info(Translate.get("resultcache.flushing.cache.cause.rollback",
00949 transactionId));
00950 flushCache();
00951 }
00952
00953
00954
00955
00956
00957
00958
00959
00960 public String[][] getCacheData() throws CacheException
00961 {
00962 try
00963 {
00964 synchronized (queries)
00965 {
00966 String[][] data = new String[queries.size()][];
00967 int count = 0;
00968 for (Iterator iter = queries.values().iterator(); iter.hasNext(); count++)
00969 {
00970 ResultCacheEntry qe = (ResultCacheEntry) iter.next();
00971 if (qe != null)
00972 {
00973 data[count] = qe.toStringTable();
00974 }
00975 }
00976 return data;
00977 }
00978 }
00979 catch (Exception e)
00980 {
00981 logger.error(Translate.get("resultcache.error.retrieving.cache.data", e));
00982 throw new CacheException(e.getMessage());
00983 }
00984 }
00985
00986
00987
00988
00989 public String[][] getCacheStatsData() throws CacheException
00990 {
00991 String[][] data = new String[1][];
00992 String[] stat = stats.getCacheStatsData();
00993 data[0] = new String[stat.length + 1];
00994 for (int i = 0; i < stat.length; i++)
00995 data[0][i] = stat[i];
00996 data[0][data[0].length - 1] = "" + queries.size();
00997 return data;
00998 }
00999
01000
01001
01002
01003 public CacheStatistics getCacheStatistics()
01004 {
01005 return stats;
01006 }
01007
01008
01009
01010
01011
01012
01013 public ArrayList getEagerCache()
01014 {
01015 return eagerCache;
01016 }
01017
01018
01019
01020
01021
01022
01023 public ArrayList getRelaxedCache()
01024 {
01025 return relaxedCache;
01026 }
01027
01028
01029
01030
01031
01032
01033 protected String getXmlImpl()
01034 {
01035 StringBuffer info = new StringBuffer();
01036 info.append("<" + DatabasesXmlTags.ELT_ResultCache + " "
01037 + DatabasesXmlTags.ATT_pendingTimeout + "=\"" + pendingQueryTimeout
01038 + "\" " + DatabasesXmlTags.ATT_maxNbOfEntries + "=\"" + maxEntries
01039 + "\" " + DatabasesXmlTags.ATT_granularity + "=\"" + getName() + "\">");
01040 info.append("<" + DatabasesXmlTags.ELT_DefaultResultCacheRule + " "
01041 + DatabasesXmlTags.ATT_timestampResolution + "=\""
01042 + defaultRule.getTimestampResolution() / 1000 + "\">");
01043 info.append(defaultRule.getCacheBehavior().getXml());
01044 info.append("</" + DatabasesXmlTags.ELT_DefaultResultCacheRule + ">");
01045 for (Iterator iter = cachingRules.iterator(); iter.hasNext();)
01046 info.append(((ResultCacheRule) iter.next()).getXml());
01047 info.append("</" + DatabasesXmlTags.ELT_ResultCache + ">");
01048 return info.toString();
01049 }
01050
01051 }