| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2001-2003 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999, Lotus |
| * Development Corporation., http://www.lotus.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| 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; |
| |
| /** |
| * 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(); |
| // 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); |
| 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(); |
| 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); |
| 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); |
| } |
| } |