public static synchronized Transformer newTransformer(String xsltFileName)
throws TransformerConfigurationException {
The parameter, an XSLT stylesheet filename, was chosen to facilitate
the "last accessed" feature. We use the
java.io.File class to determine when the file was
last modified, which allows the cache to automatically reload itself
as edits are made to the stylesheets. Had we used a system identifier
or InputStream instead of a filename, the
auto-reload feature could not have been implemented. Next, the
File object is created and its
lastModified flag is checked:
File xsltFile = new File(xsltFileName);
// determine when the file was last modified on disk
long xslLastModified = xsltFile.lastModified( );
The compiled stylesheet, represented by an instance of
MapEntry, is then retrieved from the
Map. If the entry is found, its timestamp is
compared against the current file's timestamp, thus allowing
auto-reload:
MapEntry entry = (MapEntry) cache.get(xsltFileName);
if (entry != null) {
// if the file has been modified more recently than the
// cached stylesheet, remove the entry reference
if (xslLastModified > entry.lastModified) {
entry = null;
}
}
Next, we create a new entry in the cache if the entry object
reference is still null. This is accomplished by
wrapping a StreamSource around the
File object, instantiating a
TransformerFactory instance, and using that
factory to create our Templates object. The
Templates object is then stored in the cache so it
can be reused by the next client of the cache:
// create a new entry in the cache if necessary
if (entry == null) {
Source xslSource = new StreamSource(xsltFile);
TransformerFactory transFact = TransformerFactory.newInstance( );
Templates templates = transFact.newTemplates(xslSource);
entry = new MapEntry(xslLastModified, templates);
cache.put(xsltFileName, entry);
}
return entry.templates.newTransformer( );
Returning a new Transformer is critical because,
although the Templates object is thread-safe, the
Transformer implementation is not. Each caller
gets its own copy of Transformer so multiple
clients do not collide with one another.
One potential improvement on this design could be to add a
lastAccessed timestamp to each
MapEntry object. Another thread could then execute
every couple of hours to flush map entries from memory if they have
not been accessed for a period of time. In most web applications,
this will not be an issue, but if you have a large number of pages
and some are seldom accessed, this could be a way to reduce the
memory usage of the cache.