Overview

Standalone or Factory

Creating a usable cache is simple:

			ObjectCache cache = new ObjectCache();
		
At this point, you have complete control over the cache. Methods such as put(Object, Object), get(Object), etc. allow you to use the cache in any manner you want. Of course, just like this, the ObjectCache is little more than a HashMap. The first thing you might want to do is ask the cache to delegate to a factory if the object you are looking for is not in the cache. You can do this by passing an object that implements the ObjectFactory interface into the cache during construction:
			ObjectCache cache = new ObjectCache(new SomeFactory());
		
Once this is done, any call to get an object from the cache that would return null or the cache entry is invalid, will call the createObject() method of the specified factory. So, for example, if you have a Person object that you want cached, your Person class might look like:
			public class Person {
				private static ObjectCache cache = new ObjectCache(new PersonFactory());

				private String name;
				public Person(String name)
				{
					this.name = name;
				}

				public static Person findPerson(String name)
				{
					return cache.get(name);
				}
			}
		
The PersonFactory class would be:
			public class PersonFactory implements ObjectFactory {
				public Object createObjectFor(Object key, Object data)
				{
					return new Person((String) key);
				}
			}
		
When you make a call to cache.get() passing in a name that is not in the cache, then the cache will ask the PersonFactory to create the Person. Then, before returning the Person to the calling code, the cache prepares the entry and adds it to the cache.

Strategies

With or without an ObjectFactory, the cache is still a little more than a glorified HashMap without some way of expiring a cache entry, or hint that an entry should be re-loaded. This is the work of the CachingStrategy implementations. An ObjectCache may have 0 to many strategies associated with it. When an object is put into the cache, or a factory creates one, that object is wrapped in an internal CacheEntry and each strategy is asked to prepare the entry. It may do nothing, or it may set a property on the entry, or, it may even return a whole new CacheEntry, wrapping the old one. Each subsequent call to get the object from the cache will ask each strategy to verify the entry. If any strategy fails, the object is reloaded from the factory, or null is returned.

CachingStrategies are very simple and can be combined together to perform very sophisticated caching implementations. Prepackaged with ashkay are several common strategies:

NullCachingStrategy
Caches nothing. The size method will not work right in this strategy as the NullCachingStrategy actually just uses a SoftReferenceCachingStrategy but returns false on all verifies.
FullCachingStrategy
Always returns true from verify.
SoftReferenceCachingStrategy
For memory sensative situations, a SoftReferenceCachingStrategy wraps each object in a SoftReference allowing it to be garbage collected if need be,
LastModifiedCachingStrategy
Checks the last modified time on a resource. If it has been modified, it will fail verification.
TimeExpirationCachingStrategy
Expires an extry after a designated time.
How might you combine these. Let's say you are caching XSL Transforms. They are expensive to create so caching them makes a lot of sense. But, if you are reading them from a file, you would like to have the update when the file changes; And, you don't want them hanging around if they are not used much. You can create a factory that creates a transform from a filename that is a key. When you create the object cache, just add both strategies; SoftRefernce for memory and LastModified to take care of the file changing. Easy peezy.