XSL Component

The X:Forge XSL component is an automated component for doing XSLT transformations. It provides a convenient method to configure system-wide the transformer to be used via a TRAX interface. A fairly central feature of the XSL component is that it can be used multiple times in one X:Forge pass over the XML data. The usual approach to generating XML and transforming to some output format is like this:

  1. Generate XML.
  2. Generate more XML.
  3. Transform everything to the intended output format.

With the X:Forge XSL component a workflow like this becomes possible:

  1. Generate XML (using an X:Forge component).
  2. Transform the generated XML to the intended output format.
  3. Generate more XML (using another X:Forge component).
  4. Transform the generated XML to the intended output format.

Configuration


<component-instance name="xsl" class="org.bibop.xml.xforge.components.xsl.XSLComponent"
	handle-errors-internally="true" logger="mylogger">
	<!-- <transformer-factory>com.ambrosoft.gregor.
				  trax.TransformerFactoryImpl</transformer-factory> -->
	<!-- <transformer-factory>com.icl.saxon.
				  TransformerFactoryImpl</transformer-factory> -->
</component-instance>

Declaration

The component has to be declared in the configuration file in order to use it in the system. To that end a namespace prefix (xsl would make sense) must be mapped to the appropriate class. Optionally it can be specified which transformer should be used by giving the class of its Factory implementation. Only transformers that conform to the TRAX API (package javax.xml.transform) are supported.

If there is a specific TRAX transformer defined, then the XSL component will use that one and throw an exception if it is not available or otherwise defective. If there is no specific TRAX transformer defined, then the XSL component will first try to load Xalan and if that fails it will use the system-default TRAX transformer. It is possible to set the system-default by using the command line option -Djavax.xml.transform.TransformerFactory=my.transformer.factory when starting up the JVM. If no system-default TRAX transformer is found either, an exception is thrown.

Usage

Invocation

The following example for using the XSL component is similar to the code in the supplied test/all.xml file and it uses the supplied test/test.xsl file, so it is possible to verify that the XSL component is configured correctly by running test/all.xml as a whole.


<xsl:apply>
  <xf:parameter name="xml">
    <xsldate>
      <date:xmlnow/>
    </xsldate>
  </xf:parameter>
  <xf:parameter name="xsl">test/test.xsl</xf:parameter>
  <xf:parameter name="parse">xml</xf:parameter>
</xsl:apply>

The first parameter specifies the XML to transform. It is good practice to always make sure that there is a root tag (like <xsldate> in this example), otherwise the transformer might not parse it, depending on the structure of the XML that is created. The content of this parameter can either be some static XML tags or dynamically-built XML. A good way to build XML dynamically is of course X:Forge itself - the example shows a call to the supplied example component Date. Here <date:xmlnow/> will give something like this:


<xsldate>
	<today>
		<date>
			<year>2002</year>
			<month>11</month>
			<day>27</day>
		</date>
		<time>
			<hour>11</hour>
			<minutes>6</minutes>
			<seconds>13</seconds>
			<milliseconds>159</milliseconds>
		</time>
	</today>
</xsldate>

This XML will be transformed by the XSLT stylesheet specified in the second parameter. The created transformer is saved internally, so on subsequent calls to <xsl:apply> the performance should be much better. Tests indicate a performance increase of 3-4 times. However, if the file containing the stylesheet is modified, then the XSL component will re-create the transformer automatically.

The third parameter tells X:Forge to parse as XML, this is also the default, so in most scenarios this parameter does not have to be specified at all. Besides "xml" this parameter can also contain the value "text", in which case the resulting XML will have the left angle bracket < escaped to &lt;, the double quotes " to &quot; and the ampersand & to &amp;.

Resulting XML

This is the output generated by the previous query:


<xsl-transformer-used>Apache Software Foundation,    
		      http://xml.apache.org/xalan-j</xsl-transformer-used>

<result>Right now we have: Year 2002, Month 11, Day 27, Hour 0, Minute 11, Second 48,
								and Millisecond 276.</result>

Obviously this is not very interesting, but that's exactly what the supplied test.xsl stylesheet does. It matches the various XML tags for day, minute, second etc. and forms a pseudo-sentence like above. Additionally, the stylesheet outputs a vendor string, so it is possible to verify that the intended TRAX transformer was used.

Performance Testing

