views:

73

answers:

4

hi,

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.

A: 

You could remove the entire logic from the current request and render the image with an asynchronous request (ajax) which will even allow you to have a 'loading...' icon and control timeouts and such

F.Aquino
good idea, but the complexity of the parameters for image rendering makes it difficult to de-couple the individual images. i've made the call that generation needs to be initialised at the one point, and must stick with it.
pstanton
A: 

When the client (browser) requests a web page, it doesn't actually get the images at that time. As it renders the page and finds tags, the browser makes separate requests for each image back to the server. That means you can send the HTML page before the images even exist.

So fire off your HTML response, sending image creation requests to your image service. As the browser parses the response and requests the images from the web server.

You can kick it up a notch by having yet another thread pool respond to the image requests by accepting requests in individual threads, responding when the image is ready. If you just generate the images as fast as you can and have your web server respond to image requests, your user will get 404 responses for images not generated yet.

dj_segfault
you are missing the point, but thanks.
pstanton
+1  A: 

Sounds like you need something like a futuretask

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html

Just launch a bunch of them and you can pull back the results when isDone is true or you can even block on waiting for them to complete by simply calling get().

stimms
have combined this with a Thread Pool
pstanton
far more responsive now, and since half of the images are ready by the time the page loads on the browser, this solution is way faster than the other two suggested.
pstanton
i'd double upvote if i could, but wyzard provided much more info. you set me on the right path though, thanks.
pstanton
+2  A: 

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.)

Wyzard
pretty much what i've got now.
pstanton