Class Loading and OSGi

Saxon 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 Saxon version is on the classpath. This applies both across versions (for example Saxon 10 and Saxon 9.9) and across editions (say Saxon-EE and Saxon-HE). Note that although there are many classes (such as net.sf.saxon.Configuration) which are present in all three Saxon 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 class path.

Saxon uses dynamic loading to access resources in a number of situations, for example:

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:

  1. Check if the class is present in a list of known, pre-registered classes, and if so, return the known class.
  2. Use any ClassLoader that has been explicitly registered using DynamicLoader.setClassLoader().
  3. Use the context class loader for the current thread.
  4. 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 Feature.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.