/** * Put an object into the cache specifying both the key to use and the cache groups the object * belongs to. * * @param key Key of the object in the cache * @param groups The cache groups to add the object to * @param content The object to cache * @param policy Object that implements the refresh policy logic */ public void putInCache( String key, Object content, String[] groups, EntryRefreshPolicy policy, String origin) { CacheEntry cacheEntry = this.getCacheEntry(key, policy, origin); boolean isNewEntry = cacheEntry.isNew(); // [CACHE-118] If we have an existing entry, create a new CacheEntry so we can still access the // old one later if (!isNewEntry) { cacheEntry = new CacheEntry(key, policy); } cacheEntry.setContent(content); cacheEntry.setGroups(groups); cacheMap.put(key, cacheEntry); // Signal to any threads waiting on this update that it's now ready for them // in the cache! completeUpdate(key); if (listenerList.getListenerCount() > 0) { CacheEntryEvent event = new CacheEntryEvent(this, cacheEntry, origin); if (isNewEntry) { dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_ADDED, event); } else { dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_UPDATED, event); } } }
/** * Flush a cache entry. On completion of the flush, a <tt>CacheEntryEventType.ENTRY_FLUSHED</tt> * event is fired. * * @param entry The entry to flush * @param origin The origin of this flush event (optional) */ private void flushEntry(CacheEntry entry, String origin) { String key = entry.getKey(); // Flush the object itself entry.flush(); if (!entry.isNew()) { // Update the entry's state in the map cacheMap.put(key, entry); } // Trigger an ENTRY_FLUSHED event. [CACHE-107] Do this for all flushes. if (listenerList.getListenerCount() > 0) { CacheEntryEvent event = new CacheEntryEvent(this, entry, origin); dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_FLUSHED, event); } }
/** * Retrieve an object from the cache specifying its key. * * @param key Key of the object in the cache. * @param refreshPeriod How long before the object needs refresh. To allow the object to stay in * the cache indefinitely, supply a value of {@link CacheEntry#INDEFINITE_EXPIRY}. * @param cronExpiry A cron expression that specifies fixed date(s) and/or time(s) that this cache * entry should expire on. * @return The object from cache * @throws NeedsRefreshException Thrown when the object either doesn't exist, or exists but is * stale. When this exception occurs, the CacheEntry corresponding to the supplied key will be * locked and other threads requesting this entry will potentially be blocked until the caller * repopulates the cache. If the caller choses not to repopulate the cache, they <em>must</em> * instead call {@link #cancelUpdate(String)}. */ public Object getFromCache(String key, int refreshPeriod, String cronExpiry) throws NeedsRefreshException { CacheEntry cacheEntry = this.getCacheEntry(key, null, null); Object content = cacheEntry.getContent(); CacheMapAccessEventType accessEventType = CacheMapAccessEventType.HIT; boolean reload = false; // Check if this entry has expired or has not yet been added to the cache. If // so, we need to decide whether to block, serve stale content or throw a // NeedsRefreshException if (this.isStale(cacheEntry, refreshPeriod, cronExpiry)) { // Get access to the EntryUpdateState instance and increment the usage count during the // potential sleep EntryUpdateState updateState = getUpdateState(key); // System.out.println("Stale:" + updateState.state + EntryUpdateState.NOT_YET_UPDATING); try { synchronized (updateState) { if (updateState.isAwaitingUpdate() || updateState.isCancelled()) { // No one else is currently updating this entry - grab ownership updateState.startUpdate(); if (cacheEntry.isNew()) { accessEventType = CacheMapAccessEventType.MISS; } else { accessEventType = CacheMapAccessEventType.STALE_HIT; } } else if (updateState.isUpdating()) { // Another thread is already updating the cache. We block if this // is a new entry, or blocking mode is enabled. Either putInCache() // or cancelUpdate() can cause this thread to resume. System.out.println("Yes - it's updating..."); if (cacheEntry.isNew() || blocking) { do { try { System.out.println("updateState was in:" + cacheEntry.isNew() + ":" + blocking); updateState.wait(); } catch (InterruptedException e) { } } while (updateState.isUpdating()); if (updateState.isCancelled()) { // The updating thread cancelled the update, let this one have a go. // This increments the usage count for this EntryUpdateState instance updateState.startUpdate(); if (cacheEntry.isNew()) { accessEventType = CacheMapAccessEventType.MISS; } else { accessEventType = CacheMapAccessEventType.STALE_HIT; } } else if (updateState.isComplete()) { reload = true; } else { log.error("Invalid update state for cache entry " + key); } } } else { reload = true; } } } finally { // 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 // increased by one in startUpdate() releaseUpdateState(updateState, key); } } // If reload is true then another thread must have successfully rebuilt the cache entry if (reload) { cacheEntry = (CacheEntry) cacheMap.get(key); if (cacheEntry != null) { content = cacheEntry.getContent(); } else { log.error("Could not reload cache entry after waiting for it to be rebuilt"); } } dispatchCacheMapAccessEvent(accessEventType, cacheEntry, null); // If we didn't end up getting a hit then we need to throw a NRE if (accessEventType != CacheMapAccessEventType.HIT) { throw new NeedsRefreshException(content); } return content; }