Extensible Stylesheet Language Transformations (XSLT) is a Turing complete programming language. That means that given enough memory, it can calculate anything a program written in any other programming language can calculate. However, this theoretical ability is often impractical. There are several cases where you may need to write code in a more traditional language rather than XSLT:
- External I/O -- For instance, files, databases, or network connections. XSLT has very limited ability to read or write these things.
- External devices -- For instance, Universal Serial Bus (USB) ports or the system clock.
- Advanced math -- XSLT can perform basic arithmetic easily enough, but it doesn't support trigonometry, exponential functions, logarithms, or other more advanced mathematical operators and functions. Although you can implement all of these using the basic operations XSLT does support, such a program would be both unwieldy and slow. Using a language that is designed for such operations dramatically improves both performance and legibility.
This article shows you how to link Java™ classes to XSLT to perform these sorts of operations. The means by which XSLT invokes Java classes varies from one XSLT processor to the next. This article focuses on the Apache Foundation's popular Xalan XSLT processor.
The easiest Java method to invoke from Xalan is a simple static method. For example, suppose you have a table of angles like that shown in Listing 1.
Listing 1. An XML document containing angles
<angles units="degrees"> <data>30<data> <data>45<data> <data>115<data> <angles> |
Now, suppose you want to do something as simple as write out the sines of those angles. XSLT doesn't have a sine function. You could code a Taylor series for the sine function (see Resources for more about the Taylor series), but it's much easier to call the Java method Math.sin(). Calling a preexisting static Java method from XSLT with Xalan takes only three steps:
- Map a namespace prefix such as
mathto a Uniform Resource Identifier (URI) of the form xalan://fully.package.qualified.ClassName -- for example, xalan://java.lang.Math. - List this prefix in the
extension-element-prefixesattribute of the rootxsl:stylesheetelement. - Invoke the function using the form
prefix:methodName()-- for example,math:sin(.)
Listing 2 demonstrates this process.
Listing 2. A Xalan XSLT stylesheet that finds the sines of the angles in Listing 1
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="xalan://java.lang.Math"
extension-element-prefixes="math"
>
<xsl:template match="data" >
<The Math.sin() function expects input in radians
so you have to convert from degrees before calling it. -->
<xsl:value-of select="math:sin(3.1415292 * number(.) div 180.0)" />
<xsl:template>
</xsl:stylesheet> |
The same prefix can reference several different static methods in the same class. For example, Listing 3 shows a stylesheet that generates sines, cosines, and tangents for the angles.
Listing 3. A Xalan XSLT stylesheet that finds the sines, cosines, and tangents of the angles in Listing 1
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="xalan://java.lang.Math"
extension-element-prefixes="math"
>
<xsl:template match="angles" >
<table>
<th><td>Angle<td><td>Sine</td><td>Cosine<td><td>Tangent</td></th>
<xsl:apply-templates select="data"/>
<table>
<xsl:template>
<xsl:template match="data" >
<tr>
<xsl:variable name="radians" select="3.1415292 * number(.) div 180.0"/>
<td><xsl:value-of select="." /><td>
<td><xsl:value-of select="math:sin($radians)" /><td>
<td><xsl:value-of select="math:cos($radians)" /><td>
<td><xsl:value-of select="math:tan($radians)" /></td>
</tr>
<xsl:template>
<xsl:stylesheet>
|
To invoke static methods in different classes,
map each fully qualified class name to a different prefix. Then, list each prefix in the
extension-element-prefixes attribute, separated by whitespace.
This technique is by no means limited to methods defined in the core Java packages. You can invoke static methods in your own classes exactly the same way; these methods can do anything a Java static method can do. Just put the class somewhere in the classpath you use when running Xalan.
Before you begin writing XSLT extension functions, you need to understand one critical difference between the Java programming language and XSLT. XSLT is a functional language. Among other things, this means that it has no global variables, shares no state between function invocations, and causes no side effects. These characteristics enable compilers and interpreters to perform certain optimizations they couldn't otherwise perform. For instance, because the output of a function is completely determined by its input and its code, you don't need to evaluate a function more than once with the same argument. If foo(2) returns 7 the first time you call it, it will return 7 the second time you call it, and the third, and the thirty-third. Therefore intermediate results can be cached and reused, rather than recalculated.
The Java language is imperative rather than functional: It stores state outside of functions, and functions can have side effects. In Java code, foo(2) may return a different value every time you invoke it.
Mixing nonfunctional Java code into a functional language like XSLT runs the risk of violating the XSLT processor's assumptions. For instance, the processor might not recalculate a value when it needs to. It might invoke methods in a different order than you expect. It might even invoke them more or fewer times than you expect. Then again, it might do exactly what you expect. However, there's no promise that it will do so, and even if the extension function works today, it might fail in the next point release when the optimization scheme changes a little.
To safely call a Java method from XSLT, make the method as functional as possible. Methods you call shouldn't depend on the mutable state of any object and should return fully reproducible and predictable results. Static methods that merely accept an argument or two, perform a calculation, and return a value based on nothing but those arguments are ideal. For example, you can safely call most of the methods in the java.lang.Math class, such as Math.sin() and Math.exp() from XSLT. The notable exception in this class is Math.random(), which intentionally returns a different value every time you call it.
Of course, one reason you write extension functions in Java code is precisely to add features like random numbers, dates, times, and other content that XSLT doesn't provide. It's possible to invoke these methods from XSLT. However, if you do so, be aware that these methods might not be called when or as many times as you expect.
Be especially careful to avoid depending on side effects. A method that does something that isn't expressed in its return value -- for example, writing a file -- is dangerous. If you do this, design the method to be idempotent: That is, make sure it does exactly the same thing whether it's invoked once or a dozen times. Also be sure the method returns a value that the stylesheet will use. Otherwise, Xalan can optimize away the invocation of the extension function and not call it even once.
Instance methods, almost by definition, involve object state. More often than not, they involve mutable object state. Thus they're rarely the first choice when writing a new extension function. Most extension functions are better written as static methods. However, sometimes it's convenient to invoke a preexisting instance function directly from XSLT.
Calling an instance method is similar to calling a static method: You map the fully qualified class name to the namespace prefix and then invoke the method using that prefix to identify the class and the local name to identify the method. However, instance methods need a third datum: the particular instance of the class on which to invoke the method. To get this, you must first create an object of the class and store it in a variable. To do so, you invoke the prefix:new() function. For example, the fragment in Listing 4 creates a java.awt.Color object and stores it in the XSLT variable named blue.
Listing 4. Retrieving a Color object from XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:color="xalan://java.awt.Color"
extension-element-prefixes="color"
>
<xsl:template match="/" >
<xsl:variable name="blue" select="color:new(20, 10, 160)"/>
<... -->
<xsl:template>
</xsl:stylesheet> |
Then, when invoking a method on this Color object, you pass the variable reference $blue as the first argument. For example, Listing 5 first creates a brighter blue by invoking the brighter() method and then prints its red, green, and blue components.
Listing 5. Invoking an instance method on a Color object
<xsl:template match="/" >
<xsl:variable name="blue"
select="color:new(20 div 256, 10 div 256, 160 div 256)"/>
<xsl:variable name="brighterblue" select="color:brighter($blue)"/>
<red><xsl:value-of select="color:getRed($brighterblue)"/></red>
<green><xsl:value-of select="color:getGreen($brighterblue)"/></green>
<blue><xsl:value-of select="color:getBlue($brighterblue)"/><blue>
</xsl:template>
|
If the class has a no-args constructor, and you want to use that constructor and then immediately invoke a single instance method on it, there's a shortcut. Just call prefix:methodName as you would with a static method. You don't need to explicitly create an object and store it in a variable first.
XSLT uses the XPath data model with four basic types: number (a Java double), string, boolean, and node-set. (It also adds a fifth result-tree fragment type, but that rarely comes into play in simple extension functions.) When Xalan passes arguments to Java functions and converts return values from Java functions, it converts numbers to doubles, strings to strings, and booleans to booleans. It also converts these XPath types to related types if necessary to match a method signature.
For instance, when faced with foo(2), Xalan prefers to call a method named foo that takes a double as an argument. However, if no such method exists, it calls foo(java.lang.Double), foo(float), foo(long), or foo(int) instead (in that order of preference). If those all fail, it looks for foo(short), foo(char), and foo(byte). If none of those are found, it even tries converting the number to a string and calling foo(String). As a last resort, it looks for foo(Object).
Normally, you don't have to worry much about this process -- the right thing happens by default. The only common problem is when a class has both foo(double) and foo(int) variants, and you expect the int version to be invoked. Remember, Xalan always prefers doubles, and it uses the int-accepting method only if it can't find a wider type. For example, did you wonder about this instruction from Listing 5?
<xsl:variable name="blue"
select="color:new(20 div 256, 10 div 256, 160 div 256)"/> |
Perhaps it occurred to you that it would be simpler to pass in the int codes like this:
<xsl:variable name="blue"
select="color:new(20, 10, 160)"/> |
The problem is that although 20, 10, and 160 are all int literals in the Java language, they're all doubles in XSLT. This instruction doesn't invoke Color(int, int, int). Rather, it invokes Color(float, float, float), which expects to receive values between 0.0 and 1.0, rather than 0 and 255. Before invoking this method, you have to rescale the color values into that range.
Be cautious about method overloading based on argument type when you write extension functions. It's simpler not to overload methods at all.
Extension functions that operate on node-sets are trickier to write than ones that operate on simple values, but not excessively so.
Xalan converts node-sets to Document Object Model (DOM) NodeIterator, NodeList, and Node objects, in that order of preference. If no function matches, it tries String and finally Object.
Other object types can't be handled easily within XSLT. You can return these types from a Java method and store them in a variable. However, the only thing you can do with such a Java object is pass it back to or use it to invoke another extension function. Try to design your extension functions so that they use only doubles, strings, booleans, and node-sets as both arguments and return values.
If you want to call Java methods that operate on other types, try writing at least one additional adapter function in the Java program. This adapter method can take the XPath types, convert them to the Java types, and then invoke the method you really want to call. Then, when that method returns, the adapter method can convert the return value to one of the four XPath types and return that. It's much easier to manage the conversions in Java code than in XSLT.
XSLT has no exception handling of any kind. If your extension function throws an exception, Xalan shuts down. For example, Listing 6 shows what happened with an earlier, buggier version of Listing 5.
Listing 6. When an extension function throws an exception
$ java org.apache.xalan.xslt.Process -IN http://www.cafeaulait.org/ -XSL colors.xsl ERROR: 'Color parameter outside of expected range: Red Green Blue' (Location of error unknown)XSLT Error (javax.xml.transform.TransformerException): java.lang.IllegalArgumentException: Color parameter outside of expected range: Red Green Blue |
Consequently, you need to implement all your exception-handling logic within the Java methods. Here the exception was a result of a bug in my code, which was easy to fix. If you invoke a preexisting function that can throw a checked exception, you should write a wrapper function that catches and handles that exception; then, invoke the wrapper method from XSLT instead.
XSLT is a wonderfully efficient language for transforming documents, but it's not always the best language for more traditional tasks like integrating differential equations or talking to databases. Fortunately, you can code these tasks in the Java language and then invoke them from your XSLT stylesheets using Xalan.
Learn
- The Apache Xalan Web sites: Get the official documentation for Xalan extension functions.
- XML in a Nutshell (Elliotte Rusty Harold and W. Scott Means, O'Reilly, 2005): Read this complete reference and brief tutorial on XSLT.
- XSLT: Programmer's Reference (Michael Kay, Wrox, 2001): In the preeminent book on XSLT, find recent changes in the XSLT specification and developments in XSLT parsers plus a new chapter on writing extension functions.
- Taylor series: Visit Wikipedia and learn how to calculate sines using the basic arithmetic operations XSLT supports.
- The IBM developerWorks Java technology
zone: Explore hundreds of articles and tutorials about every aspect of Java programming.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
Get products and technologies
- Xalan 2: Download the XSLT engine is discussed in this article.
- IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
Discuss
- XML zone discussion forums: Participate in any of several XML-centered forums.
- developerWorks blogs: Get involved in the developerWorks community.

Elliotte Rusty Harold is originally from New Orleans, to which he returns periodically in search of a decent bowl of gumbo. However, he resides in the Prospect Heights neighborhood of Brooklyn with his wife, Beth, and cats Charm (named after the quark) and Marjorie (named after his mother-in-law). He's an adjunct professor of computer science at Polytechnic University, where he teaches Java and object-oriented programming. His Cafe au Lait Web site has become one of the most popular independent Java sites on the Internet, and his spin-off site, Cafe con Leche, has become one of the most popular XML sites. His most recent book is Java I/O, 2nd edition. He's currently working on the XOM API for processing XML, the Jaxen XPath engine, and the Jester test coverage tool. He'll speak about XML at XML 2006 in Boston in December. You can reach Elliotte at elharo@metalab.unc.edu.
Comments (Undergoing maintenance)





