Java/Maven - Saxon without SeviceLoader override - java

We are building a common component that is a dependency for multiple other projects.
Our project does some XSLT transformations and we need to use the Saxon engine.
We have full control over the specific XSLT transformation that must use Saxon, but no control over the classpath of the applications that are dependent on us, and we don't want to force them to use Saxon for other XML work.
We can manually invoke the Saxon library directly when doing our transformations using the API provided by those factories.
The problem is that Saxon uses the ServiceLoader pattern to inject itself as the TransformerFactory implementation using this file in the jar:
[saxon.jar]/META-INF/services/javax.xml.transform.TransformerFactory
This means that applications that use us as a dependency might end up using Saxon instead of their existing XML libraries. Asking those applications to change their code to invoke their specific implementation is not an option.
Is there any way we can 'override' the Saxon library to remove the ServiceLoader implementation? Either using Maven, Java or some other process?

Unfortunately it's all too common to find yourself using libraries that have been written to use the JAXP pluggability mechanism to pick up whatever XSLT processor is on the classpath, but which will actually only work if that processor happens to be Xalan.
For the XPath scenario this problem was so acute that the Saxon META-INF no longer declares itself as an XPath service provider (although it still implements all the JAXP interfaces). But for XSLT that solution wouldn't be acceptable.
I would think that for most situations, setting the Java system property javax.xml.transform.TransformerFactory to the relevant classname for Xalan should solve the problem.

Answering this for any future developers with the same issue.
We were not able to find a way to solve this issue. We considered writing a Maven plugin to remove the META-INF/services/ file from the JAR but ultimately decided this was not an appropriate solution.
We are now in the same position we started - dependent applications end up with Saxon as a registered provider and it might override their existing configuration.
For those applications that must use a specific XSLT processer we have asked them to set the system property, e.g.
javax.xml.transform.TransformerFactory=org.apache.xalan.processor.TransformerFactoryImpl

Related

Woodstox on Android

I've previously written a library in Java using the native Java 1.6 Stax parser heavily. However, I now want use this library for Android, meaning that this parser is not supported. I'd like to use Woodstox as it implements the Stax 1.0 api and I wouldn't have to rewrite any of my current code, just sub in the dependency.
Android does not have the stax 1 api, so I realize I have to add it. Right now, I've added the woodstox-core-asl-4.2.0.jar, stax-api-1.0-2.jar, and the stax2-api-3.1.3.jar to the classpath. Everything compiles fine, but when I actually try to run an Android application which depends on this library, I get runtime errors indicating it isn't using Woodstox as the implementation for the stax 1 api.
Is there something I'm misunderstanding or doing incorrectly? Am I missing a jar? I've read the Woodstox help page thoroughly but can't find anything else I'm missing.
EDIT: I'm starting to wonder if it's actually possible to use Woodstox on Android. The issue is with the dependency on the stax api. After some research I discovered that the Dalvik VM appears to not be ok with those packages being in the javax.* namespace.
How are you passing XMLInputFactory / XMLOutputFactory instance? Usually it is better to directly construct instances (of WstxInputFactory, WstxOutputFactory), since there is no real benefit from callign XMLInputFactory.newInstance(): at most it adds overhead (much slower, scans classpath).
You are not missing any jars: core and stax2-api are needed always; and if the platform does not include Stax API jar (which Android for some odd reason does not, even tho it is part of JDK 1.6), then that one.
EDIT:
Auto-discovery should be based on couple of things:
XMLInputFactory (etc) check to see if matching system property ("javax.xml.stream.XMLInputFactory") has value; this is class name of factory to use, if any. So you can set system property with specific impl value
If no system property found, see if there is resource META-INF/services/javax.xml.stream.XMLInputFactory to read; one line with class name
If neither works, try to create default instance; for J2SE SDK this would be implementation that Oracle provides (Sjsxp)
Woodstox provides SPI information (step 2), to auto-register itself. But since on Android you repackage jars (as Android packages, APK?), it is possible that resource files are either not included, or not found. If this is the root cause, you can still set matching system property. If not, you will need to provide class name or factory instance using other means.

how to override a service provider in java

