By G. Todd Miller -updated May 11, 2001
See also: Calling XSLTC with the TrAX/JAXP API
| |
This document describes what I have so far as a prototype for integrating
Translets with the JAXP TrAX. In a nutshell, a new Transformer factory
class has be written that extends the JAXP SAXTransformerFactory class.
The newfactory delivers Translets as Transformers, and creates Templates.
Calling the Transformer transform() method will cause the given XML
document to be transformed by a translet that has been compiled from the
supplied stylesheet. The switch that determines what XSLT processor gets
to transform the XML document is based on the value of a JAXP system
property for the Transformers factory.
|
| |
The Java API for XML Processing (JAXP) includes an XSLT framework based on the
Transformation API for XML (TrAX). In a typical JAXP transformation application
the steps involved in transforming an XML document with an XSLT stylesheet are: (1)
create an instance of the TransformerFactory class, (2) from the factory instance and
a given XSLT stylesheet, create a new Transformer object, (3) call the Transformer
objects transform() method on a given XML document and a specified Result object.
Alternatively, one could also ask the instance of the TransformerFactory for
a Templates object given an XSLT stylesheet. From the Templates object, a new
Transformer can be created, and again, its transform() method can be called with
an XML document and specified Result object.
The code below illustrates a simple JAXP transformation application (Proto.java)
that creates the Transformer directly.
| | | | import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
public class Proto {
public static void main(String[] args){
Proto app = new Proto();
app.run(args);
}
public void run(String[] args){
if (args.length != 2) {
usage();
}
String inputFilename = args[0];
String stylesheet = args[1];
Transformer transformer;
TransformerFactory factory = TransformerFactory.newInstance();
try {
transformer = factory.newTransformer(new StreamSource(stylesheet));
transformer.transform(new StreamSource(inputFilename),
new StreamResult(System.out));
} catch (Exception e) {
// nothing...
}
}
public void usage() {
System.err.println(
"Usage: run <xml_file> <xsl_file>");
System.exit(1);
}
} | | | | |
The use of Templates is useful when multiple instances of the same transformer
are needed. For example transforming multiple documents by the same transformer,
each working in a separate thread. In this case, the TransformerFactory is used to
create a Templates object for a given stylesheet. Each Transformer instance is then
created by the Templates object. In this way the Templates object can compile the
stylesheet once, and each time a new instance of the transformer is asked for, a clone is
provided rather than recompile the stylesheet again and again. The Proto class above
would be modified as follows:
| | | | try {
Templates templates = factory.newTemplates(new StreamSource(stylesheet));
transformer = templates.newTransformer();
transformer.transform(new StreamSource(inputFilename),
new StreamResult(System.out));
} catch (Exception e) {
// nothing...
} | | | | |
The JAXP TransformerFactory is configurable. The API supports configuring the
factory to: (1) use attributes which are passed down to the underlying XSL processor,
these are vendor dependent, (2) register an ErrorListener that provides callbacks
to handle error and warning message generated by the XSL processor, (3) register
an URIResolver which can be used to resolve URIs encountered in xsl:include,
xsl:import,anddocument() functions.
The JAXP TransformerFactory can be queried at runtme to discover what features
it supports. For example, an application might want to know if a particular factory
implementation supports the use of SAX events as a source, or whether it can write
out transformation results as a DOM. The factory API queries with the getFeature()
method. In the Proto code above, I could add the following code before the try-catch
block:
| | | | if (!(factory.getFeature(StreamSource.FEATURE)) ||
!(factory.getFeature(StreamResult.FEATURE))) {
System.err.println(
"Stream Source/Result not supported by Transformer Factory\n" +
"exiting.");
System.exit(1);
} | | | | |
|
| |
The crux of the integration strategy is the pluggable TransformerFactory class. The
JAXP specifies that the actual TransformerFactory implementation be controlled by the
a Java system property (javax.xml.transformer.TransformerFactory) . This system property can be specified in the usual ways, for
example in a properties file or on the command line as a -D optionpassed to thejava
engine itself. The strategy involves writing a TransformerFactory for Translets.
In the JAXP the TransformerFactory is an abstract class. In Xalan, the system
property specifies the implementation class TransformerFactoryImpl (org.apache.xalan.processor.TransformerFactoryImpl).
This implementation is an extension of the abstract SAXTransformerFactory class, which in
turn is an extension of the abstract TransformerFactory class (javax.xml.transform.sax.SAXTransformerFactory).
In this prototype integration I have constructed an XSLTC TransformerFactory as
an extension of the abstract SAXTransformerFactory class.
|
| | | | SAXTransformerFactory for Translets | | | | |
| |
The prototype XSLTC TransformerFactory needs to accomplish the following:
| | | | TransformerFactory Methods | | | | |
| |
The methods of the abstract TransformerFactory class have been stubbed out as
follows:
| | | | public ErrorListener getErrorListener() { return null; }
public void setErrorListener(ErrorListener listener) { }
public Object getAttribute(String name) { return null; }
public void setAttribute(String name, Object value) { }
public boolean getFeature(String name) { return false; }
public URIResolver getURIResolver() { return null; }
public void setURIResolver(URIResolver resolver) { }
public Source getAssociatedStylesheet(Source src, String media,
String title, String charset) { return null; }
public Templates newTemplates(Source xslSrc) throws
TransformerConfigurationException { return null; }
public Transformer newTransformer() throws
TransformerConfigurationException { return null; } | | | | |
The methods listed above fall into 4 categories: (1) Transform creation,
(2) Templates creation, (3) feature discovery,
and (4) others that will remain unimplemented at this time.
| |
The JAXP specifies a method that takes the stylesheet as a Source object and returns a
Transformer.
public Transformer newTransformer(Source xslSrc)
This method needs to return a Transformer to comply with the JAXP API, but also
needs to return a Translet. In the XSLTC/Xalan API, a Translet is an interface that is
implemented by the runtime abstract class AbstractTranslet (org.apache.xalan.xsltc.Translet and org.apache.xalan.xsltc.runtime.AbstractTranslet, repectively). Therefore, I had to
change the AbstractTranslet class so that it will extend the abstract Transformer
class while still implementing the Translet interface. Once this is done, then the
newTransformer() method of the new TransformerFactory can return a Translet that
is a Transformer.
In order to have AbstractTranslet extend Transformer, several abstract methods
have to be implemented in AbstractTranslet class. These are:
| | | | public void clearParameters() { }
public ErrorListener getErrorListener() { return null; }
public Properties getOutputProperties() throws IllegalArgumentException {
return null;
}
public String getOutputProperty(String name)
throws IllegalArgumentException{ return ""; }
//public Object getParameter(String name) { return null; }
public URIResolver getURIResolver() { return null; }
public void setErrorListener(ErrorListener listener)
throws IllegalArgumentException { }
public void setOutputProperties(Properties props)
throws IllegalArgumentException { }
public void setOutputProperty(String name, String value)
throws IllegalArgumentException { }
public void setParameter(String name, Object value) { }
public void setURIResolver(URIResolver resolver) { }
public void transform(Source xmlsrc, Result outputTarget)
throws TransformerException { ... } | | | | |
Mapping these methods to some existing ones in AbstractTranslet still has to be
done. In particular the transform() method which is explained further in a following
section.
Now that XSLTCbstractTranslet is a Transformer, the new Transformer
factory class can implement the newTransformer() method. This method needs to do
two fundamental things: (1) create a translet from the given XSLT stylesheet and (2)
instantiate the translet so it can be returned as a Transformer.
The code belows hows my current implementation of the newTransformer() method
in the new factory class:
| | | | public class ToddsTransformerFactoryImpl extends SAXTransformerFactory {
...
public Transformer newTransformer(Source stylesheet) throws
TransformerConfigurationException
{
XSLTC xsltc = new XSLTC();
xsltc.init();
String stylesheetName = stylesheet.getSystemId();
int index = stylesheetName.indexOf(;
String transletName = stylesheetName.substring(0,index);
boolean isSuccessful = true;
try {
File file = new File(stylesheetName);
URL url = file.toURL();
isSuccessful = xsltc.compile(url);
} catch (MalformedURLException e) {
throw new TransformerConfigurationException(
"URL for stylesheet stylesheetName +
"n not be formed.");
}
if (!isSuccessful) {
throw new TransformerConfigurationException(
"Compilation of stylesheet stylesheetName + "iled.");
}
Translet translet = null;
try {
Class clazz = Class.forName(transletName);
translet = (Translet)clazz.newInstance();
((AbstractTranslet)translet).setTransletName(transletName);
} catch (ClassNotFoundException e) {
throw new TransformerConfigurationException(
"Translet class transletName + "t found.");
} catch (InstantiationException e) {
throw new TransformerConfigurationException(
"Translet class transletName +
"uld not be instantiated");
} catch (IllegalAccessException e) {
throw new TransformerConfigurationException(
"Translet class transletName + "uld not be accessed.");
}
return (AbstractTranslet)translet;
}
} | | | | |
|
| |
As you can see, the prototype will compile the stylesheet everytime. This obviously
needs to be smarter. This is where the question I already have this translet?eds
to be answered. Also for now the translet bytecodes are written out to a File in the current
working directory, and then instantiated using a Class.forName, newInstance()
pair. This also needs work. Note the return statement, the Translet is cast to an AbstractTranslet
which now is a Transformer.
|
|
|
| | | | AbstractTransletransform() Method | | | | |
| |
Now the JAXP program (Proto) shown in the first section has a way to get a Transformer
factory that can return Translets. The next step of the Proto.java program is
calling the transform() method and having the Translet process the given XML
document.
This requires some surgery in the AbstractTranslet class again. The AbstractTranslet
already has a transform() method, but its signature is not the same as the
JAXP Transform signature. In the AbstractTranslet, transform methods expect a DOM
argument, in some cases a NodeIterator, and a TransletOutputHandler argument. The
JAXP Transformransform() signature requires only the XML document as a Source
object reference and an outputTarget as a Result object reference. My implementation
of the JAXP transform() signature is as shown:
| | | | public void transform(Source xmlsrc, Result outputTarget)
throws TransformerException
{
doTransform( xmlsrc.getSystemId(),
((StreamResult)outputTarget).getOutputStream() );
} | | | | |
This implementation leverages code derived from XSLTCefaultRun class. In
that class there is a doTransform() method that carries out the work of: (1) parsing the
input XML document (using a JAXP SAXParserFactory and SAXParser), (2) creating
a DOMImpl (org.apache.xalan.xsltc.dom.DOMImpl), (3) creating a SAXOutputHandler, and finally (4) calling the translet to
transform the input XML document (from the DOMImpl).
The code for doTransform is shown below:
| | | | public abstract class AbstractTranslet extends Transformer implements Translet{
...
private void doTransform(String xmlDocName, OutputStream ostream) {
try {
final Translet translet = (Translet)this; // GTM added
// Create a SAX parser and get the XMLReader object it uses
final SAXParserFactory factory = SAXParserFactory.newInstance();
final SAXParser parser = factory.newSAXParser();
final XMLReader reader = parser.getXMLReader();
// Set the DOMOM builder as the XMLReaderAX2 content handler
final DOMImpl dom = new DOMImpl();
reader.setContentHandler(dom.getBuilder());
// Create a DTD monitor and pass it to the XMLReader object
final DTDMonitor dtdMonitor = new DTDMonitor();
dtdMonitor.handleDTD(reader);
dom.setDocumentURI(xmlDocName);
/****************
if (_uri)
reader.parse(xmlDocName);
else
*******************/
reader.parse("file:"+(new File(xmlDocName).getAbsolutePath()));
// Set size of key/id indices
setIndexSize(dom.getSize());
// If there are any elements with ID attributes, build an index
dtdMonitor.buildIdIndex(dom, 0, this);
setUnparsedEntityURIs(dtdMonitor.getUnparsedEntityURIs());
// Transform the document
String encoding = translet.getOutputEncoding();
if (encoding == null) encoding = "UTF-8";
//TextOutput textOutput = new TextOutput(System.out, encoding);
DefaultSAXOutputHandler saxHandler = new
DefaultSAXOutputHandler(ostream, encoding);
TextOutput textOutput = new TextOutput(saxHandler, encoding);
translet.transform(dom, textOutput);
textOutput.flush();
}
catch (TransletException e) {
...
}
catch (RuntimeException e) {
...
}
catch (FileNotFoundException e) {
...
}
catch (MalformedURLException e) {
...
}
catch (UnknownHostException e) {
...
}
catch (Exception e) {
...
}
}
} | | | | |
|
| |
This is the current state of the integration of Translet and TrAX. The JAXP program
illustrated in the first section (Proto.java) compiles and runs with the changes outlined
above. The new transformer factory TransformerFactoryImpl supports transformer,
templates creation, and the feature discovery mechanism. The transformers
returned from the factory are in fact AbstractTranslet objects. The new class
TransletTemplates implements the Templates interface for translets. This is the
result of a first pass implementation, there are many stubbed out methods that need to
be implemented, translet caching in Templates needs to be implemented and support
for Source and Result types beyond simple stream types, such as DOM and SAX.
|
| |
| | | | TransformerFactoryImpl.java | | | | |
| |
| | | | package org.apache.xalan.xsltc.runtime;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.URIResolver;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TemplatesHandler;
import javax.xml.transform.sax.TransformerHandler;
import org.xml.sax.XMLFilter;
import org.apache.xalan.xsltc.Translet;
import org.apache.xalan.xsltc.compiler.XSLTC;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;
import java.io.File;
import java.net.URL;
import java.net.MalformedURLException;
/**
* Implementation of a JAXP1.1 SAXTransformerFactory for Translets.
*/
public class TransformerFactoryImpl extends SAXTransformerFactory {
public TransformerFactoryImpl() { /* nothing yet */ }
//////////////////////////////////////////////////////
// SAXTransformerFactory (subclass of TransformerFactory)
//
public TemplatesHandler newTemplatesHandler() { /*TBD*/ return null; }
public TransformerHandler newTransformerHandler() { /*TBD*/ return null; }
public TransformerHandler newTransformerHandler(Source src) {
/*TBD*/ return null;
}
public TransformerHandler newTransformerHandler(Templates templates) {
/*TBD*/ return null;
}
public XMLFilter newXMLFilter(Source src) { /*TBD*/ return null; }
public XMLFilter newXMLFilter(Templates templates) { /*TBD*/ return null; }
//
// End SAXTransformerFactory methods
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
// TransformerFactory
//
public ErrorListener getErrorListener() { /*TBD*/ return null; }
public void setErrorListener(ErrorListener listener) {/*TBD*/ }
public Object getAttribute(String name) { /*TBD*/ return null; }
public void setAttribute(String name, Object value) { /*TBD*/ }
public boolean getFeature(String name) {
if ((StreamSource.FEATURE == name) ||
(StreamResult.FEATURE == name) ||
(SAXTransformerFactory.FEATURE == name)) {
return true;
} else if ((StreamSource.FEATURE.equals(name))
|| (StreamResult.FEATURE.equals(name))
|| (SAXTransformerFactory.FEATURE.equals(name))) {
return true;
} else {
return false;
}
}
public URIResolver getURIResolver() { /*TBD*/ return null; }
public void setURIResolver(URIResolver resolver) {/*TBD*/ }
public Source getAssociatedStylesheet(Source src, String media,
String title, String charset) { /*TBD*/ return null; }
public Transformer newTransformer() throws
TransformerConfigurationException { /*TBD*/ return null; }
//
// End TransformerFactory methods
//////////////////////////////////////////////////////
public Transformer newTransformer(Source stylesheet) throws
TransformerConfigurationException
{
XSLTC xsltc = new XSLTC();
xsltc.init();
String stylesheetName = stylesheet.getSystemId();
int index = stylesheetName.indexOf(;
String transletName = stylesheetName.substring(0,index);
boolean isSuccessful = true;
try {
File file = new File(stylesheetName);
URL url = file.toURL();
isSuccessful = xsltc.compile(url);
} catch (MalformedURLException e) {
throw new TransformerConfigurationException(
"URL for stylesheet stylesheetName +
"n not be formed.");
}
if (!isSuccessful) {
throw new TransformerConfigurationException(
"Compilation of stylesheet stylesheetName + "iled.");
}
Translet translet = null;
try {
Class clazz = Class.forName(transletName);
translet = (Translet)clazz.newInstance();
((AbstractTranslet)translet).setTransletName(transletName);
} catch (ClassNotFoundException e) {
throw new TransformerConfigurationException(
"Translet class transletName + "t found.");
} catch (InstantiationException e) {
throw new TransformerConfigurationException(
"Translet class transletName +
"uld not be instantiated");
} catch (IllegalAccessException e) {
throw new TransformerConfigurationException(
"Translet class transletName + "uld not be accessed.");
}
return (AbstractTranslet)translet;
}
public Templates newTemplates(Source stylesheet) throws
TransformerConfigurationException
{
Transformer translet = newTransformer(stylesheet);
return new TransletTemplates(translet);
}
} | | | | |
|
| |
| | | | package org.apache.xalan.xsltc.runtime;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXTransformerFactory;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;
import java.util.Properties;
/**
* Implementation of a JAXP1.1 Templates object for Translets.
*/
public class TransletTemplates implements Templates {
public TransletTemplates(Transformer translet) {
_translet = (AbstractTranslet)translet;
}
public Properties getOutputProperties() { /*TBD*/ return null; }
public Transformer newTransformer() throws
TransformerConfigurationException
{
if (_translet == null) {
throw new TransformerConfigurationException(
"Error: Null Translet");
}
return _translet;
}
private AbstractTranslet _translet = null;
} | | | | |
|
| |
| | | | JAXP=/usr/local/jaxp-1.1/jaxp.jar
CRIMSON=/usr/local/jaxp-1.1/crimson.jar
XSLT=/net/bigblock/files18/tmiller/xml-xalan/java/build/classes
CLASSPATH=${JAXP}:${CRIMSON}:${XSLT}
SRCS=\
Proto.java
all:
javac -classpath ${CLASSPATH} ${SRCS} | | | | |
|
|
|