I decided to write my own blogging software. Partly, because I want to play with Atom format, and experiment with extension elements. If Atom is a decent format, then it makes sense to make it the definitive representation of my blog, rather than rely on some poorly thought out database schema. I decided to make the tool an offline tool, because diagnosing problems with my ISP’s oddly configured CGI platform over an FTP connection isn’t my idea of a fun personal project.
My software uses Atom Entry Documents, as the source format for entries. I can edit them easily enough in Emacs using nxml-mode, and it gives me complete control over how the Atom for the feed will be generated. The entry documents are combined with a feed skeleton, and a bunch of HTML templates to make the feed.
The process of generating the feed includes several subtasks. There is the parsing of the files to DOMs, replacing variables, running XSLT, creating category listings, creating index pages, summary pages, entry pages. My initial idea was to have a bunch of Unix-style scripts that did simple tasks, and together they’d work together to make the publication. I suppose I could then have used Ant to execute the tasks. This would kind of work, but I didn’t want to parse the XML to make the Atom feed, then again for category listings. So I had the idea of stealing ideas from JavaSpaces, and having a system of subtasks, with declared dependencies, that used a shared blackboard space to make intermediate results available to each other.
A type-safe(ish), late-bound, blackboard
I thought about using a shared RDF graph, or a Map to store intermediate results. That would have worked, but RDF isn’t great at handling binaries, and all of that casting, is a bit of a pain. So, I came up with an interesting idea using Dynamic Proxies:
Each task is represented by a Task singleton which has a getStateInterfaces() method that returns a list of state interfaces as a Class[] array. A state interface is an interface containing JavaBeans-style get/set methods. It also supports indexed get/set methods (where the index can be an int, any primitive type, or any class).
The driver class calls getStateInterfaces() on each Task, takes the combined set of state interfaces. With these it constructs a Dynamic Proxy that implements all of the interfaces, where the implementation is a generic implementation that implements storage for the get/set methods using a Map.
This means that the driver class can pass the state object to the Task’s initialisation method, the Task can hold a field for each of the State interfaces that it requires, and initialise these fields by casting the object to each of those interfaces. The close locality of the init() method that does the casting, and the getStateInterfaces() method on the same class that decides what interfaces the object is guaranteed to implement, make the solution as good as typesafe. The task’s execute() method can then read and write intermediate results to and from the type-safe state interfaces.
Task dependencies
Tasks also have a getDependencies() method. This method also returns a Class[] array. The driver examines each top-level Task, and uses this to decide what order to execute the tasks in.
Extending Ant
Unlike most Java Abstract Factory Framework horrors, my publishing tool doesn’t declare dependencies in an XML file. They’re all declared directly in the code. I don’t think that this is such a bad idea. There seems to be this idea in the Java world, that recompiling code when a configuration change is needed is unacceptable; but I think it is a reasonable strategy for this problem.
Still, it might be interesting if this pattern of dependencies, and a shared blackboard for intermediate Java objects could be implemented as an extension of Ant. I have no idea whether it could or not, but implementing the Task’s as Ant Tasks, would make the system a bit more reusable.