i’m creating a web page which contains a few dynamically generated images.
in my page request handling i create all of the images and store them in a memory cache until they are subsequently requested by the browser.
public class CachedImage
{
byte[] data;
Date created;
}
currently, my image cache is essentially a HashMap<Integer, CachedImage>.
the problem is that the image generation takes time, and i’d like to start rendering the page before all of the images have finished generating.
so i’d like to generate the images in a Thread Pool and when requested, return the data if it’s ready or wait until the data is ready and then return.
can anyone come up with a tidy model for this mechanism?
the images are small, and i’m happy with the approach of caching the whole image in memory for now.
While generating the page, choose a new unique identifier for each image that the page will display. This might be something simple like a number obtained by incrementing an AtomicInteger, or it might be something more sophisticated like a UUID to prevent users from guessing other users’ image URLs. Put these unique identifiers into the URLs that the client will use to retrieve the images.
Once you’ve chosen the identifier for an image, construct a Callable that will generate and return the image, and submit it to a ThreadPoolExecutor to be run asynchronously. This gives you back a Future which can be used to retrieve the result. Save the Future in a map, with the image’s identifier as the key.
Later, when the client requests the image, you can take the image identifier and look it up in the map to find the associated Future object. Calling
get()on the Future will return the image, waiting for the generator to finish if necessary. (If the requested identifier isn’t found in the map, return a 404 error.)To avoid filling up the server’s memory with old images, you’ll probably want to discard them after a few minutes. To do that, each time you create a task and store its Future into the map of available images, you can put a task into a DelayQueue that’ll remove the entry from the map after some suitable delay. Use a daemon thread to take items from this queue and act on them, in a loop.
It’d also be a good idea to call
cancel(true)on the Future when removing it from the map, in case the generator was still running for some reason. (Otherwise it would continue running even though the image will no longer be accessible anyway.)