how to override a service provider in java - 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.

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.

Resolve implementation of an API dynamically

We have a system where we wanted to consume the implementation of our interfaces in a separate jar. The scenario is clients consume our work and provide their own implementation to override default implementation.
The question is what is the best way to bind/wire the actual implementation classes into our system?
One way is let spring wire the dependencies. It is currently not an option since all clients are not using spring.
Looked into some options like resolving interface implementation classes using reflection. Not very happy with this solution.
Another good old option is configure the class name in one of the property and let clients configure it. It looks good.
But wanted to find some elegant option if available.
Also any idea how SLF4J / EL resolves their implementations automatically?
I'd suggest you to use SPI (Service Provider Interface).
It requires creating file that enumerates all available implementations of specific service. This may be annoying. Fortunately you can use this open source library that does this work for you: http://code.google.com/p/spi/
Perhaps the Reflections library is what you are looking for.
Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project.
Using Reflections you can query your metadata such as:
get all subtypes of some type
get all types/methods/fields annotated with some annotation, w/o annotation parameters matching
get all resources matching matching a regular expression

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();

Changing default implementation of org.w3c.dom.Document

I need to change the default implementation within my project for org.w3c.dom.Document.
I followed this link
to change the default implementation for:
javax.xml.parsers.DocumentBuilderFactory
javax.xml.parsers.SAXParserFactory
javax.xml.transform.TransformerFactory
I've created 3 files with the above names with in META-INF/services and put in each the following lines:
In file: javax.xml.parsers.DocumentBuilderFactory I put: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
In file: javax.xml.parsers.SAXParserFactory I put: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
In file: javax.xml.transform.TransformerFactory I put: org.apache.xalan.processor.TransformerFactoryImpl
But when I deployed over Oracle Application Server I got that the implementation class of org.w3c.dom.Document is : oracle.xml.parser.v2.XMLDocument instead of com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl that is being printed when development on Jetty.
I am developing on Jetty and deploying on Oracle application server.
It sounds like you are doing the right thing. But it might be simpler to use the system properties method ... at least until you can figure out what is going wrong with the "services" method.
Possibly the oracle.xml.parser.v2.XMLDocument implementation for org.w3c.dom.Document is found before com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl by the classloader.
Check if its possible to exclude the Oracle implementation, look for the file which contains the class. This might be located in a "container wide" folder and your implementation in an "application wide" folder.
Haven't had exactly the same problem, but similar, where the order of jars loaded by the classloader was important. Hope this can give you a push in the right direction at least
Two things to note:
1.
com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl has a hint for you: it's an Internal implementation (as opposed to Public API). It is prone to change and should not be the top choice for production development.
2.
I found that com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl is the best implementation that suited all of my needs and as far as I could tell, it matches the specification.
You can easily see a conflict.
I'm hoping one day there will be a platform standard, public API for a Document implementation and for all of its factories and such.
Here's my experience:
playing with META-INF/services and JAR ordering in the classpath felt like a hack, worked even worse and in the end, I moved away from that approach.
Here's why it didn't work for me: there were 2+ 3rd party implementations on the classpath, so there was no hope getting .xerces.internal. by default. However, specifying it as high as System Property would override it for everything, which didn't work out for the 3rd party products.
• • • I ended up creating a property and loading the precise factory I wanted explicitly, without relying on the search mechanism through the META-INF/services and system properties.
By the way, different factories use different steps of searching, which is inconsistent and I hope Oracle can find a way to standardize this process and make it more flexible and controllable.

Implementing dynamic plugins in Java

I'd like to implement a dynamic plugin feature in a Java application. Ideally:
The application would define an interface Plugin with a method like getCapabilities().
A plugin would be a JAR pluginX.jar containing a class PluginXImpl implementing Plugin (and maybe some others).
The user would put pluginX.jar in a special directory or set a configuration parameter pointing to it. The user should not necessarily have to include pluginX.jar in their classpath.
The application would find PluginXImpl (maybe via the JAR manifest, maybe by reflection) and add it to a registry.
The client could get an instance of PluginXImpl, e.g., by invoking a method like getPluginWithCapabilities("X"). The user should not necessarily have to know the name of the plugin.
I've got a sense I should be able to do this with peaberry, but I can't make any sense of the documentation. I've invested some time in learning Guice, so my preferred answer would not be "use Spring Dynamic Modules."
Can anybody give me a simple idea of how to go about doing this using Guice/peaberry, OSGi, or just plain Java?
This is actually quite easy using plain Java means:
Since you don't want the user to configure the classpath before starting the application, I would first create a URLClassLoader with an array of URLs to the files in your plugin directory. Use File.listFiles to find all plugin jars and then File.toURI().toURL() to get a URL to each file. You should pass the system classloader (ClassLoader.getSystemClassLoader()) as a parent to your URLClassLoader.
If the plugin jars contain a configuration file in META-INF/services as described in the API documentation for java.util.ServiceLoader, you can now use ServiceLoader.load(Plugin.class, myUrlClassLoader) to obatin a service loader for your Plugin interface and call iterator() on it to get instances of all configured Plugin implementations.
You still have to provide your own wrapper around this to filter plugin capabilites, but that shouldn't be too much trouble, I suppose.
OSGI would be fine if you want to replace the plugins during runtime i.g. for bugfixes in a 24/7 environment. I played a while with OSGI but it took too much time, because it wasn't a requirement, and you need a plan b if you remove a bundle.
My humble solution then was, providing a properties files with the class names of plugin descriptor classes and let the server call them to register (including quering their capabilities).
This is obvious suboptimal but I can't wait to read the accepted answer.
Any chance you can leverage the Service Provider Interface?
The best way to implement plug-ins with Guice is with Multibindings. The linked page goes into detail on how to use multibindings to host plugins.
Apologize if you know this, but check out the forName method of Class. It is used at least in JDBC to dynamically load the DBMS-specific driver classes runtime by class name.
Then I guess it would not be difficult to enumerate all class/jar files in a directory, load each of them, and define an interface for a static method getCapabilities() (or any name you choose) that returns their capabilities/description in whatever terms and format that makes sense for your system.

Categories

Resources