diff options
Diffstat (limited to 'test/java/src/org/apache/qetest/xslwrapper/TraxStreamWrapper.java')
-rw-r--r-- | test/java/src/org/apache/qetest/xslwrapper/TraxStreamWrapper.java | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/test/java/src/org/apache/qetest/xslwrapper/TraxStreamWrapper.java b/test/java/src/org/apache/qetest/xslwrapper/TraxStreamWrapper.java new file mode 100644 index 0000000..6591651 --- /dev/null +++ b/test/java/src/org/apache/qetest/xslwrapper/TraxStreamWrapper.java @@ -0,0 +1,587 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * $Id$ + */ +package org.apache.qetest.xslwrapper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.Hashtable; +import java.util.Properties; + +import javax.xml.transform.Source; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.qetest.QetestUtils; +import org.apache.xml.utils.DefaultErrorHandler; + +/** + * Implementation of TransformWrapper that uses the TrAX API and + * uses in-memory streams for it's sources. + * + * <p>This implementation separates the process of reading xml and xsl + * files from disk into byte arrays out from the time processing of + * a new StreamSource(byte[]) takes to build a stylesheet. + * It also separates the time of performing the transformation + * to a StreamResult(byte[]) from the time spent simply sending + * the byte[] through a FileOutputStream to disk.</p> + * + * <p><b>Important!</b> The underlying System property of + * javax.xml.transform.TransformerFactory will determine the actual + * TrAX implementation used. This value will be reported out in + * our getProcessorInfo() method.</p> + * + * <p>//@todo: currently limited to reading in files whose length + * will fit into an int value. The actual file reading routine + * should be updated in several of our methods.</p> + * + * @author Shane Curcuru + * @version $Id$ + */ +public class TraxStreamWrapper extends TransformWrapperHelper +{ + + /** + * TransformerFactory to use; constructed in newProcessor(). + */ + protected TransformerFactory factory = null; + + + /** + * Templates to use for buildStylesheet(). + */ + protected Templates builtTemplates = null; + + + /** + * Cached copy of newProcessor() Hashtable. + */ + protected Hashtable newProcessorOpts = null; + + + /** + * Get a general description of this wrapper itself. + * + * @return Uses TrAX to perform transforms from StreamSource(systemId) + */ + public String getDescription() + { + return "Uses TrAX to perform transforms from StreamSource(stream)"; + } + + + /** + * Get a specific description of the wrappered processor. + * + * @return specific description of the underlying processor or + * transformer implementation: this should include both the + * general product name, as well as specific version info. If + * possible, should be implemented without actively creating + * an underlying processor. + */ + public Properties getProcessorInfo() + { + Properties p = TraxWrapperUtils.getTraxInfo(); + p.put("traxwrapper.method", "streams"); + p.put("traxwrapper.desc", getDescription()); + return p; + } + + + /** + * Actually create/initialize an underlying processor or factory. + * + * For TrAX/javax.xml.transform implementations, this creates + * a new TransformerFactory. + * + * @param options Hashtable of options, unused. + * + * @return (Object)getProcessor() as a side-effect, this will + * be null if there was any problem creating the processor OR + * if the underlying implementation doesn't use this + * + * @throws Exception covers any underlying exceptions thrown + * by the actual implementation + */ + public Object newProcessor(Hashtable options) throws Exception + { + newProcessorOpts = options; + //@todo do we need to do any other cleanup? + reset(false); + factory = TransformerFactory.newInstance(); + factory.setErrorListener(new DefaultErrorHandler()); + // Verify the factory supports Streams! + if (!(factory.getFeature(StreamSource.FEATURE) + && factory.getFeature(StreamResult.FEATURE))) + { + throw new TransformerConfigurationException("TraxStreamWrapper.newProcessor: factory does not support Streams!"); + } + // Set any of our options as Attributes on the factory + TraxWrapperUtils.setAttributes(factory, options); + return (Object)factory; + } + + + /** + * Transform supplied xmlName file with the stylesheet in the + * xslName file into a resultName file. + * + * Names are assumed to be local path\filename references, and + * will be read as byte streams before being passed to + * underlying StreamSources, etc. + * + * @param xmlName local path\filename of XML file to transform + * @param xslName local path\filename of XSL stylesheet to use + * @param resultName local path\filename to put result in + * + * @return array of longs denoting timing of all parts of + * our operation: IDX_OVERALL, IDX_XSLREAD, IDX_XSLBUILD, + * IDX_XMLREAD, IDX_TRANSFORM, IDX_RESULTWRITE + * + * @throws Exception any underlying exceptions from the + * wrappered processor are simply allowed to propagate; throws + * a RuntimeException if any other problems prevent us from + * actually completing the operation + */ + public long[] transform(String xmlName, String xslName, String resultName) + throws Exception + { + preventFootShooting(); + long startTime = 0; + long xslRead = 0; + long xslBuild = 0; + long xmlRead = 0; + long transform = 0; + long resultWrite = 0; + + File xslFile = new File(xslName); + int xslLength = new Long(xslFile.length()).intValue(); //@todo warning: possible overflow + byte[] xslBytes = new byte[xslLength]; + FileInputStream xslStream = new FileInputStream(xslFile); + // Timed: read xsl into a byte array + startTime = System.currentTimeMillis(); + int xslRetVal = xslStream.read(xslBytes); + xslRead = System.currentTimeMillis() - startTime; + + // Untimed: create StreamSource and setSystemId + StreamSource xslSource = new StreamSource(new ByteArrayInputStream(xslBytes)); + // Note that systemIds must be a legal URI + xslSource.setSystemId(QetestUtils.filenameToURL(xslName)); + + // Timed: build Transformer from StreamSource + startTime = System.currentTimeMillis(); + Transformer transformer = factory.newTransformer(xslSource); + transformer.setErrorListener(new DefaultErrorHandler()); + xslBuild = System.currentTimeMillis() - startTime; + + File xmlFile = new File(xmlName); + int xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow + byte[] xmlBytes = new byte[xmlLength]; + FileInputStream xmlStream = new FileInputStream(xmlFile); + // Timed: read xml into a byte array + startTime = System.currentTimeMillis(); + int xmlRetVal = xmlStream.read(xmlBytes); + xmlRead = System.currentTimeMillis() - startTime; + + // Untimed: create StreamSource and setSystemId + StreamSource xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes)); + xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName)); + + // Untimed: create StreamResult + ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + StreamResult byteResult = new StreamResult(outBytes); + + // Untimed: Set any of our options as Attributes on the transformer + TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); + + // Untimed: Apply any parameters needed + applyParameters(transformer); + + // Timed: build xml (so to speak) and transform + startTime = System.currentTimeMillis(); + transformer.transform(xmlSource, byteResult); + transform = System.currentTimeMillis() - startTime; + + // Timed: writeResults from the byte array + startTime = System.currentTimeMillis(); + byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not? + FileOutputStream writeStream = new FileOutputStream(resultName); + writeStream.write(writeBytes); + writeStream.close(); + resultWrite = System.currentTimeMillis() - startTime; + + long[] times = getTimeArray(); + times[IDX_OVERALL] = xslRead + xslBuild + xmlRead + + transform + resultWrite; + times[IDX_XSLREAD] = xslRead; + times[IDX_XSLBUILD] = xslBuild; + times[IDX_XMLREAD] = xmlRead; + times[IDX_TRANSFORM] = transform; + times[IDX_RESULTWRITE] = resultWrite; + return times; + } + + + /** + * Pre-build/pre-compile a stylesheet. + * + * Although the actual mechanics are implementation-dependent, + * most processors have some method of pre-setting up the data + * needed by the stylesheet itself for later use in transforms. + * In TrAX/javax.xml.transform, this equates to creating a + * Templates object. + * + * Sets isStylesheetReady() to true if it succeeds. Users can + * then call transformWithStylesheet(xmlName, resultName) to + * actually perform a transformation with this pre-built + * stylesheet. + * + * @param xslName local path\filename of XSL stylesheet to use + * + * @return array of longs denoting timing of all parts of + * our operation: IDX_OVERALL, IDX_XSLREAD, IDX_XSLBUILD + * + * @throws Exception any underlying exceptions from the + * wrappered processor are simply allowed to propagate; throws + * a RuntimeException if any other problems prevent us from + * actually completing the operation + * + * @see #transformWithStylesheet(String xmlName, String resultName) + */ + public long[] buildStylesheet(String xslName) throws Exception + { + preventFootShooting(); + long startTime = 0; + long xslRead = 0; + long xslBuild = 0; + + File xslFile = new File(xslName); + int xslLength = new Long(xslFile.length()).intValue(); //@todo warning: possible overflow + byte[] xslBytes = new byte[xslLength]; + FileInputStream xslStream = new FileInputStream(xslFile); + // Timed: read xsl into a byte array + startTime = System.currentTimeMillis(); + int xslRetVal = xslStream.read(xslBytes); + xslRead = System.currentTimeMillis() - startTime; + + // Untimed: create StreamSource and setSystemId + StreamSource xslSource = new StreamSource(new ByteArrayInputStream(xslBytes)); + // Note that systemIds must be a legal URI + xslSource.setSystemId(QetestUtils.filenameToURL(xslName)); + + // Timed: build Transformer from StreamSource + startTime = System.currentTimeMillis(); + builtTemplates = factory.newTemplates(xslSource); + xslBuild = System.currentTimeMillis() - startTime; + + // Set internal state that we have a templates ready + // Note: in theory, there's no need to check builtTemplates + // since the newTemplates should never return null + // (it might have thrown an exception, but we don't care) + m_stylesheetReady = true; + + long[] times = getTimeArray(); + times[IDX_OVERALL] = xslBuild + xslRead; + times[IDX_XSLREAD] = xslRead; + times[IDX_XSLBUILD] = xslBuild; + return times; + } + + + /** + * Transform supplied xmlName file with a pre-built/pre-compiled + * stylesheet into a resultName file. + * + * User must have called buildStylesheet(xslName) beforehand, + * obviously. + * Names are assumed to be local path\filename references, and + * will be converted to URLs as needed. + * + * @param xmlName local path\filename of XML file to transform + * @param resultName local path\filename to put result in + * + * @return array of longs denoting timing of all parts of + * our operation: IDX_OVERALL, + * IDX_XMLREAD, IDX_TRANSFORM, IDX_RESULTWRITE + * + * @throws Exception any underlying exceptions from the + * wrappered processor are simply allowed to propagate; throws + * a RuntimeException if any other problems prevent us from + * actually completing the operation; throws an + * IllegalStateException if isStylesheetReady() == false. + * + * @see #buildStylesheet(String xslName) + */ + public long[] transformWithStylesheet(String xmlName, String resultName) + throws Exception + { + if (!isStylesheetReady()) + throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false"); + + preventFootShooting(); + long getTransformer = 0; // This is timed in DataPower's xsltMark + long startTime = 0; + long xslRead = 0; + long xslBuild = 0; + long xmlRead = 0; + long transform = 0; + long resultWrite = 0; + + // Timed: get Transformer from Templates + startTime = System.currentTimeMillis(); + Transformer transformer = builtTemplates.newTransformer(); + transformer.setErrorListener(new DefaultErrorHandler()); + getTransformer = System.currentTimeMillis() - startTime; + + File xmlFile = new File(xmlName); + int xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow + byte[] xmlBytes = new byte[xmlLength]; + FileInputStream xmlStream = new FileInputStream(xmlFile); + // Timed: read xml into a byte array + startTime = System.currentTimeMillis(); + int xmlRetVal = xmlStream.read(xmlBytes); + xmlRead = System.currentTimeMillis() - startTime; + + // Untimed: create StreamSource and setSystemId + StreamSource xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes)); + xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName)); + + // Untimed: create StreamResult + ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + StreamResult byteResult = new StreamResult(outBytes); + + // Untimed: Set any of our options as Attributes on the transformer + TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); + + // Untimed: Apply any parameters needed + applyParameters(transformer); + + // Timed: build xml (so to speak) and transform + startTime = System.currentTimeMillis(); + transformer.transform(xmlSource, byteResult); + transform = System.currentTimeMillis() - startTime; + + // Timed: writeResults from the byte array + startTime = System.currentTimeMillis(); + byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not? + FileOutputStream writeStream = new FileOutputStream(resultName); + writeStream.write(writeBytes); + writeStream.close(); + resultWrite = System.currentTimeMillis() - startTime; + + long[] times = getTimeArray(); + times[IDX_OVERALL] = getTransformer + xmlRead + transform + resultWrite; + times[IDX_XMLREAD] = xmlRead; + times[IDX_TRANSFORM] = getTransformer + xmlRead + transform; + times[IDX_RESULTWRITE] = resultWrite; + return times; + } + + + /** + * Transform supplied xmlName file with a stylesheet found in an + * xml-stylesheet PI into a resultName file. + * + * Names are assumed to be local path\filename references, and + * will be converted to URLs as needed. Implementations will + * use whatever facilities exist in their wrappered processor + * to fetch and build the stylesheet to use for the transform. + * + * @param xmlName local path\filename of XML file to transform + * @param resultName local path\filename to put result in + * + * @return array of longs denoting timing of only these parts of + * our operation: IDX_OVERALL, IDX_XSLREAD (time to find XSL + * reference from the xml-stylesheet PI), IDX_XSLBUILD, (time + * to then build the Transformer therefrom), IDX_TRANSFORM, + * IDX_XMLREAD, IDX_RESULTWRITE + * + * @throws Exception any underlying exceptions from the + * wrappered processor are simply allowed to propagate; throws + * a RuntimeException if any other problems prevent us from + * actually completing the operation + */ + public long[] transformEmbedded(String xmlName, String resultName) + throws Exception + { + preventFootShooting(); + long startTime = 0; + long xslRead = 0; + long xslBuild = 0; + long xmlRead = 0; + long transform = 0; + long resultWrite = 0; + + + File xmlFile = new File(xmlName); + int xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow + byte[] xmlBytes = new byte[xmlLength]; + FileInputStream xmlStream = new FileInputStream(xmlFile); + // Timed: read xml into a byte array + startTime = System.currentTimeMillis(); + int xmlRetVal = xmlStream.read(xmlBytes); + xmlRead = System.currentTimeMillis() - startTime; + + // Untimed: create StreamSource and setSystemId + StreamSource xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes)); + xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName)); + + // Timed: readxsl from the xml document + // Should this be timed as something? + startTime = System.currentTimeMillis(); + Source xslSource = factory.getAssociatedStylesheet(xmlSource, null, null, null); + xslRead = System.currentTimeMillis() - startTime; + + // Timed: build xsl from a URL + startTime = System.currentTimeMillis(); + Transformer transformer = factory.newTransformer(xslSource); + transformer.setErrorListener(new DefaultErrorHandler()); + xslBuild = System.currentTimeMillis() - startTime; + + // Re-read the XML file for use in transform; not timed + xmlFile = new File(xmlName); + xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow + xmlBytes = new byte[xmlLength]; + xmlStream = new FileInputStream(xmlFile); + xmlRetVal = xmlStream.read(xmlBytes); + + // Untimed: create StreamSource and setSystemId + xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes)); + xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName)); + + // Untimed: create StreamResult + ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + StreamResult byteResult = new StreamResult(outBytes); + + // Untimed: Set any of our options as Attributes on the transformer + TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); + + // Untimed: Apply any parameters needed + applyParameters(transformer); + + // Timed: build xml (so to speak) and transform + startTime = System.currentTimeMillis(); + transformer.transform(xmlSource, byteResult); + transform = System.currentTimeMillis() - startTime; + + // Timed: writeResults from the byte array + startTime = System.currentTimeMillis(); + byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not? + FileOutputStream writeStream = new FileOutputStream(resultName); + writeStream.write(writeBytes); + writeStream.close(); + resultWrite = System.currentTimeMillis() - startTime; + + long[] times = getTimeArray(); + times[IDX_OVERALL] = xslRead + xslBuild + xmlRead + + transform + resultWrite; + times[IDX_XSLREAD] = xslRead; + times[IDX_XSLBUILD] = xslBuild; + times[IDX_XMLREAD] = xmlRead; + times[IDX_TRANSFORM] = transform; + times[IDX_RESULTWRITE] = resultWrite; + return times; + + } + + + /** + * Reset our parameters and wrapper state, and optionally + * force creation of a new underlying processor implementation. + * + * This always clears our built stylesheet and any parameters + * that have been set. If newProcessor is true, also forces a + * re-creation of our underlying processor as if by calling + * newProcessor(). + * + * @param newProcessor if we should reset our underlying + * processor implementation as well + */ + public void reset(boolean newProcessor) + { + super.reset(newProcessor); // clears indent and parameters + m_stylesheetReady = false; + builtTemplates = null; + if (newProcessor) + { + try + { + newProcessor(newProcessorOpts); + } + catch (Exception e) + { + //@todo Hmm: what should we do here? + } + } + } + + + /** + * Apply a single parameter to a Transformer. + * + * Overridden to take a Transformer and call setParameter(). + * + * @param passThru to be passed to each applyParameter() method + * call - for TrAX, you might pass a Transformer object. + * @param namespace for the parameter, may be null + * @param name for the parameter, should not be null + * @param value for the parameter, may be null + */ + protected void applyParameter(Object passThru, String namespace, + String name, Object value) + { + try + { + Transformer t = (Transformer)passThru; + // Munge the namespace into the name per + // javax.xml.transform.Transformer.setParameter() + if (null != namespace) + { + name = "{" + namespace + "}" + name; + } + t.setParameter(name, value); + } + catch (Exception e) + { + throw new IllegalArgumentException("applyParameter threw: " + e.toString()); + } + } + + + /** + * Ensure newProcessor has been called when needed. + * + * Prevent users from shooting themselves in the foot by + * calling a transform* API before newProcessor(). + * + * (Sorry, I couldn't resist) + */ + public void preventFootShooting() throws Exception + { + if (null == factory) + newProcessor(newProcessorOpts); + } +} |