This is more a general question by example:
I'm using xstream and woodstox, woodstox comes with a service provider for javax.xml.stream.XMLOutputFactory in woodstox jar registering com.ctc.wstx.stax.WstxOutputFactory.
I want to provide my own javax.xml.stream.XMLOutputFactory and still have woodstox jar in the classpath. I know I can provide my own with the system property javax.xml.stream.XMLOutputFactory , but I'm trying to take off the hassle from our dev ops team and do it with a service file in my jar or maybe in my war's META-INF/services folder. looking the code of javax.xml.stream.FactoryFinder how can I make sure that my
META-INF/services/javax.xml.stream.XMLOutputFactory file will be the one used by FactoryFinder?
we use xstream with camel and could not find a way to inject the factory to XStreamDataFormat
First: instead of relying on JDK SPI interface, I strongly recommend simplifying your life and NOT using it. It really adds no value over injecting XMLInputFactory and/or XMLOutputFactory yourself. For injection you can use Guice (or Spring); or just pass it manually. Since these factories do not have dependencies of their own, this is easy.
But if choose to (or have to) use XMLInputFactory.newInstance(), you can define a System property for "javax.xml.stream.XMLOutputFactory" and "javax.xml.stream.XMLInputFactory".
So why not use JDK approach? Multiple reasons:
It adds overhead: if you are not specifying System property, it will have to scan the whole classpath, and with big app servers this takes 10x-100x as long as most parsing
Precedence of implementations is undefined: if you multiple in classpath, which one will you get? Who knows... (and note: it might even change when you add new jars in classpath)
You are very likely to get multiple impl via transitive dependencies
Unfortunately, Oracle still seems to insist on adding this known-faulty method for registering service providers. Why? Probably because they do not have a DI lib/framework of their own (Guice is by google, Spring by Springsource), and they tend to be pretty control hungry.
You can just do like this to specify the XMLOutputFactory implementation You want to use:
System.setProperty("javax.xml.stream.XMLOutputFactory", ... full classname You want to use ...);
Source:
http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/tutorial/doc/SJSXP4.html
Deriving from JAXP, the XMLInputFactory.newInstance() method
determines the specific XMLInputFactory implementation class to load
by using the following lookup procedure:
Use the javax.xml.stream.XMLInputFactory system property.
Use the lib/xml.stream.properties file in the JRE directory.
Use the Services API, if available, to determine the classname by looking in the META-INF/services/javax.xml.stream.XMLInputFactory
files in jars available to the JRE.
Use the platform default XMLInputFactory instance.
I discovered that if I put the service file under
WEB-INF/classes/services/javax.xml.stream.XMLOutputFactory then it will be first in classpath and before jars in WEB-INF/lib.
and that's my solution.
We had similar issue where parsing would run in local but fail on server. After debugging found server is using reader com.ctc.wstx.evt.WstxEventReader
Whereas on local reader was com.sun.xml.internal.stream.XMLEventReaderImpl
We set following property to resolve it.
System.setProperty("javax.xml.stream.XMLInputFactory", "com.sun.xml.internal.stream.XMLInputFactoryImpl");
If your implementation is in a jar then make sure it is before woodstox.jar on the class path, then FactoryFinder will use your implementation.

How to set Saxon as the Xslt processor in Java?

