Relying on NeedsRefreshException

Stefano Santoro <[email protected]>
Newsgroups gmane.comp.java.open-symphony.os-cache
Message-ID <[email protected]>
Hi,

First thank you for creating such a great package. I was thrilled
when some one told me about OS Cache, and I have been, for the last
couple of months, a big advocate for it in my company.

One thing though has left me puzzled: the requirement to catch and
handle NeedsRefreshException, as described in the 
GeneralCacheAdministrator java doc. A cache miss is expected during
normal operations, and has nothing 'exceptional' about it. All the
java best practices indicate that Exception handling is an 'exceptional'
event, and it should not be part of the normal and expected execution 
path of a program.

I can envision, though, the use of a intermediary data structure to 
achieve the same results:

SampleCache       cache = // com.opensymphony.oscache.base.Cache extension
SampleCache.Probe probe = null;
String            content = null;

try {
    probe = cache.getProbe( cacheKey);
    if( ! probe.isFresh()) {
	// get the content to be cached
	content = getContent();
	cache.putInCache( cacheKey, content);
    }
    else {
	content = probe.getContent();
    }
    probe = null;
}
finally {
    if( probe != null) { 
	cache.cancelUpdate();
    }
}

Perhaps I have not read the documentation thoroughly enough, and I most
likely missing the point somewhere. Please let me know if there is a way
to use the GeneralCacheAdmimistrator without having to catch 
NeedsRefreshException.

I am enclosing my SampleCache implementation for your consideration:

Ciao
Stefano
SampleCache.java (text/plain, 3.7 KB)
import com.opensymphony.oscache.base.Cache;
import com.opensymphony.oscache.base.CacheEntry;
import com.opensymphony.oscache.base.EntryRefreshPolicy;
import com.opensymphony.oscache.base.EntryUpdateState;
import com.opensymphony.oscache.base.events.CacheMapAccessEvent;
import com.opensymphony.oscache.base.events.CacheMapAccessEventType;
import com.opensymphony.oscache.base.events.CacheMapAccessEventListener;
import com.opensymphony.oscache.base.events.CacheEntryEvent;

public class SampleCache extends Cache {

  private boolean blocking;

  public SampleCache(boolean useMemoryCaching, boolean unlimitedDiskCache) {
    super(useMemoryCaching, unlimitedDiskCache, false, null, 0);
  }

  public SampleCache
    (boolean useMemoryCaching, boolean unlimitedDiskCache, 
     boolean blocking, String algoClass, int capacity) 
  {
    super(useMemoryCaching,unlimitedDiskCache,blocking,algoClass,capacity);
    this.blocking = blocking;
  }

  public final static class Probe {
    private CacheMapAccessEventType state_;
    private Object                  content_;

    public Probe(  CacheMapAccessEventType aState, Object aContent) {
      state_   = aState;
      content_ = aContent;
    }

    public boolean isFresh() {
      return state_ == CacheMapAccessEventType.HIT;
    }

    public boolean isNew() {
      return state_ == CacheMapAccessEventType.MISS;
    }

    public Object getContent() throws IllegalStateException {
      return content_;
    }
  }

  public Object getProbe(String key) {
    return getProbe(key, CacheEntry.INDEFINITE_EXPIRY, null);
  }

  public Probe getProbe
    ( String key, int refreshPeriod, String cronExpiry) 
  {
    CacheEntry              cacheEntry      = getCacheEntry(key, null, null);
    CacheMapAccessEventType accessEventType = CacheMapAccessEventType.HIT;
    
    boolean reload = false;

    if( isStale(cacheEntry, refreshPeriod, cronExpiry)) {
      EntryUpdateState updateState = null;
      boolean          canUpdate   = false;

      while( updateState == null) {
	updateState = getUpdateState(key);
	synchronized( updateState) {

	  if( cacheEntry.isNew() || blocking) {

	    while( updateState.isUpdating()) {
	      try {
		updateState.wait();
	      }
	      catch( InterruptedException iex) {
		iex = null;
	      }
	    }
	    if( updateState.isComplete()) {
	      reload = true;
	    }
	    else if( updateState.isCancelled()) {
	      updateState = null;
	    }
	  }

	  if( updateState != null && updateState.isAwaitingUpdate()) {
	    updateState.startUpdate();
	  }
	}
      }
      accessEventType = CacheMapAccessEventType.STALE_HIT;

      if( reload) {
	CacheEntry reloadEntry = getCacheEntry(key, null, null);

	if( reloadEntry != null) { 
	  cacheEntry = reloadEntry;
	  accessEventType = CacheMapAccessEventType.HIT;
	}
      }

      if( cacheEntry.isNew()) {
	accessEventType = CacheMapAccessEventType.MISS;
      }
    }

    dispatchCacheMapAccessEvent(accessEventType, cacheEntry, null);

    return new Probe( accessEventType, cacheEntry.getContent());

  }

  private void dispatchCacheMapAccessEvent
    (CacheMapAccessEventType eventType, CacheEntry entry, String origin) 
  {
    CacheMapAccessEvent event = 
      new CacheMapAccessEvent (eventType, entry, origin);

    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();

    // Process the listeners last to first, notifying
    // those that are interested in this event
    for (int i = listeners.length - 2; i >= 0; i -= 2) {
      if (listeners[i] == CacheMapAccessEventListener.class) {
	((CacheMapAccessEventListener) listeners[i + 1]).accessed(event);
      }
    }
  }
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.