Class loading and OSGi
SaxonJ runs in a wide variety of Java environments without problems, but problems can occasionally arise because of the specialized requirements of particular platforms. This is particularly true of environments that have their own way of doing Java class loading (examples are Eclipse and Apache Camel).
As a general rule, arbitrary failures can arise if more than one SaxonJ version is on the classpath.
This applies both across versions (for example SaxonJ 10 and SaxonJ 9.9) and across editions (say SaxonJ-EE and SaxonJ-HE).
Note that although there are many classes (such as net.sf.saxon.Configuration
) which are present in all
three SaxonJ editions (HE, PE, EE) the actual content of the class may vary slightly between editions, which is why
mixing classes loaded from different editions is generally fatal.
JAXP provides the ability to load parsers, XSLT transformation engines, XPath processors, and schema processors by searching the classpath for a JAR file that declares itself appropriately as a service provider. The intent of this mechanism is that applications can switch from one processor to another simply by changing what JAR files are on the classpath. In Saxonica's experience this mechanism can often cause problems, because the level of compatibility between different providers is not good enough. If an XSLT stylesheet has been tested under Xalan, for example, there are many reasons why it might not work under Saxon (or vice versa): the products implement different versions of the W3C specifications, many features of the W3C specifications have been left implementation-defined (for example, vendor extension functions), and even at the API level, products are in many cases allowed to interpret the specifications in their own way. Therefore Saxonica recommends that applications should explicitly load a software package that has been tested with the application and is known to work, rather than allowing a different provider to be substituted simply by changing the classpath.
SaxonJ uses dynamic loading to access resources in a number of situations, for example:
- Locating the license file for SaxonJ-PE and SaxonJ-EE
- Under Saxon-EE, loading bytecode that has been dynamically generated
- Loading user-supplied extension functions
- Loading copies of commonly-used W3C schemas and DTDs (issued within the Saxon JAR file in
folder
net/sf/saxon/data
) - Invoking "plugins" of various kinds, such as localization code, collations, URI resolvers, non-standard XML parsers
Generally all such dynamic loading is channeled via a DynamicLoader
object obtained as a property of the
Configuration
. Applications with specialist requirements can either customize Saxon's DynamicLoader
(for example by calling configuration.getDymanicLoader().setClassLoader()
), or they can replace Saxon's
DynamicLoader
with one of their own.
By default, Saxon's DynamicLoader
uses the following strategy to load a class, in order:
- Check if the class is present in a list of known, pre-registered classes, and if so, return the known class.
- Use any
ClassLoader
that has been explicitly registered usingDynamicLoader.setClassLoader()
. - Use the context class loader for the current thread.
- Use
Class.forName()
.
This strategy sometimes fails in environments that use OSGi (for example Apache Camel). Saxon does not explicitly support OSGi.
In some cases users have reported
that a sufficient workaround is to call configuration.getDynamicLoader().setClassLoader(Configuration.class.getClassLoader());
.
The area that tends to be most problematic in complex environments is loading of Saxon-generated bytecode.
The simplest workaround here is to switch off bytecode generation. This can be done using the configuration option
GENERATE_BYTE_CODE, or by an appropriate call on XsltCompiler.getUnderlyingCompilerInfo().setOptimizerOptions()
.
The performance benefit of bytecode generation is typically in the range 10%-40%, so in many cases the performance hit will not be
noticeable.
See also Troubleshooting license file problems.