This is a simple question, but one I cannot find the answer to. I have an XSLT 2.0 stylesheet that I'm trying to process in Java. It relies on XSL elements from Saxon.
My current class works fine with simple XSLT 1.0, but I'm getting errors about unrecognized elements with my 2.0 XSLT built with Saxon.
I cannot figure out how to tell Java to use Saxon as the processor. I'm using javax.xml.transform in my class. Is this a property I can set? What do I set it to?
Thanks!
Edited
I figured out how to set the property to use Saxon, but now I'm getting this error.
Provider net.sf.saxon.TransformerFactoryImpl not found
How do I include Saxon in my application?
There are multiple ways to do this (in order of lookup precedence):
Direct Instantiation
Explicitly instantiate the Saxon factory (with a nod to Michael's comment above):
TransformerFactory fact = new net.sf.saxon.TransformerFactoryImpl()
This approach means that your code is locked into using Saxon at compile time. This can be seen as an advantage (no risk of it running with the wrong processor) or a disadvantage (no opportunity to configure a different processor at execution time - not even Saxon Enterprise Edition).
For Saxon-PE, substitute com.saxonica.config.ProfessionalTransformerFactory. For Saxon-EE, substitute com.saxonica.config.EnterpriseTransformerFactory.
Specify Class Name
Specify the factory class when constructing it:
TransformerFactory fact = TransformerFactory.newInstance(
"net.sf.saxon.TransformerFactoryImpl", null);
Note: available as of Java 6. The Java 5 version does not have this method.
This approach allows you to choose the processor at execution time, while still avoiding the costs and risks of a classpath search. For example, your application could provide some configuration mechanism to allow it to run with different Saxon editions by choosing between the various Saxon factory classes.
Use System Property
Set the javax.xml.transform.TransformerFactory system property before creating an instance:
System.setProperty("javax.xml.transform.TransformerFactory",
"net.sf.saxon.TransformerFactoryImpl");
Or on the command line (line broken for readability):
java -Djavax.xml.transform.TransformerFactory=
net.sf.saxon.TransformerFactoryImpl YourApp
This approach has the disadvantage that system properties affect the whole Java VM. Setting this property to select Saxon could mean that some other module in the application, which you might not even know about, starts using Saxon instead of Xalan, and that module could fail as a result if it uses Xalan-specific XSLT constructs.
Use Properties File
Create the following file:
JRE/lib/jaxp.properties
With the following contents:
javax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl
This approach has similar consequences to using the system property.
Service Loader
Create the following file in any JAR on the CLASSPATH:
META-INF/services/javax.xml.transform.TransformerFactory
With the following contents:
net.sf.saxon.TransformerFactoryImpl
This approach has the disadvantage that a small change to the classpath could cause the application to run with a different XSLT engine, perhaps one that the application has never been tested with.
Platform Default
If none of the above are done, then the platform default TransformerFactory instance will be loaded. A friendly description of this plugability layer can be found here.
Note that 'platform' here means the Java VM, not the hardware or operating system that it is running on. For all current known Java VMs, the platform default is a version of Xalan (which only supports XSLT 1.0). There is no guarantee that this will always be true of every Java VM in the future.
I'd consider this answer an argument against the Java way of doing things.
You can explicitly construct the required Source and Result objects to make sure they are Saxon implementations rather than whatever the default ones are.
I wrote a wrapper around Saxon parser in order to make its use simple, and I called it "EasySaxon": you can find it here, with some code snippet of samples.
Hope it helps.
Francesco

Tomcat web app - specify TransformerFactoryImpl class doesn't work

I need to use an xsl transformation inside a webapp and I decided to use saxon for the xml implementation. I included saxon(version 9.1) in the pom.xml as dependency. But I still keep getting this error - Provider net.sf.saxon.TransformerFactoryImpl not found
I found this thread,
How to select saxon TransformerFactory in Java
and did all that was recommended, setting property, specifying a service file with saxon implementation class. For flexibility reasons, I can't directly create an instance of TransformerFactoryImpl. So, do I have any other choice left?
Thanks in advance,
Edit: This is the stack trace of the error -
javax.xml.transform.TransformerFactoryConfigurationError: Provider net.sf.saxon.TransformerFactoryImpl not found
javax.xml.transform.TransformerFactory.newInstance(TransformerFactory.java:108)
com.mondeca.sesame.toolkit.repository.XMLtoRDFDataInjector.injectData(XMLtoRDFDataInjector.java:83)
com.mondeca.sesame.toolkit.repository.LocalMemoryRepositoryProvider.init(LocalMemoryRepositoryProvider.java:105)
org.datalift.modules.base.XmlConverter.applyXslTransformation(XmlConverter.java:192)
org.datalift.modules.base.XmlConverter.transformData(XmlConverter.java:204)
org.datalift.modules.base.XmlConverter.transformData(XmlConverter.java:174)
org.datalift.modules.base.XmlConverter.loadSourceData(XmlConverter.java:149)
From which it's clear that it can't find saxon. But I simply don't understand why! I can see that its packaged in the jar file too!
I thought this would be useful for someone looking for a solution. I solved it a long time ago, but I forgot to update it here. Basically tomcat will try and over ride any XML implementations with it's own implementations. The tomcat I was using(must be 6.0.23) didn't contain the latest XML APIs to deal with XSLT transformations(I needed APIs which could handle XSLT2). Actually tomcat has a bit of strange behaviour here, it doesn't load classes from classpath first but tries to load classes from it's own libraries(which inturn might contain all required java classes), which is not a natural order of loading classes. So, specifying in the manifest file wouldn't help. On the other hand we can over ride this by specifying as an option while starting the server. Use this option as server arg
-Djava.endorsed.dirs=$JAVA_ENDORSED_DIRS
For more information, tomcat official documentation on this issue
You could try adding Saxon JAR to the CLASSPATH and load Saxon using:
TransformerFactory transFactory = new net.sf.saxon.TransformerFactoryImpl();

calling plugin from java code

hi i am using eclipse as IDE for the development of my application. I have one doubt.
I have one plugin that is capable to creating a class on from one xml file. Now, the problem is that i have many xml files and classses to generate..
can anyone please tell me how to invoke the plugin from my java test class, so that i can create classes all together..
Please help me.
I'm not sure which plugin you have in mind, but considering that Eclipse is written in Java itself, you are likely to be able to find a suitable jar file that implement what you need. How its API is documented depends on the plugin (in some cases, you might even be able to find its source code). It's possible that using such a plugin may require using OSGi, since it's what Eclipse uses for its plugins.
However, if your goal is to generate classes from XML (presumably XML shemas) there are libraries for this that you can use directly, for example:
JAXB
Apache XML Beans
Relaxer (for Relax-NG, not for XML shema, with some brief documentation in English within the zip file)
The Eclipse Dali Plugin is able to generate Java classes from an XML schema. In the link below see the section on "JAXB Class Generation".
ttp://www.eclipse.org/webtools/releases/3.2.0/NewAndNoteworthy/jpa.php

Categories

Resources