Previous | Next
Stylesheet CompilationXSLT is a developing language, expressed using XML syntax. This is not for the benefit of the computer, but rather for human interpretation. Before the stylesheet can be processed, it must be converted into some internal machine-readable format. This process should sound familiar, because it is the same process used for every high-level developing language. You, the developer, work in terms of the high-level language, and an interpreter or compiler converts this language into some machine format that can be executed by the computer. Interpreters analyze source code and translate it into machine code with each execution. In this case of XSLT, this requires that the stylesheet be read into memory using an XML parser, translated into machine format, and then applied to your XML data. Performance is the obvious problem, particularly when you consider that stylesheets rarely change. Typically, the stylesheets are defined early on in the development process and remain static, while XML data is generated dynamically with each client request. A better approach is to parse the XSLT stylesheet into memory once, compile it to machine-format, and then preserve that machine representation in memory for repeated use. This is called stylesheet compilation and is no different in concept than the compilation of any developing language. Templates APIDifferent XSLT processors implement stylesheet compilation differently, so JAXP includes the public interface Templates { java.util.Properties getOutputProperties( ); javax.xml.transform.Transformer newTransformer( ) throws TransformerConfigurationException; } The The public Templates newTemplates(Source source) throws TransformerConfigurationException As in earlier examples, the Whether the stylesheet is actually compiled is up to the implementation, but a safe bet is that performance will continually improve over the next several years as these tools stabilize and vendors have time to apply optimizations. Figure 5-6 illustrates the relationship between Figure 5-6. Relationship between Templates and TransformerThread safety is an important issue in any Java application, particularly in a web context where many users share the same stylesheet. As Figure 5-6 illustrates, an instance of
A Stylesheet Cache XSLT transformations commonly occur on a shared web server with a large number of concurrent users, so it makes sense to use The code shown in Example 5-10 illustrates a custom XSLT stylesheet cache that automates the mundane tasks associated with creating Example 5-10. StylesheetCache.javapackage com.anonymous.javaxslt.util; import java.io.*; import java.util.*; import javax.xml.transform.*; import javax.xml.transform.stream.*; /** * A utility class that caches XSLT stylesheets in memory. * */ public class StylesheetCache { // map xslt file names to MapEntry instances // (MapEntry is defined below) private static Map cache = new HashMap( ); /** * Flush all cached stylesheets from memory, emptying the cache. */ public static synchronized void flushAll( ) { cache.clear( ); } /** * Flush a specific cached stylesheet from memory. * * @param xsltFileName the file name of the stylesheet to remove. */ public static synchronized void flush(String xsltFileName) { cache.remove(xsltFileName); } /** * Obtain a new Transformer instance for the specified XSLT file name. * A new entry will be added to the cache if this is the first request * for the specified file name. * * @param xsltFileName the file name of an XSLT stylesheet. * @return a transformation context for the given stylesheet. */ public static synchronized Transformer newTransformer(String xsltFileName) throws TransformerConfigurationException { File xsltFile = new File(xsltFileName); // determine when the file was last modified on disk long xslLastModified = xsltFile.lastModified( ); 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; } } // 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( ); } // prevent instantiation of this class private StylesheetCache( ) { } /** * This class represents a value in the cache Map. */ static class MapEntry { long lastModified; // when the file was modified Templates templates; MapEntry(long lastModified, Templates templates) { this.lastModified = lastModified; this.templates = templates; } } } Because this class is a singleton, it has a private constructor and uses only static methods. Furthermore, each method is declared as The heart of this class is the cache itself, which is implemented using private static Map cache = new HashMap( ); Although static class MapEntry { long lastModified; // when the file was modified Templates templates; MapEntry(long lastModified, Templates templates) { this.lastModified = lastModified; this.templates = templates; } } Removing entries from the cache is accomplished by one of two methods: public static synchronized void flushAll( ) { cache.clear( ); } public static synchronized void flush(String xsltFileName) { cache.remove(xsltFileName); } The first method merely removes everything from the The majority of interaction with this class occurs via the 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 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 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 // 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); } Finally, a brand new return entry.templates.newTransformer( ); Returning a new One potential improvement on this design could be to add a Another potential modification is to allow |