Java Caches
A project I'm currently working on, called ICFS (Internal Catalogue Filing System), has really stretched and expanded my knowledge of coding in Java. This is the second best thing that a project can do for you1, and I'm glad for all the things I've learnt. When I've got it in some form of usable form, it'll be going open source, which should be interesting as a number of friends will be affected by its deployment to our college Science Fiction and Fantasy library2. There have lots of interesting issues in getting the project to even make sense, let alone work3.
The basic objective is to produce an API for the effective maintenance and control of a databse of all 13000 items within the library, noting the items authors, series, type, location, whether it's been damaged or lost, and who it has been on loan to. One of the things I've been trying to do is reduce the amount of time it spends talking to the server. This was easily achieved in the writing side by requiring a store() call to store the data. The application can then call this whenever it is done dealing with the object. This will be extended to include a 'dirty' bit-flag, which determines whether the item has been updated since the last time store was called - allowing unnecessary requests to be detected
On the reading side of things, I've attempted to save time by implementing a cache. This has been...an interesting experience. Because of the design of the API, one client can be connected to a number of remote servers4, which may be in no way related to any of the others. Therefore, each connection must have it's own cache.
Annoyingly, as I only spotted the needs for multiple connections during alpha testing5, I had originally implemented the caches within the classes, so that they could have lower privileges and still function as required. Needless to say that this has left me with a barely workable system that will require quite a lot of re-writing over the coming days.
This aside, writing an object cache in Java is a surprisingly complex task. I believe I've finally figured out how to do it, and it really isn't pretty. As I need to maintain the state of an object across a Connection, I have to be aware of references to it. As far as I can tell, there is no way in Java to forcibly destroy and object, only to destroy your reference to it (and, if it's the last reference, wait for the garbage collector to come a free the memory). Moreover, I need to implement my own cache clean up to stop memory useage building p, as any object in the cache will never be freed by the garbage collector.
My current solution to this can be roughly described as follows (pending me writing a large amount of code to implement some of the classes described
abstract class AbstractCacheableObject {
protected boolean isValid;
protected boolean isDirty;
protected int id;
abstract void store();
int getId() {
return id;
}
}
class Cache {
protected KVQueue, Integer>, AbstractCacheableObject> c; // Some queue object
// It store a queue of Key-Value pairs, and implements conatins(), get(), etc. as per Map etc.
// Put, however, returns the data pushed off the end of the queue (null if none)
// This class, is obviously, made up. I'll spend a while looking through Java libraries to find something similar...
// The combination of Class and Id describes exactly what object we are looking for.
boolean contains(Class cl, Integer i) {
return c.contains(cl, i);
}
AbstractCacheableObject get(Class cl, Integer i) {
if (!(c.conatins(cl, i)) reutrn null;
AbstractCacheableObject o = c.get(cl, i);
// Move to the top
c.remove(cl, i);
c.add(cl, i, o);
return o;
}
void put(Class cl, Integer i, AbstractCacheableObject o) {
if (c.contains(cl, i)) {
c.get(cl, i).isValid = false;
c.remove(cl, i);
}
o = c.put(cl, i, o);
if (o == null) return;
if (o.isValid)
if (o.isDirty)
o.store();
}
}
Somehow, I feel this is not the ideal solution. There's still a lot of fudge factor, but I may just have to live with that if I want to keep queries down to a minimum. If only there was a way to change the this pointer :P
- The best being get you loads of money. Possibly. The exact ordering depends on your train of thought, and weather you can currently afford food.
- ICSF, of which I am (unfortunately) currently web editor. Erm...yeah. I've been meaning to change the website to look less 1995.
- As of writing, it still doesn't. Lucky I still have some spare time :)
- This is to allow a static JSP servlet to be able to find the correct connection data. As the logged in user if stored in the icfs.Connection object, each session must have a unique Connection. However, these Connections may share a logical link to the database.
- This was rather embarrassing, to say the least. Although I did plenty of research on Java APIs, I forgot entirely to consider the implementation of JSP servlets, and consider the implication of static database connections within the API. Fail