Source Code Cross Referenced for Cache.java in  » Cache » OSCache » com » opensymphony » oscache » base » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
C# / C Sharp
C# / CSharp Tutorial
ASP.Net
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
PHP
Python
SQL Server / T-SQL
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Cache » OSCache » com.opensymphony.oscache.base 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright (c) 2002-2003 by OpenSymphony
0003:         * All rights reserved.
0004:         */
0005:        package com.opensymphony.oscache.base;
0006:
0007:        import com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache;
0008:        import com.opensymphony.oscache.base.algorithm.LRUCache;
0009:        import com.opensymphony.oscache.base.algorithm.UnlimitedCache;
0010:        import com.opensymphony.oscache.base.events.*;
0011:        import com.opensymphony.oscache.base.persistence.PersistenceListener;
0012:        import com.opensymphony.oscache.util.FastCronParser;
0013:
0014:        import org.apache.commons.logging.Log;
0015:        import org.apache.commons.logging.LogFactory;
0016:
0017:        import java.io.Serializable;
0018:
0019:        import java.text.ParseException;
0020:
0021:        import java.util.*;
0022:
0023:        import javax.swing.event.EventListenerList;
0024:
0025:        /**
0026:         * Provides an interface to the cache itself. Creating an instance of this class
0027:         * will create a cache that behaves according to its construction parameters.
0028:         * The public API provides methods to manage objects in the cache and configure
0029:         * any cache event listeners.
0030:         *
0031:         * @version        $Revision: 468 $
0032:         * @author <a href="mailto:mike@atlassian.com">Mike Cannon-Brookes</a>
0033:         * @author <a href="mailto:tgochenour@peregrine.com">Todd Gochenour</a>
0034:         * @author <a href="mailto:fbeauregard@pyxis-tech.com">Francois Beauregard</a>
0035:         * @author <a href="&#109;a&#105;&#108;&#116;&#111;:chris&#64;swebtec.&#99;&#111;&#109;">Chris Miller</a>
0036:         */
0037:        public class Cache implements  Serializable {
0038:            /**
0039:             * An event that origininated from within another event.
0040:             */
0041:            public static final String NESTED_EVENT = "NESTED";
0042:            private static transient final Log log = LogFactory
0043:                    .getLog(Cache.class);
0044:
0045:            /**
0046:             * A list of all registered event listeners for this cache.
0047:             */
0048:            protected EventListenerList listenerList = new EventListenerList();
0049:
0050:            /**
0051:             * The actual cache map. This is where the cached objects are held.
0052:             */
0053:            private AbstractConcurrentReadCache cacheMap = null;
0054:
0055:            /**
0056:             * Date of last complete cache flush.
0057:             */
0058:            private Date flushDateTime = null;
0059:
0060:            /**
0061:             * A map that holds keys of cache entries that are currently being built, and EntryUpdateState instance as values. This is used to coordinate threads
0062:             * that modify/access a same key in concurrence.
0063:             * 
0064:             * The cache checks against this map when a stale entry is requested, or a cache miss is observed.
0065:             * 
0066:             * If the requested key is in here, we know the entry is currently being
0067:             * built by another thread and hence we can either block and wait or serve
0068:             * the stale entry (depending on whether cache blocking is enabled or not).
0069:             * <p>
0070:             * To avoid data races, values in this map should remain present during the whole time distinct threads deal with the
0071:             * same key. We implement this using explicit reference counting in the EntryUpdateState instance, to be able to clean up
0072:             * the map once all threads have declared they are done accessing/updating a given key.
0073:             * 
0074:             * It is not possible to locate this into the CacheEntry because this would require to have a CacheEntry instance for all cache misses, and
0075:             * may therefore generate a memory leak. More over, the CacheEntry instance may not be hold in memory in the case no
0076:             * memory cache is configured.
0077:             */
0078:            private Map updateStates = new HashMap();
0079:
0080:            /**
0081:             * Indicates whether the cache blocks requests until new content has
0082:             * been generated or just serves stale content instead.
0083:             */
0084:            private boolean blocking = false;
0085:
0086:            /**
0087:             * Create a new Cache
0088:             *
0089:             * @param useMemoryCaching Specify if the memory caching is going to be used
0090:             * @param unlimitedDiskCache Specify if the disk caching is unlimited
0091:             * @param overflowPersistence Specify if the persistent cache is used in overflow only mode
0092:             */
0093:            public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache,
0094:                    boolean overflowPersistence) {
0095:                this (useMemoryCaching, unlimitedDiskCache, overflowPersistence,
0096:                        false, null, 0);
0097:            }
0098:
0099:            /**
0100:             * Create a new Cache.
0101:             *
0102:             * If a valid algorithm class is specified, it will be used for this cache.
0103:             * Otherwise if a capacity is specified, it will use LRUCache.
0104:             * If no algorithm or capacity is specified UnlimitedCache is used.
0105:             *
0106:             * @see com.opensymphony.oscache.base.algorithm.LRUCache
0107:             * @see com.opensymphony.oscache.base.algorithm.UnlimitedCache
0108:             * @param useMemoryCaching Specify if the memory caching is going to be used
0109:             * @param unlimitedDiskCache Specify if the disk caching is unlimited
0110:             * @param overflowPersistence Specify if the persistent cache is used in overflow only mode
0111:             * @param blocking This parameter takes effect when a cache entry has
0112:             * just expired and several simultaneous requests try to retrieve it. While
0113:             * one request is rebuilding the content, the other requests will either
0114:             * block and wait for the new content (<code>blocking == true</code>) or
0115:             * instead receive a copy of the stale content so they don't have to wait
0116:             * (<code>blocking == false</code>). the default is <code>false</code>,
0117:             * which provides better performance but at the expense of slightly stale
0118:             * data being served.
0119:             * @param algorithmClass The class implementing the desired algorithm
0120:             * @param capacity The capacity
0121:             */
0122:            public Cache(boolean useMemoryCaching, boolean unlimitedDiskCache,
0123:                    boolean overflowPersistence, boolean blocking,
0124:                    String algorithmClass, int capacity) {
0125:                // Instantiate the algo class if valid
0126:                if (((algorithmClass != null) && (algorithmClass.length() > 0))
0127:                        && (capacity > 0)) {
0128:                    try {
0129:                        cacheMap = (AbstractConcurrentReadCache) Class.forName(
0130:                                algorithmClass).newInstance();
0131:                        cacheMap.setMaxEntries(capacity);
0132:                    } catch (Exception e) {
0133:                        log
0134:                                .error("Invalid class name for cache algorithm class. "
0135:                                        + e.toString());
0136:                    }
0137:                }
0138:
0139:                if (cacheMap == null) {
0140:                    // If we have a capacity, use LRU cache otherwise use unlimited Cache
0141:                    if (capacity > 0) {
0142:                        cacheMap = new LRUCache(capacity);
0143:                    } else {
0144:                        cacheMap = new UnlimitedCache();
0145:                    }
0146:                }
0147:
0148:                cacheMap.setUnlimitedDiskCache(unlimitedDiskCache);
0149:                cacheMap.setOverflowPersistence(overflowPersistence);
0150:                cacheMap.setMemoryCaching(useMemoryCaching);
0151:
0152:                this .blocking = blocking;
0153:            }
0154:
0155:            /**
0156:             * @return the maximum number of items to cache can hold.
0157:             */
0158:            public int getCapacity() {
0159:                return cacheMap.getMaxEntries();
0160:            }
0161:
0162:            /**
0163:             * Allows the capacity of the cache to be altered dynamically. Note that
0164:             * some cache implementations may choose to ignore this setting (eg the
0165:             * {@link UnlimitedCache} ignores this call).
0166:             *
0167:             * @param capacity the maximum number of items to hold in the cache.
0168:             */
0169:            public void setCapacity(int capacity) {
0170:                cacheMap.setMaxEntries(capacity);
0171:            }
0172:
0173:            /**
0174:             * Checks if the cache was flushed more recently than the CacheEntry provided.
0175:             * Used to determine whether to refresh the particular CacheEntry.
0176:             *
0177:             * @param cacheEntry The cache entry which we're seeing whether to refresh
0178:             * @return Whether or not the cache has been flushed more recently than this cache entry was updated.
0179:             */
0180:            public boolean isFlushed(CacheEntry cacheEntry) {
0181:                if (flushDateTime != null) {
0182:                    final long lastUpdate = cacheEntry.getLastUpdate();
0183:                    final long flushTime = flushDateTime.getTime();
0184:
0185:                    // CACHE-241: check flushDateTime with current time also
0186:                    return (flushTime <= System.currentTimeMillis())
0187:                            && (flushTime >= lastUpdate);
0188:                } else {
0189:                    return false;
0190:                }
0191:            }
0192:
0193:            /**
0194:             * Retrieve an object from the cache specifying its key.
0195:             *
0196:             * @param key             Key of the object in the cache.
0197:             *
0198:             * @return The object from cache
0199:             *
0200:             * @throws NeedsRefreshException Thrown when the object either
0201:             * doesn't exist, or exists but is stale. When this exception occurs,
0202:             * the CacheEntry corresponding to the supplied key will be locked
0203:             * and other threads requesting this entry will potentially be blocked
0204:             * until the caller repopulates the cache. If the caller choses not
0205:             * to repopulate the cache, they <em>must</em> instead call
0206:             * {@link #cancelUpdate(String)}.
0207:             */
0208:            public Object getFromCache(String key) throws NeedsRefreshException {
0209:                return getFromCache(key, CacheEntry.INDEFINITE_EXPIRY, null);
0210:            }
0211:
0212:            /**
0213:             * Retrieve an object from the cache specifying its key.
0214:             *
0215:             * @param key             Key of the object in the cache.
0216:             * @param refreshPeriod   How long before the object needs refresh. To
0217:             * allow the object to stay in the cache indefinitely, supply a value
0218:             * of {@link CacheEntry#INDEFINITE_EXPIRY}.
0219:             *
0220:             * @return The object from cache
0221:             *
0222:             * @throws NeedsRefreshException Thrown when the object either
0223:             * doesn't exist, or exists but is stale. When this exception occurs,
0224:             * the CacheEntry corresponding to the supplied key will be locked
0225:             * and other threads requesting this entry will potentially be blocked
0226:             * until the caller repopulates the cache. If the caller choses not
0227:             * to repopulate the cache, they <em>must</em> instead call
0228:             * {@link #cancelUpdate(String)}.
0229:             */
0230:            public Object getFromCache(String key, int refreshPeriod)
0231:                    throws NeedsRefreshException {
0232:                return getFromCache(key, refreshPeriod, null);
0233:            }
0234:
0235:            /**
0236:             * Retrieve an object from the cache specifying its key.
0237:             *
0238:             * @param key             Key of the object in the cache.
0239:             * @param refreshPeriod   How long before the object needs refresh. To
0240:             * allow the object to stay in the cache indefinitely, supply a value
0241:             * of {@link CacheEntry#INDEFINITE_EXPIRY}.
0242:             * @param cronExpiry      A cron expression that specifies fixed date(s)
0243:             *                        and/or time(s) that this cache entry should
0244:             *                        expire on.
0245:             *
0246:             * @return The object from cache
0247:             *
0248:             * @throws NeedsRefreshException Thrown when the object either
0249:             * doesn't exist, or exists but is stale. When this exception occurs,
0250:             * the CacheEntry corresponding to the supplied key will be locked
0251:             * and other threads requesting this entry will potentially be blocked
0252:             * until the caller repopulates the cache. If the caller choses not
0253:             * to repopulate the cache, they <em>must</em> instead call
0254:             * {@link #cancelUpdate(String)}.
0255:             */
0256:            public Object getFromCache(String key, int refreshPeriod,
0257:                    String cronExpiry) throws NeedsRefreshException {
0258:                CacheEntry cacheEntry = this .getCacheEntry(key, null, null);
0259:
0260:                Object content = cacheEntry.getContent();
0261:                CacheMapAccessEventType accessEventType = CacheMapAccessEventType.HIT;
0262:
0263:                boolean reload = false;
0264:
0265:                // Check if this entry has expired or has not yet been added to the cache. If
0266:                // so, we need to decide whether to block, serve stale content or throw a
0267:                // NeedsRefreshException
0268:                if (this .isStale(cacheEntry, refreshPeriod, cronExpiry)) {
0269:
0270:                    //Get access to the EntryUpdateState instance and increment the usage count during the potential sleep
0271:                    EntryUpdateState updateState = getUpdateState(key);
0272:                    try {
0273:                        synchronized (updateState) {
0274:                            if (updateState.isAwaitingUpdate()
0275:                                    || updateState.isCancelled()) {
0276:                                // No one else is currently updating this entry - grab ownership
0277:                                updateState.startUpdate();
0278:
0279:                                if (cacheEntry.isNew()) {
0280:                                    accessEventType = CacheMapAccessEventType.MISS;
0281:                                } else {
0282:                                    accessEventType = CacheMapAccessEventType.STALE_HIT;
0283:                                }
0284:                            } else if (updateState.isUpdating()) {
0285:                                // Another thread is already updating the cache. We block if this
0286:                                // is a new entry, or blocking mode is enabled. Either putInCache()
0287:                                // or cancelUpdate() can cause this thread to resume.
0288:                                if (cacheEntry.isNew() || blocking) {
0289:                                    do {
0290:                                        try {
0291:                                            updateState.wait();
0292:                                        } catch (InterruptedException e) {
0293:                                        }
0294:                                    } while (updateState.isUpdating());
0295:
0296:                                    if (updateState.isCancelled()) {
0297:                                        // The updating thread cancelled the update, let this one have a go. 
0298:                                        // This increments the usage count for this EntryUpdateState instance
0299:                                        updateState.startUpdate();
0300:
0301:                                        if (cacheEntry.isNew()) {
0302:                                            accessEventType = CacheMapAccessEventType.MISS;
0303:                                        } else {
0304:                                            accessEventType = CacheMapAccessEventType.STALE_HIT;
0305:                                        }
0306:                                    } else if (updateState.isComplete()) {
0307:                                        reload = true;
0308:                                    } else {
0309:                                        log
0310:                                                .error("Invalid update state for cache entry "
0311:                                                        + key);
0312:                                    }
0313:                                }
0314:                            } else {
0315:                                reload = true;
0316:                            }
0317:                        }
0318:                    } finally {
0319:                        //Make sure we release the usage count for this EntryUpdateState since we don't use it anymore. If the current thread started the update, then the counter was
0320:                        //increased by one in startUpdate()
0321:                        releaseUpdateState(updateState, key);
0322:                    }
0323:                }
0324:
0325:                // If reload is true then another thread must have successfully rebuilt the cache entry
0326:                if (reload) {
0327:                    cacheEntry = (CacheEntry) cacheMap.get(key);
0328:
0329:                    if (cacheEntry != null) {
0330:                        content = cacheEntry.getContent();
0331:                    } else {
0332:                        log
0333:                                .error("Could not reload cache entry after waiting for it to be rebuilt");
0334:                    }
0335:                }
0336:
0337:                dispatchCacheMapAccessEvent(accessEventType, cacheEntry, null);
0338:
0339:                // If we didn't end up getting a hit then we need to throw a NRE
0340:                if (accessEventType != CacheMapAccessEventType.HIT) {
0341:                    throw new NeedsRefreshException(content);
0342:                }
0343:
0344:                return content;
0345:            }
0346:
0347:            /**
0348:             * Set the listener to use for data persistence. Only one
0349:             * <code>PersistenceListener</code> can be configured per cache.
0350:             *
0351:             * @param listener The implementation of a persistance listener
0352:             */
0353:            public void setPersistenceListener(PersistenceListener listener) {
0354:                cacheMap.setPersistenceListener(listener);
0355:            }
0356:
0357:            /**
0358:             * Retrieves the currently configured <code>PersistenceListener</code>.
0359:             *
0360:             * @return the cache's <code>PersistenceListener</code>, or <code>null</code>
0361:             * if no listener is configured.
0362:             */
0363:            public PersistenceListener getPersistenceListener() {
0364:                return cacheMap.getPersistenceListener();
0365:            }
0366:
0367:            /**
0368:             * Register a listener for Cache events. The listener must implement
0369:             * one of the child interfaces of the {@link CacheEventListener} interface.
0370:             *
0371:             * @param listener  The object that listens to events.
0372:             * @since 2.4
0373:             */
0374:            public void addCacheEventListener(CacheEventListener listener) {
0375:                // listenerList.add(CacheEventListener.class, listener);
0376:                listenerList.add(listener.getClass(), listener);
0377:            }
0378:
0379:            /**
0380:             * Register a listener for Cache events. The listener must implement
0381:             * one of the child interfaces of the {@link CacheEventListener} interface.
0382:             *
0383:             * @param listener  The object that listens to events.
0384:             * @param clazz the type of the listener to be added
0385:             * @deprecated use {@link #addCacheEventListener(CacheEventListener)}
0386:             */
0387:            public void addCacheEventListener(CacheEventListener listener,
0388:                    Class clazz) {
0389:                if (CacheEventListener.class.isAssignableFrom(clazz)) {
0390:                    listenerList.add(clazz, listener);
0391:                } else {
0392:                    log
0393:                            .error("The class '"
0394:                                    + clazz.getName()
0395:                                    + "' is not a CacheEventListener. Ignoring this listener.");
0396:                }
0397:            }
0398:
0399:            /**
0400:             * Returns the list of all CacheEventListeners.
0401:             * @return the CacheEventListener's list of the Cache
0402:             */
0403:            public EventListenerList getCacheEventListenerList() {
0404:                return listenerList;
0405:            }
0406:
0407:            /**
0408:             * Cancels any pending update for this cache entry. This should <em>only</em>
0409:             * be called by the thread that is responsible for performing the update ie
0410:             * the thread that received the original {@link NeedsRefreshException}.<p/>
0411:             * If a cache entry is not updated (via {@link #putInCache} and this method is
0412:             * not called to let OSCache know the update will not be forthcoming, subsequent
0413:             * requests for this cache entry will either block indefinitely (if this is a new
0414:             * cache entry or cache.blocking=true), or forever get served stale content. Note
0415:             * however that there is no harm in cancelling an update on a key that either
0416:             * does not exist or is not currently being updated.
0417:             *
0418:             * @param key The key for the cache entry in question.
0419:             * @throws IllegalStateException if the cache entry isn't in the state UPDATE_IN_PROGRESS
0420:             */
0421:            public void cancelUpdate(String key) {
0422:                EntryUpdateState state;
0423:
0424:                if (key != null) {
0425:                    synchronized (updateStates) {
0426:                        state = (EntryUpdateState) updateStates.get(key);
0427:
0428:                        if (state != null) {
0429:                            synchronized (state) {
0430:                                int usageCounter = state.cancelUpdate();
0431:                                state.notify();
0432:
0433:                                checkEntryStateUpdateUsage(key, state,
0434:                                        usageCounter);
0435:                            }
0436:                        } else {
0437:                            if (log.isErrorEnabled()) {
0438:                                log
0439:                                        .error("internal error: expected to get a state from key ["
0440:                                                + key + "]");
0441:                            }
0442:                        }
0443:                    }
0444:                }
0445:            }
0446:
0447:            /**
0448:             * Utility method to check if the specified usage count is zero, and if so remove the corresponding EntryUpdateState from the updateStates. This is designed to factor common code.
0449:             * 
0450:             * Warning: This method should always be called while holding both the updateStates field and the state parameter
0451:             * @throws Exception
0452:             */
0453:            private void checkEntryStateUpdateUsage(String key,
0454:                    EntryUpdateState state, int usageCounter) {
0455:                //Clean up the updateStates map to avoid a memory leak once no thread is using this EntryUpdateState instance anymore.
0456:                if (usageCounter == 0) {
0457:                    EntryUpdateState removedState = (EntryUpdateState) updateStates
0458:                            .remove(key);
0459:                    if (state != removedState) {
0460:                        if (log.isErrorEnabled()) {
0461:                            try {
0462:                                throw new Exception(
0463:                                        "OSCache: internal error: removed state ["
0464:                                                + removedState + "] from key ["
0465:                                                + key
0466:                                                + "] whereas we expected ["
0467:                                                + state + "]");
0468:                            } catch (Exception e) {
0469:                                log.error(e);
0470:                            }
0471:                        }
0472:                    }
0473:                }
0474:            }
0475:
0476:            /**
0477:             * Flush all entries in the cache on the given date/time.
0478:             *
0479:             * @param date The date at which all cache entries will be flushed.
0480:             */
0481:            public void flushAll(Date date) {
0482:                flushAll(date, null);
0483:            }
0484:
0485:            /**
0486:             * Flush all entries in the cache on the given date/time.
0487:             *
0488:             * @param date The date at which all cache entries will be flushed.
0489:             * @param origin The origin of this flush request (optional)
0490:             */
0491:            public void flushAll(Date date, String origin) {
0492:                flushDateTime = date;
0493:
0494:                if (listenerList.getListenerCount() > 0) {
0495:                    dispatchCachewideEvent(CachewideEventType.CACHE_FLUSHED,
0496:                            date, origin);
0497:                }
0498:            }
0499:
0500:            /**
0501:             * Flush the cache entry (if any) that corresponds to the cache key supplied.
0502:             * This call will flush the entry from the cache and remove the references to
0503:             * it from any cache groups that it is a member of. On completion of the flush,
0504:             * a <tt>CacheEntryEventType.ENTRY_FLUSHED</tt> event is fired.
0505:             *
0506:             * @param key The key of the entry to flush
0507:             */
0508:            public void flushEntry(String key) {
0509:                flushEntry(key, null);
0510:            }
0511:
0512:            /**
0513:             * Flush the cache entry (if any) that corresponds to the cache key supplied.
0514:             * This call will mark the cache entry as flushed so that the next access
0515:             * to it will cause a {@link NeedsRefreshException}. On completion of the
0516:             * flush, a <tt>CacheEntryEventType.ENTRY_FLUSHED</tt> event is fired.
0517:             *
0518:             * @param key The key of the entry to flush
0519:             * @param origin The origin of this flush request (optional)
0520:             */
0521:            public void flushEntry(String key, String origin) {
0522:                flushEntry(getCacheEntry(key, null, origin), origin);
0523:            }
0524:
0525:            /**
0526:             * Flushes all objects that belong to the supplied group. On completion
0527:             * this method fires a <tt>CacheEntryEventType.GROUP_FLUSHED</tt> event.
0528:             *
0529:             * @param group The group to flush
0530:             */
0531:            public void flushGroup(String group) {
0532:                flushGroup(group, null);
0533:            }
0534:
0535:            /**
0536:             * Flushes all unexpired objects that belong to the supplied group. On
0537:             * completion this method fires a <tt>CacheEntryEventType.GROUP_FLUSHED</tt>
0538:             * event.
0539:             *
0540:             * @param group The group to flush
0541:             * @param origin The origin of this flush event (optional)
0542:             */
0543:            public void flushGroup(String group, String origin) {
0544:                // Flush all objects in the group
0545:                Set groupEntries = cacheMap.getGroup(group);
0546:
0547:                if (groupEntries != null) {
0548:                    Iterator itr = groupEntries.iterator();
0549:                    String key;
0550:                    CacheEntry entry;
0551:
0552:                    while (itr.hasNext()) {
0553:                        key = (String) itr.next();
0554:                        entry = (CacheEntry) cacheMap.get(key);
0555:
0556:                        if ((entry != null)
0557:                                && !entry
0558:                                        .needsRefresh(CacheEntry.INDEFINITE_EXPIRY)) {
0559:                            flushEntry(entry, NESTED_EVENT);
0560:                        }
0561:                    }
0562:                }
0563:
0564:                if (listenerList.getListenerCount() > 0) {
0565:                    dispatchCacheGroupEvent(CacheEntryEventType.GROUP_FLUSHED,
0566:                            group, origin);
0567:                }
0568:            }
0569:
0570:            /**
0571:             * Flush all entries with keys that match a given pattern
0572:             *
0573:             * @param  pattern The key must contain this given value
0574:             * @deprecated For performance and flexibility reasons it is preferable to
0575:             * store cache entries in groups and use the {@link #flushGroup(String)} method
0576:             * instead of relying on pattern flushing.
0577:             */
0578:            public void flushPattern(String pattern) {
0579:                flushPattern(pattern, null);
0580:            }
0581:
0582:            /**
0583:             * Flush all entries with keys that match a given pattern
0584:             *
0585:             * @param  pattern The key must contain this given value
0586:             * @param origin The origin of this flush request
0587:             * @deprecated For performance and flexibility reasons it is preferable to
0588:             * store cache entries in groups and use the {@link #flushGroup(String, String)}
0589:             * method instead of relying on pattern flushing.
0590:             */
0591:            public void flushPattern(String pattern, String origin) {
0592:                // Check the pattern
0593:                if ((pattern != null) && (pattern.length() > 0)) {
0594:                    String key = null;
0595:                    CacheEntry entry = null;
0596:                    Iterator itr = cacheMap.keySet().iterator();
0597:
0598:                    while (itr.hasNext()) {
0599:                        key = (String) itr.next();
0600:
0601:                        if (key.indexOf(pattern) >= 0) {
0602:                            entry = (CacheEntry) cacheMap.get(key);
0603:
0604:                            if (entry != null) {
0605:                                flushEntry(entry, origin);
0606:                            }
0607:                        }
0608:                    }
0609:
0610:                    if (listenerList.getListenerCount() > 0) {
0611:                        dispatchCachePatternEvent(
0612:                                CacheEntryEventType.PATTERN_FLUSHED, pattern,
0613:                                origin);
0614:                    }
0615:                } else {
0616:                    // Empty pattern, nothing to do
0617:                }
0618:            }
0619:
0620:            /**
0621:             * Put an object in the cache specifying the key to use.
0622:             *
0623:             * @param key       Key of the object in the cache.
0624:             * @param content   The object to cache.
0625:             */
0626:            public void putInCache(String key, Object content) {
0627:                putInCache(key, content, null, null, null);
0628:            }
0629:
0630:            /**
0631:             * Put an object in the cache specifying the key and refresh policy to use.
0632:             *
0633:             * @param key       Key of the object in the cache.
0634:             * @param content   The object to cache.
0635:             * @param policy   Object that implements refresh policy logic
0636:             */
0637:            public void putInCache(String key, Object content,
0638:                    EntryRefreshPolicy policy) {
0639:                putInCache(key, content, null, policy, null);
0640:            }
0641:
0642:            /**
0643:             * Put in object into the cache, specifying both the key to use and the
0644:             * cache groups the object belongs to.
0645:             *
0646:             * @param key       Key of the object in the cache
0647:             * @param content   The object to cache
0648:             * @param groups    The cache groups to add the object to
0649:             */
0650:            public void putInCache(String key, Object content, String[] groups) {
0651:                putInCache(key, content, groups, null, null);
0652:            }
0653:
0654:            /**
0655:             * Put an object into the cache specifying both the key to use and the
0656:             * cache groups the object belongs to.
0657:             *
0658:             * @param key       Key of the object in the cache
0659:             * @param groups    The cache groups to add the object to
0660:             * @param content   The object to cache
0661:             * @param policy    Object that implements the refresh policy logic
0662:             */
0663:            public void putInCache(String key, Object content, String[] groups,
0664:                    EntryRefreshPolicy policy, String origin) {
0665:                CacheEntry cacheEntry = this .getCacheEntry(key, policy, origin);
0666:                boolean isNewEntry = cacheEntry.isNew();
0667:
0668:                // [CACHE-118] If we have an existing entry, create a new CacheEntry so we can still access the old one later
0669:                if (!isNewEntry) {
0670:                    cacheEntry = new CacheEntry(key, policy);
0671:                }
0672:
0673:                cacheEntry.setContent(content);
0674:                cacheEntry.setGroups(groups);
0675:                cacheMap.put(key, cacheEntry);
0676:
0677:                // Signal to any threads waiting on this update that it's now ready for them
0678:                // in the cache!
0679:                completeUpdate(key);
0680:
0681:                if (listenerList.getListenerCount() > 0) {
0682:                    CacheEntryEvent event = new CacheEntryEvent(this ,
0683:                            cacheEntry, origin);
0684:
0685:                    if (isNewEntry) {
0686:                        dispatchCacheEntryEvent(
0687:                                CacheEntryEventType.ENTRY_ADDED, event);
0688:                    } else {
0689:                        dispatchCacheEntryEvent(
0690:                                CacheEntryEventType.ENTRY_UPDATED, event);
0691:                    }
0692:                }
0693:            }
0694:
0695:            /**
0696:             * Unregister a listener for Cache events.
0697:             *
0698:             * @param listener  The object that currently listens to events.
0699:             * @param clazz  The registrated class of listening object.
0700:             * @deprecated use instead {@link #removeCacheEventListener(CacheEventListener)}
0701:             */
0702:            public void removeCacheEventListener(CacheEventListener listener,
0703:                    Class clazz) {
0704:                listenerList.remove(clazz, listener);
0705:            }
0706:
0707:            /**
0708:             * Unregister a listener for Cache events.
0709:             *
0710:             * @param listener  The object that currently listens to events.
0711:             * @since 2.4
0712:             */
0713:            public void removeCacheEventListener(CacheEventListener listener) {
0714:                // listenerList.remove(CacheEventListener.class, listener);
0715:                listenerList.remove(listener.getClass(), listener);
0716:            }
0717:
0718:            /**
0719:             * Get an entry from this cache or create one if it doesn't exist.
0720:             *
0721:             * @param key    The key of the cache entry
0722:             * @param policy Object that implements refresh policy logic
0723:             * @param origin The origin of request (optional)
0724:             * @return CacheEntry for the specified key.
0725:             */
0726:            protected CacheEntry getCacheEntry(String key,
0727:                    EntryRefreshPolicy policy, String origin) {
0728:                CacheEntry cacheEntry = null;
0729:
0730:                // Verify that the key is valid
0731:                if ((key == null) || (key.length() == 0)) {
0732:                    throw new IllegalArgumentException(
0733:                            "getCacheEntry called with an empty or null key");
0734:                }
0735:
0736:                cacheEntry = (CacheEntry) cacheMap.get(key);
0737:
0738:                // if the cache entry does not exist, create a new one
0739:                if (cacheEntry == null) {
0740:                    if (log.isDebugEnabled()) {
0741:                        log.debug("No cache entry exists for key='" + key
0742:                                + "', creating");
0743:                    }
0744:
0745:                    cacheEntry = new CacheEntry(key, policy);
0746:                }
0747:
0748:                return cacheEntry;
0749:            }
0750:
0751:            /**
0752:             * Indicates whether or not the cache entry is stale.
0753:             *
0754:             * @param cacheEntry     The cache entry to test the freshness of.
0755:             * @param refreshPeriod  The maximum allowable age of the entry, in seconds.
0756:             * @param cronExpiry     A cron expression specifying absolute date(s) and/or time(s)
0757:             * that the cache entry should expire at. If the cache entry was refreshed prior to
0758:             * the most recent match for the cron expression, the entry will be considered stale.
0759:             *
0760:             * @return <code>true</code> if the entry is stale, <code>false</code> otherwise.
0761:             */
0762:            protected boolean isStale(CacheEntry cacheEntry, int refreshPeriod,
0763:                    String cronExpiry) {
0764:                boolean result = cacheEntry.needsRefresh(refreshPeriod)
0765:                        || isFlushed(cacheEntry);
0766:
0767:                if ((!result) && (cronExpiry != null)
0768:                        && (cronExpiry.length() > 0)) {
0769:                    try {
0770:                        FastCronParser parser = new FastCronParser(cronExpiry);
0771:                        result = result
0772:                                || parser.hasMoreRecentMatch(cacheEntry
0773:                                        .getLastUpdate());
0774:                    } catch (ParseException e) {
0775:                        log.warn(e);
0776:                    }
0777:                }
0778:
0779:                return result;
0780:            }
0781:
0782:            /**
0783:             * Get the updating cache entry from the update map. If one is not found,
0784:             * create a new one (with state {@link EntryUpdateState#NOT_YET_UPDATING})
0785:             * and add it to the map.
0786:             *
0787:             * @param key The cache key for this entry
0788:             *
0789:             * @return the CacheEntry that was found (or added to) the updatingEntries
0790:             * map.
0791:             */
0792:            protected EntryUpdateState getUpdateState(String key) {
0793:                EntryUpdateState updateState;
0794:
0795:                synchronized (updateStates) {
0796:                    // Try to find the matching state object in the updating entry map.
0797:                    updateState = (EntryUpdateState) updateStates.get(key);
0798:
0799:                    if (updateState == null) {
0800:                        // It's not there so add it.
0801:                        updateState = new EntryUpdateState();
0802:                        updateStates.put(key, updateState);
0803:                    } else {
0804:                        //Otherwise indicate that we start using it to prevent its removal until all threads are done with it.
0805:                        updateState.incrementUsageCounter();
0806:                    }
0807:                }
0808:
0809:                return updateState;
0810:            }
0811:
0812:            /**
0813:             * releases the usage that was made of the specified EntryUpdateState. When this reaches zero, the entry is removed from the map. 
0814:             * @param state the state to release the usage of
0815:             * @param key the associated key.
0816:             */
0817:            protected void releaseUpdateState(EntryUpdateState state, String key) {
0818:                synchronized (updateStates) {
0819:                    int usageCounter = state.decrementUsageCounter();
0820:                    checkEntryStateUpdateUsage(key, state, usageCounter);
0821:                }
0822:            }
0823:
0824:            /**
0825:             * Completely clears the cache.
0826:             */
0827:            protected void clear() {
0828:                cacheMap.clear();
0829:            }
0830:
0831:            /**
0832:             * Removes the update state for the specified key and notifies any other
0833:             * threads that are waiting on this object. This is called automatically
0834:             * by the {@link #putInCache} method, so it is possible that no EntryUpdateState was hold
0835:             * when this method is called.
0836:             *
0837:             * @param key The cache key that is no longer being updated.
0838:             */
0839:            protected void completeUpdate(String key) {
0840:                EntryUpdateState state;
0841:
0842:                synchronized (updateStates) {
0843:                    state = (EntryUpdateState) updateStates.get(key);
0844:
0845:                    if (state != null) {
0846:                        synchronized (state) {
0847:                            int usageCounter = state.completeUpdate();
0848:                            state.notifyAll();
0849:
0850:                            checkEntryStateUpdateUsage(key, state, usageCounter);
0851:
0852:                        }
0853:                    } else {
0854:                        //If putInCache() was called directly (i.e. not as a result of a NeedRefreshException) then no EntryUpdateState would be found. 
0855:                    }
0856:                }
0857:            }
0858:
0859:            /**
0860:             * Completely removes a cache entry from the cache and its associated cache
0861:             * groups.
0862:             *
0863:             * @param key The key of the entry to remove.
0864:             */
0865:            public void removeEntry(String key) {
0866:                removeEntry(key, null);
0867:            }
0868:
0869:            /**
0870:             * Completely removes a cache entry from the cache and its associated cache
0871:             * groups.
0872:             *
0873:             * @param key    The key of the entry to remove.
0874:             * @param origin The origin of this remove request.
0875:             */
0876:            protected void removeEntry(String key, String origin) {
0877:                CacheEntry cacheEntry = (CacheEntry) cacheMap.get(key);
0878:                cacheMap.remove(key);
0879:
0880:                if (listenerList.getListenerCount() > 0) {
0881:                    CacheEntryEvent event = new CacheEntryEvent(this ,
0882:                            cacheEntry, origin);
0883:                    dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_REMOVED,
0884:                            event);
0885:                }
0886:            }
0887:
0888:            /**
0889:             * Dispatch a cache entry event to all registered listeners.
0890:             *
0891:             * @param eventType   The type of event (used to branch on the proper method)
0892:             * @param event       The event that was fired
0893:             */
0894:            private void dispatchCacheEntryEvent(CacheEntryEventType eventType,
0895:                    CacheEntryEvent event) {
0896:                // Guaranteed to return a non-null array
0897:                Object[] listeners = listenerList.getListenerList();
0898:
0899:                // Process the listeners last to first, notifying
0900:                // those that are interested in this event
0901:                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0902:                    if (listeners[i + 1] instanceof  CacheEntryEventListener) {
0903:                        CacheEntryEventListener listener = (CacheEntryEventListener) listeners[i + 1];
0904:                        if (eventType.equals(CacheEntryEventType.ENTRY_ADDED)) {
0905:                            listener.cacheEntryAdded(event);
0906:                        } else if (eventType
0907:                                .equals(CacheEntryEventType.ENTRY_UPDATED)) {
0908:                            listener.cacheEntryUpdated(event);
0909:                        } else if (eventType
0910:                                .equals(CacheEntryEventType.ENTRY_FLUSHED)) {
0911:                            listener.cacheEntryFlushed(event);
0912:                        } else if (eventType
0913:                                .equals(CacheEntryEventType.ENTRY_REMOVED)) {
0914:                            listener.cacheEntryRemoved(event);
0915:                        }
0916:                    }
0917:                }
0918:            }
0919:
0920:            /**
0921:             * Dispatch a cache group event to all registered listeners.
0922:             *
0923:             * @param eventType The type of event (this is used to branch to the correct method handler)
0924:             * @param group     The cache group that the event applies to
0925:             * @param origin      The origin of this event (optional)
0926:             */
0927:            private void dispatchCacheGroupEvent(CacheEntryEventType eventType,
0928:                    String group, String origin) {
0929:                CacheGroupEvent event = new CacheGroupEvent(this , group, origin);
0930:
0931:                // Guaranteed to return a non-null array
0932:                Object[] listeners = listenerList.getListenerList();
0933:
0934:                // Process the listeners last to first, notifying
0935:                // those that are interested in this event
0936:                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0937:                    if (listeners[i + 1] instanceof  CacheEntryEventListener) {
0938:                        CacheEntryEventListener listener = (CacheEntryEventListener) listeners[i + 1];
0939:                        if (eventType.equals(CacheEntryEventType.GROUP_FLUSHED)) {
0940:                            listener.cacheGroupFlushed(event);
0941:                        }
0942:                    }
0943:                }
0944:            }
0945:
0946:            /**
0947:             * Dispatch a cache map access event to all registered listeners.
0948:             *
0949:             * @param eventType     The type of event
0950:             * @param entry         The entry that was affected.
0951:             * @param origin        The origin of this event (optional)
0952:             */
0953:            private void dispatchCacheMapAccessEvent(
0954:                    CacheMapAccessEventType eventType, CacheEntry entry,
0955:                    String origin) {
0956:                CacheMapAccessEvent event = new CacheMapAccessEvent(eventType,
0957:                        entry, origin);
0958:
0959:                // Guaranteed to return a non-null array
0960:                Object[] listeners = listenerList.getListenerList();
0961:
0962:                // Process the listeners last to first, notifying
0963:                // those that are interested in this event
0964:                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0965:                    if (listeners[i + 1] instanceof  CacheMapAccessEventListener) {
0966:                        CacheMapAccessEventListener listener = (CacheMapAccessEventListener) listeners[i + 1];
0967:                        listener.accessed(event);
0968:                    }
0969:                }
0970:            }
0971:
0972:            /**
0973:             * Dispatch a cache pattern event to all registered listeners.
0974:             *
0975:             * @param eventType The type of event (this is used to branch to the correct method handler)
0976:             * @param pattern     The cache pattern that the event applies to
0977:             * @param origin      The origin of this event (optional)
0978:             */
0979:            private void dispatchCachePatternEvent(
0980:                    CacheEntryEventType eventType, String pattern, String origin) {
0981:                CachePatternEvent event = new CachePatternEvent(this , pattern,
0982:                        origin);
0983:
0984:                // Guaranteed to return a non-null array
0985:                Object[] listeners = listenerList.getListenerList();
0986:
0987:                // Process the listeners last to first, notifying
0988:                // those that are interested in this event
0989:                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0990:                    if (listeners[i + 1] instanceof  CacheEntryEventListener) {
0991:                        if (eventType
0992:                                .equals(CacheEntryEventType.PATTERN_FLUSHED)) {
0993:                            CacheEntryEventListener listener = (CacheEntryEventListener) listeners[i + 1];
0994:                            listener.cachePatternFlushed(event);
0995:                        }
0996:                    }
0997:                }
0998:            }
0999:
1000:            /**
1001:             * Dispatches a cache-wide event to all registered listeners.
1002:             *
1003:             * @param eventType The type of event (this is used to branch to the correct method handler)
1004:             * @param origin The origin of this event (optional)
1005:             */
1006:            private void dispatchCachewideEvent(CachewideEventType eventType,
1007:                    Date date, String origin) {
1008:                CachewideEvent event = new CachewideEvent(this , date, origin);
1009:
1010:                // Guaranteed to return a non-null array
1011:                Object[] listeners = listenerList.getListenerList();
1012:
1013:                // Process the listeners last to first, notifying
1014:                // those that are interested in this event
1015:                for (int i = listeners.length - 2; i >= 0; i -= 2) {
1016:                    if (listeners[i + 1] instanceof  CacheEntryEventListener) {
1017:                        if (eventType.equals(CachewideEventType.CACHE_FLUSHED)) {
1018:                            CacheEntryEventListener listener = (CacheEntryEventListener) listeners[i + 1];
1019:                            listener.cacheFlushed(event);
1020:                        }
1021:                    }
1022:                }
1023:            }
1024:
1025:            /**
1026:             * Flush a cache entry. On completion of the flush, a
1027:             * <tt>CacheEntryEventType.ENTRY_FLUSHED</tt> event is fired.
1028:             *
1029:             * @param entry The entry to flush
1030:             * @param origin The origin of this flush event (optional)
1031:             */
1032:            private void flushEntry(CacheEntry entry, String origin) {
1033:                String key = entry.getKey();
1034:
1035:                // Flush the object itself
1036:                entry.flush();
1037:
1038:                if (!entry.isNew()) {
1039:                    // Update the entry's state in the map
1040:                    cacheMap.put(key, entry);
1041:                }
1042:
1043:                // Trigger an ENTRY_FLUSHED event. [CACHE-107] Do this for all flushes.
1044:                if (listenerList.getListenerCount() > 0) {
1045:                    CacheEntryEvent event = new CacheEntryEvent(this , entry,
1046:                            origin);
1047:                    dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_FLUSHED,
1048:                            event);
1049:                }
1050:            }
1051:
1052:            /**
1053:             * @return the total number of cache entries held in this cache. 
1054:             */
1055:            public int getSize() {
1056:                synchronized (cacheMap) {
1057:                    return cacheMap.size();
1058:                }
1059:            }
1060:
1061:            /**
1062:             * Test support only: return the number of EntryUpdateState instances within the updateStates map. 
1063:             */
1064:            protected int getNbUpdateState() {
1065:                synchronized (updateStates) {
1066:                    return updateStates.size();
1067:                }
1068:            }
1069:
1070:            /**
1071:             * Test support only: return the number of entries currently in the cache map
1072:             * @deprecated use getSize() 
1073:             */
1074:            public int getNbEntries() {
1075:                synchronized (cacheMap) {
1076:                    return cacheMap.size();
1077:                }
1078:            }
1079:        }
www.java2java.com | Contact Us
Copyright 2010 - 2030 Java Source and Support. All rights reserved.
All other trademarks are property of their respective owners.