To minimize external effects the supplied test/XSLT.xml should be used as a starting point for performance testing. The XSL component will then tally additional stats and give the raw XSLT transformation time at the end (i.e. excluding X:Forge overhead). Basically, the difference of test/XSLT.xml compared to the previous examples is that the <xsl:applyandmeasure> element is used and the XML to transform is static.

Errors

Known Error Conditions

  • No TRAX-compliant transformer can be found. JDK 1.4 automatically includes Xalan, so this error should never happen there. However, for earlier JDKs a TRAX compliant transformer must be in the lib directory.
  • The XML to be transformed is broken in some way: no root tag, invalid structure, invalid XML characters etc.
  • The XSLT stylesheet that was specified is broken in some way.

Error Handling

If the component is configured to handle exceptions internally, when an exception occurs, the resulting XML is the exception formatted. Here is an example:


            <error>Something wrong with the transformer:
                javax.xml.transform.TransformerConfigurationException:
                javax.xml.transform.TransformerConfigurationException:
                javax.xml.transform.TransformerException:
                javax.xml.transform.TransformerException: Non-whitespace
                text is not allowed in this position in the stylesheet!
                <stack-trace>org.bibop.xml.xforge.XForgeException:
                    Something wrong with the transformer:
                    javax.xml.transform.TransformerConfigurationException:
                    javax.xml.transform.TransformerConfigurationException:
                    javax.xml.transform.TransformerException:
                    javax.xml.transform.TransformerException:
                    Non-whitespace text is not allowed in this position
                    in the stylesheet!  at
                    org.bibop.xml.xforge.components.xsl.XSLComponent.apply
			(XSLComponent.java:126)
                    at
                    org.bibop.xml.xforge.components.xsl.XSLComponent.apply
			(XSLComponent.java:93)
                    at
                    org.bibop.xml.xforge.components.xsl.XSLComponent.applyandmeasure
			(XSLComponent.java:195)
                    at
                    sun.reflect.NativeMethodAccessorImpl.invoke0(Native
                    Method)  at
                    sun.reflect.NativeMethodAccessorImpl.invoke
			(NativeMethodAccessorImpl.java:39)
                    at
                    sun.reflect.DelegatingMethodAccessorImpl.invoke
			(DelegatingMethodAccessorImpl.java:25)
                    at java.lang.reflect.Method.invoke(Method.java:324)
                    at
                    org.bibop.xml.xforge.components.automation.AbstractAutomatedComponent.executeMethod
			(AbstractAutomatedComponent.java:127)
                    at
                    org.bibop.xml.xforge.components.automation.AbstractAutomatedComponent.toSax
			(AbstractAutomatedComponent.java:44)
                    at
                    org.bibop.xml.xforge.AutomationElement.toSax
			(AutomationElement.java:202)
                    at
                    org.bibop.xml.xforge.XForgeProcessor.endElement
			(XForgeProcessor.java:588)
                    at
                    org.apache.xerces.parsers.AbstractSAXParser.endElement
			(AbstractSAXParser.java:585)
                    at
                    org.apache.xerces.impl.XMLNamespaceBinder.handleEndElement
			(XMLNamespaceBinder.java:898)
                    at
                    org.apache.xerces.impl.XMLNamespaceBinder.endElement
			(XMLNamespaceBinder.java:644)
                    at
                    org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanEndElement
			(XMLDocumentFragmentScannerImpl.java:1008)
                    at
                    org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch
			(XMLDocumentFragmentScannerImpl.java:1469)
                    at
                    org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument
			(XMLDocumentFragmentScannerImpl.java:329)
                    at
                    org.apache.xerces.parsers.DTDConfiguration.parse
			(DTDConfiguration.java:525)
                    at
                    org.apache.xerces.parsers.DTDConfiguration.parse
			(DTDConfiguration.java:581)
                    at
                    org.apache.xerces.parsers.XMLParser.parse
			(XMLParser.java:152)
                    at
                    org.apache.xerces.parsers.AbstractSAXParser.parse
			(AbstractSAXParser.java:1175)
                    at org.bibop.xml.xforge.Test.test
			(Test.java:208)  at
                    org.bibop.xml.xforge.Test.main
			(Test.java:122)
                </stack-trace>
            </error>

Otherwise, if the component is configured not to handle exceptions, the error is just re-thrown to the caller.