to.etc.domui.caches.images
Class ImageCache

java.lang.Object
  extended by to.etc.domui.caches.images.ImageCache

public class ImageCache
extends java.lang.Object

This cache handles images and transformations of images.

Image and image transformation cache and handler.

Original image sources

Image originals can come from many sources: database tables, file system files etc. The source never needs to be "duplicated" (in long-term cache like the database) but can be obtained in some way. This code generalizes the retrieval process for an original image into the IImageRetriever interface using a factory pattern. The factory (IIMageRetriever) to use is defined by the retriever's name which is a small string. The image retriever, when asked, must deliver some basic information on the image and a stream source to accesss it's content.

The Image Retriever retrieves a specific image using a retriever-specific key string called the retriever key. This key string's value is only meaningful to the specific retriever it is meant for. The retriever decodes the string value into a primary key, filename or whatnot and uses that to access the actual image. The combination of an IImageRetriever and a retriever key uniquely identifies an original image. Internally this identity is maintained in ImageKey. For external purposes the retriever itself can be specified as a string too: the retriever's name. This combination of retriever name and retriever key can be used in URL's to access a given original image. This is the task of the CachedImagePart part.

Image transformations

Getting an original image is nice but it's often not really needed - we usually need to have some specific transformed version of the original, like:

This code allows you to retrieve an original image and add permutations to that image: operations to the image that somehow transform it in another version of it. Adding permutations must be done in order: for a paged set you must first add the "page select" permutation before you can add a "thumbnail" permutation. Each permutation has a "permutation key" which is a short string representation of the transformation done and it's arguments. By concatenating all these strings we get an "unique key" for that specific set of transformations done on that image. Combined with the ImageKey this means we can cache the result of a permutation of an image too.

Caching

We cache images for better performance for often-used images like thumbnail of the current set of properties that are available for rent. We also cache metadata on images (format, size) to allow code to properly generate whatever HTML is needed to access the image.

Since many transformations on an image are very expensive we cache the result of those transformations too. These transformations are cached in database tables if so defined. They are always cached on the file system.

Retrieving images

We distinguish between retrieving image specifications and the image data. The image specifications are things like it's mime type, the number of pages (if paged) and it's size. These are all the result of the identification phase of the original image or of an actual transformation process.

Multithreaded locking strategy

We use a multi-level lock using the standard mechanism. The fast lock is the ImageCache instance itself; it protects the registered factory list and the map from ImageKey to ImageRoot. It also handles LRU processing for the individual image instances.

The 2nd level lock is a lock on ImageRoot which is locked for any access to whatever permutation of that image. This second-level lock is a slow lock meaning that any time-consuming operation on any of the derivatives of the image will keep this lock closed.

Accessing image data

Access to metadata of the image can be done by just requesting it. The data is cached but will never change, and if the cache entry disappears while you are using that metadata nothing special happens - the data will be garbage collected as soon as you release your reference to it.

This is not the case for image data. This data is cached in memory as a set of buffers, an optional extra file and data related to all this. While someone is using this data it cannot be released (the file cannot be deleted). To prevent this image data has a use count. This use count is at least 1 as long as the data is accessible in the cache and gets incremented for every reference to the data that is returned to the user. The user must release this data explicitly with a call to close which decreases the use count. If the use count becomes zero then the resources for image data will be released.

Locking path analysis

Get a new original image

Image retrieval process

Author:
Frits Jalvingh Created on Oct 2, 2008

Method Summary
 void addRetriever(IImageRetriever r)
          Add a new image factory.
 ImageKey createImageKey(java.lang.String retrieverkey, java.lang.String instancekey)
           
 IImageRetriever findRetriever(java.lang.String key)
           
 FullImage getFullImage(ImageKey k, java.util.List<IImageConversionSpecifier> convlist)
          Get full image data: both the data source AND it's info.
 IImageStreamSource getImageData(ImageKey k, java.util.List<IImageConversionSpecifier> convlist)
           
 ImageInfo getImageInfo(ImageKey k, java.util.List<IImageConversionSpecifier> convlist)
           
static ImageCache getInstance()
          Get the singleton image cache.
 IImageStreamSource getOriginalData(ImageKey k)
          Return a data reference to the original image's data.
 ImageInfo getOriginalInfo(ImageKey k)
          Return a data reference to the original image's info.
static void initialize(long maxsize, long maxfilesize, java.io.File cacheDir)
           
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

getInstance

public static ImageCache getInstance()
Get the singleton image cache.

Returns:

initialize

public static void initialize(long maxsize,
                              long maxfilesize,
                              java.io.File cacheDir)
                       throws java.lang.Exception
Throws:
java.lang.Exception

addRetriever

public void addRetriever(IImageRetriever r)
Add a new image factory.

Parameters:
r -

findRetriever

public IImageRetriever findRetriever(java.lang.String key)

createImageKey

public ImageKey createImageKey(java.lang.String retrieverkey,
                               java.lang.String instancekey)

getOriginalData

public IImageStreamSource getOriginalData(ImageKey k)
                                   throws java.lang.Exception
Return a data reference to the original image's data.

Parameters:
k -
Returns:
Throws:
java.lang.Exception

getOriginalInfo

public ImageInfo getOriginalInfo(ImageKey k)
                          throws java.lang.Exception
Return a data reference to the original image's info.

Parameters:
k -
Returns:
Throws:
java.lang.Exception

getImageData

public IImageStreamSource getImageData(ImageKey k,
                                       java.util.List<IImageConversionSpecifier> convlist)
                                throws java.lang.Exception
Parameters:
k -
convlist -
Returns:
Throws:
java.lang.Exception

getImageInfo

public ImageInfo getImageInfo(ImageKey k,
                              java.util.List<IImageConversionSpecifier> convlist)
                       throws java.lang.Exception
Throws:
java.lang.Exception

getFullImage

public FullImage getFullImage(ImageKey k,
                              java.util.List<IImageConversionSpecifier> convlist)
                       throws java.lang.Exception
Get full image data: both the data source AND it's info.

Parameters:
k -
convlist -
Returns:
Throws:
java.lang.Exception