How to perform XSLT Transformation with Java extension from within OSGi Bundle
We are in the process of converting existing code to an OSGi environment. In one of our (yet to be) OSGi bundles we have code that perform an XSLT transformation. A piece of the XSLT includes a java extension function to create a unique numeric value. The Java class also resides within the bundle. This is a fragment of the style sheet:
<xsl:template match="m:property">
<xsl:variable name="uniqueDataStreamName" select="concat(../@id,'/',@name)" />
<xsl:variable name="uniqueDataStreamId"
select="java:com.xyz.TransformationUtils.makeDataStreamIdFromUniqueName($uniqueDataStreamName)" />
<data id="{number($uniqueDataStreamId)}">
<tag>
<xsl:value-of select="$uniqueDataStreamName" />
</tag>
<current_value>
<xsl:value-of select="@value" />
</current_value>
</data>
For reference, this is how the transformation is set up and called:
protected Templates createTemplates(Source xsltSource) {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Templates templates = tf.newTemplates(xsltSource);
return templates;
} catch (TransformerException e) {
throw new RuntimeException(e);
}
}
protected byte[] transform(byte[] input) throws TransformerException {
ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
templates.newTransformer().transform(
new StreamSource(new ByteArrayInputStream(input)),
new Stream开发者_运维百科Result(out));
return out.toByteArray();
}
When run in a non OSGi environemnt, it works. When run in an OSGi framework, it fails because the stylesheet cannot be compiled because the class TransformationUtils cannot be found. I kind of understand that--the class loader loading the jaxp transformer implementation does not see the extension class in our bundle. However, I'm stumped at finding a solution. I've tried using OSGi:fied Xalan and Xerces bundles to no avail.
My question is: How can this be solved? Can it?
The answer depends on how the XSLT processor looks up the extension class. You might need to read the source code and/or run it through a debugger to find this out.
For example, if the XSLT processor uses the Thread Context ClassLoader (TCCL) then this would normally fail because the TCCL is undefined in OSGi. You can work around this by explicitly setting the TCCL for the duration of the call to the XSLT processor, e.g.:
ClassLoader orig = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader());
try {
// invoke XSLT processor
} finally {
Thread.currentThread.setContextClassLoader(orig);
}
where MyClass
is a class from your bundle and has visibility of the extension class.
In the worst case, the processor might look up the extension using its own classloader, i.e. with a simple call to Class.forName()
. The best thing to do here is to beat up the developer of the library for being so stupid. After you've done that, you could use a fragment to attach the extension class to the processor bundle... it's a nasty hack but better than some of the other possible nasty hacks.
精彩评论