• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id$
20  */
21 package org.apache.qetest.xslwrapper;
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.util.Hashtable;
28 import java.util.Properties;
29 
30 import javax.xml.transform.Source;
31 import javax.xml.transform.Templates;
32 import javax.xml.transform.Transformer;
33 import javax.xml.transform.TransformerConfigurationException;
34 import javax.xml.transform.TransformerFactory;
35 import javax.xml.transform.stream.StreamResult;
36 import javax.xml.transform.stream.StreamSource;
37 
38 import org.apache.qetest.QetestUtils;
39 import org.apache.xml.utils.DefaultErrorHandler;
40 
41 /**
42  * Implementation of TransformWrapper that uses the TrAX API and
43  * uses in-memory streams for it's sources.
44  *
45  * <p>This implementation separates the process of reading xml and xsl
46  * files from disk into byte arrays out from the time processing of
47  * a new StreamSource(byte[]) takes to build a stylesheet.
48  * It also separates the time of performing the transformation
49  * to a StreamResult(byte[]) from the time spent simply sending
50  * the byte[] through a FileOutputStream to disk.</p>
51  *
52  * <p><b>Important!</b>  The underlying System property of
53  * javax.xml.transform.TransformerFactory will determine the actual
54  * TrAX implementation used.  This value will be reported out in
55  * our getProcessorInfo() method.</p>
56  *
57  * <p>//@todo: currently limited to reading in files whose length
58  * will fit into an int value.  The actual file reading routine
59  * should be updated in several of our methods.</p>
60  *
61  * @author Shane Curcuru
62  * @version $Id$
63  */
64 public class TraxStreamWrapper extends TransformWrapperHelper
65 {
66 
67     /**
68      * TransformerFactory to use; constructed in newProcessor().
69      */
70     protected TransformerFactory factory = null;
71 
72 
73     /**
74      * Templates to use for buildStylesheet().
75      */
76     protected Templates builtTemplates = null;
77 
78 
79     /**
80      * Cached copy of newProcessor() Hashtable.
81      */
82     protected Hashtable newProcessorOpts = null;
83 
84 
85     /**
86      * Get a general description of this wrapper itself.
87      *
88      * @return Uses TrAX to perform transforms from StreamSource(systemId)
89      */
getDescription()90     public String getDescription()
91     {
92         return "Uses TrAX to perform transforms from StreamSource(stream)";
93     }
94 
95 
96     /**
97      * Get a specific description of the wrappered processor.
98      *
99      * @return specific description of the underlying processor or
100      * transformer implementation: this should include both the
101      * general product name, as well as specific version info.  If
102      * possible, should be implemented without actively creating
103      * an underlying processor.
104      */
getProcessorInfo()105     public Properties getProcessorInfo()
106     {
107         Properties p = TraxWrapperUtils.getTraxInfo();
108         p.put("traxwrapper.method", "streams");
109         p.put("traxwrapper.desc", getDescription());
110         return p;
111     }
112 
113 
114     /**
115      * Actually create/initialize an underlying processor or factory.
116      *
117      * For TrAX/javax.xml.transform implementations, this creates
118      * a new TransformerFactory.
119      *
120      * @param options Hashtable of options, unused.
121      *
122      * @return (Object)getProcessor() as a side-effect, this will
123      * be null if there was any problem creating the processor OR
124      * if the underlying implementation doesn't use this
125      *
126      * @throws Exception covers any underlying exceptions thrown
127      * by the actual implementation
128      */
newProcessor(Hashtable options)129     public Object newProcessor(Hashtable options) throws Exception
130     {
131         newProcessorOpts = options;
132         //@todo do we need to do any other cleanup?
133         reset(false);
134         factory = TransformerFactory.newInstance();
135         factory.setErrorListener(new DefaultErrorHandler());
136         // Verify the factory supports Streams!
137         if (!(factory.getFeature(StreamSource.FEATURE)
138               && factory.getFeature(StreamResult.FEATURE)))
139         {
140             throw new TransformerConfigurationException("TraxStreamWrapper.newProcessor: factory does not support Streams!");
141         }
142         // Set any of our options as Attributes on the factory
143         TraxWrapperUtils.setAttributes(factory, options);
144         return (Object)factory;
145     }
146 
147 
148     /**
149      * Transform supplied xmlName file with the stylesheet in the
150      * xslName file into a resultName file.
151      *
152      * Names are assumed to be local path\filename references, and
153      * will be read as byte streams before being passed to
154      * underlying StreamSources, etc.
155      *
156      * @param xmlName local path\filename of XML file to transform
157      * @param xslName local path\filename of XSL stylesheet to use
158      * @param resultName local path\filename to put result in
159      *
160      * @return array of longs denoting timing of all parts of
161      * our operation: IDX_OVERALL, IDX_XSLREAD, IDX_XSLBUILD,
162      * IDX_XMLREAD, IDX_TRANSFORM, IDX_RESULTWRITE
163      *
164      * @throws Exception any underlying exceptions from the
165      * wrappered processor are simply allowed to propagate; throws
166      * a RuntimeException if any other problems prevent us from
167      * actually completing the operation
168      */
transform(String xmlName, String xslName, String resultName)169     public long[] transform(String xmlName, String xslName, String resultName)
170         throws Exception
171     {
172         preventFootShooting();
173         long startTime = 0;
174         long xslRead = 0;
175         long xslBuild = 0;
176         long xmlRead = 0;
177         long transform = 0;
178         long resultWrite = 0;
179 
180         File xslFile = new File(xslName);
181         int xslLength = new Long(xslFile.length()).intValue(); //@todo warning: possible overflow
182         byte[] xslBytes = new byte[xslLength];
183         FileInputStream xslStream = new FileInputStream(xslFile);
184         // Timed: read xsl into a byte array
185         startTime = System.currentTimeMillis();
186         int xslRetVal = xslStream.read(xslBytes);
187         xslRead = System.currentTimeMillis() - startTime;
188 
189         // Untimed: create StreamSource and setSystemId
190         StreamSource xslSource = new StreamSource(new ByteArrayInputStream(xslBytes));
191         // Note that systemIds must be a legal URI
192         xslSource.setSystemId(QetestUtils.filenameToURL(xslName));
193 
194         // Timed: build Transformer from StreamSource
195         startTime = System.currentTimeMillis();
196         Transformer transformer = factory.newTransformer(xslSource);
197         transformer.setErrorListener(new DefaultErrorHandler());
198         xslBuild = System.currentTimeMillis() - startTime;
199 
200         File xmlFile = new File(xmlName);
201         int xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow
202         byte[] xmlBytes = new byte[xmlLength];
203         FileInputStream xmlStream = new FileInputStream(xmlFile);
204         // Timed: read xml into a byte array
205         startTime = System.currentTimeMillis();
206         int xmlRetVal = xmlStream.read(xmlBytes);
207         xmlRead = System.currentTimeMillis() - startTime;
208 
209         // Untimed: create StreamSource and setSystemId
210         StreamSource xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes));
211         xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName));
212 
213         // Untimed: create StreamResult
214         ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
215         StreamResult byteResult = new StreamResult(outBytes);
216 
217         // Untimed: Set any of our options as Attributes on the transformer
218         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
219 
220         // Untimed: Apply any parameters needed
221         applyParameters(transformer);
222 
223         // Timed: build xml (so to speak) and transform
224         startTime = System.currentTimeMillis();
225         transformer.transform(xmlSource, byteResult);
226         transform = System.currentTimeMillis() - startTime;
227 
228         // Timed: writeResults from the byte array
229         startTime = System.currentTimeMillis();
230         byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not?
231         FileOutputStream writeStream = new FileOutputStream(resultName);
232         writeStream.write(writeBytes);
233         writeStream.close();
234         resultWrite = System.currentTimeMillis() - startTime;
235 
236         long[] times = getTimeArray();
237         times[IDX_OVERALL] = xslRead + xslBuild + xmlRead
238                              + transform + resultWrite;
239         times[IDX_XSLREAD] = xslRead;
240         times[IDX_XSLBUILD] = xslBuild;
241         times[IDX_XMLREAD] = xmlRead;
242         times[IDX_TRANSFORM] = transform;
243         times[IDX_RESULTWRITE] = resultWrite;
244         return times;
245     }
246 
247 
248     /**
249      * Pre-build/pre-compile a stylesheet.
250      *
251      * Although the actual mechanics are implementation-dependent,
252      * most processors have some method of pre-setting up the data
253      * needed by the stylesheet itself for later use in transforms.
254      * In TrAX/javax.xml.transform, this equates to creating a
255      * Templates object.
256      *
257      * Sets isStylesheetReady() to true if it succeeds.  Users can
258      * then call transformWithStylesheet(xmlName, resultName) to
259      * actually perform a transformation with this pre-built
260      * stylesheet.
261      *
262      * @param xslName local path\filename of XSL stylesheet to use
263      *
264      * @return array of longs denoting timing of all parts of
265      * our operation: IDX_OVERALL, IDX_XSLREAD, IDX_XSLBUILD
266      *
267      * @throws Exception any underlying exceptions from the
268      * wrappered processor are simply allowed to propagate; throws
269      * a RuntimeException if any other problems prevent us from
270      * actually completing the operation
271      *
272      * @see #transformWithStylesheet(String xmlName, String resultName)
273      */
buildStylesheet(String xslName)274     public long[] buildStylesheet(String xslName) throws Exception
275     {
276         preventFootShooting();
277         long startTime = 0;
278         long xslRead = 0;
279         long xslBuild = 0;
280 
281         File xslFile = new File(xslName);
282         int xslLength = new Long(xslFile.length()).intValue(); //@todo warning: possible overflow
283         byte[] xslBytes = new byte[xslLength];
284         FileInputStream xslStream = new FileInputStream(xslFile);
285         // Timed: read xsl into a byte array
286         startTime = System.currentTimeMillis();
287         int xslRetVal = xslStream.read(xslBytes);
288         xslRead = System.currentTimeMillis() - startTime;
289 
290         // Untimed: create StreamSource and setSystemId
291         StreamSource xslSource = new StreamSource(new ByteArrayInputStream(xslBytes));
292         // Note that systemIds must be a legal URI
293         xslSource.setSystemId(QetestUtils.filenameToURL(xslName));
294 
295         // Timed: build Transformer from StreamSource
296         startTime = System.currentTimeMillis();
297         builtTemplates = factory.newTemplates(xslSource);
298         xslBuild = System.currentTimeMillis() - startTime;
299 
300         // Set internal state that we have a templates ready
301         //  Note: in theory, there's no need to check builtTemplates
302         //  since the newTemplates should never return null
303         //  (it might have thrown an exception, but we don't care)
304         m_stylesheetReady = true;
305 
306         long[] times = getTimeArray();
307         times[IDX_OVERALL] = xslBuild + xslRead;
308         times[IDX_XSLREAD] = xslRead;
309         times[IDX_XSLBUILD] = xslBuild;
310         return times;
311     }
312 
313 
314     /**
315      * Transform supplied xmlName file with a pre-built/pre-compiled
316      * stylesheet into a resultName file.
317      *
318      * User must have called buildStylesheet(xslName) beforehand,
319      * obviously.
320      * Names are assumed to be local path\filename references, and
321      * will be converted to URLs as needed.
322      *
323      * @param xmlName local path\filename of XML file to transform
324      * @param resultName local path\filename to put result in
325      *
326      * @return array of longs denoting timing of all parts of
327      * our operation: IDX_OVERALL,
328      * IDX_XMLREAD, IDX_TRANSFORM, IDX_RESULTWRITE
329      *
330      * @throws Exception any underlying exceptions from the
331      * wrappered processor are simply allowed to propagate; throws
332      * a RuntimeException if any other problems prevent us from
333      * actually completing the operation; throws an
334      * IllegalStateException if isStylesheetReady() == false.
335      *
336      * @see #buildStylesheet(String xslName)
337      */
transformWithStylesheet(String xmlName, String resultName)338     public long[] transformWithStylesheet(String xmlName, String resultName)
339         throws Exception
340     {
341         if (!isStylesheetReady())
342             throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false");
343 
344         preventFootShooting();
345 		long getTransformer = 0; // This is timed in DataPower's xsltMark
346         long startTime = 0;
347         long xslRead = 0;
348         long xslBuild = 0;
349         long xmlRead = 0;
350         long transform = 0;
351         long resultWrite = 0;
352 
353         // Timed: get Transformer from Templates
354 		startTime = System.currentTimeMillis();
355         Transformer transformer = builtTemplates.newTransformer();
356         transformer.setErrorListener(new DefaultErrorHandler());
357 	getTransformer = System.currentTimeMillis() - startTime;
358 
359         File xmlFile = new File(xmlName);
360         int xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow
361         byte[] xmlBytes = new byte[xmlLength];
362         FileInputStream xmlStream = new FileInputStream(xmlFile);
363         // Timed: read xml into a byte array
364         startTime = System.currentTimeMillis();
365         int xmlRetVal = xmlStream.read(xmlBytes);
366         xmlRead = System.currentTimeMillis() - startTime;
367 
368         // Untimed: create StreamSource and setSystemId
369         StreamSource xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes));
370         xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName));
371 
372         // Untimed: create StreamResult
373         ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
374         StreamResult byteResult = new StreamResult(outBytes);
375 
376         // Untimed: Set any of our options as Attributes on the transformer
377         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
378 
379         // Untimed: Apply any parameters needed
380         applyParameters(transformer);
381 
382         // Timed: build xml (so to speak) and transform
383         startTime = System.currentTimeMillis();
384         transformer.transform(xmlSource, byteResult);
385         transform = System.currentTimeMillis() - startTime;
386 
387         // Timed: writeResults from the byte array
388         startTime = System.currentTimeMillis();
389         byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not?
390         FileOutputStream writeStream = new FileOutputStream(resultName);
391         writeStream.write(writeBytes);
392         writeStream.close();
393         resultWrite = System.currentTimeMillis() - startTime;
394 
395         long[] times = getTimeArray();
396         times[IDX_OVERALL] = getTransformer + xmlRead + transform + resultWrite;
397         times[IDX_XMLREAD] = xmlRead;
398         times[IDX_TRANSFORM] = getTransformer + xmlRead + transform;
399         times[IDX_RESULTWRITE] = resultWrite;
400         return times;
401     }
402 
403 
404     /**
405      * Transform supplied xmlName file with a stylesheet found in an
406      * xml-stylesheet PI into a resultName file.
407      *
408      * Names are assumed to be local path\filename references, and
409      * will be converted to URLs as needed.  Implementations will
410      * use whatever facilities exist in their wrappered processor
411      * to fetch and build the stylesheet to use for the transform.
412      *
413      * @param xmlName local path\filename of XML file to transform
414      * @param resultName local path\filename to put result in
415      *
416      * @return array of longs denoting timing of only these parts of
417      * our operation: IDX_OVERALL, IDX_XSLREAD (time to find XSL
418      * reference from the xml-stylesheet PI), IDX_XSLBUILD, (time
419      * to then build the Transformer therefrom), IDX_TRANSFORM,
420      * IDX_XMLREAD, IDX_RESULTWRITE
421      *
422      * @throws Exception any underlying exceptions from the
423      * wrappered processor are simply allowed to propagate; throws
424      * a RuntimeException if any other problems prevent us from
425      * actually completing the operation
426      */
transformEmbedded(String xmlName, String resultName)427     public long[] transformEmbedded(String xmlName, String resultName)
428         throws Exception
429     {
430         preventFootShooting();
431         long startTime = 0;
432         long xslRead = 0;
433         long xslBuild = 0;
434         long xmlRead = 0;
435         long transform = 0;
436         long resultWrite = 0;
437 
438 
439         File xmlFile = new File(xmlName);
440         int xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow
441         byte[] xmlBytes = new byte[xmlLength];
442         FileInputStream xmlStream = new FileInputStream(xmlFile);
443         // Timed: read xml into a byte array
444         startTime = System.currentTimeMillis();
445         int xmlRetVal = xmlStream.read(xmlBytes);
446         xmlRead = System.currentTimeMillis() - startTime;
447 
448         // Untimed: create StreamSource and setSystemId
449         StreamSource xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes));
450         xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName));
451 
452         // Timed: readxsl from the xml document
453         // Should this be timed as something?
454         startTime = System.currentTimeMillis();
455         Source xslSource = factory.getAssociatedStylesheet(xmlSource, null, null, null);
456         xslRead = System.currentTimeMillis() - startTime;
457 
458         // Timed: build xsl from a URL
459         startTime = System.currentTimeMillis();
460         Transformer transformer = factory.newTransformer(xslSource);
461         transformer.setErrorListener(new DefaultErrorHandler());
462         xslBuild = System.currentTimeMillis() - startTime;
463 
464         // Re-read the XML file for use in transform; not timed
465         xmlFile = new File(xmlName);
466         xmlLength = new Long(xmlFile.length()).intValue(); //@todo warning: possible overflow
467         xmlBytes = new byte[xmlLength];
468         xmlStream = new FileInputStream(xmlFile);
469         xmlRetVal = xmlStream.read(xmlBytes);
470 
471         // Untimed: create StreamSource and setSystemId
472         xmlSource = new StreamSource(new ByteArrayInputStream(xmlBytes));
473         xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName));
474 
475         // Untimed: create StreamResult
476         ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
477         StreamResult byteResult = new StreamResult(outBytes);
478 
479         // Untimed: Set any of our options as Attributes on the transformer
480         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
481 
482         // Untimed: Apply any parameters needed
483         applyParameters(transformer);
484 
485         // Timed: build xml (so to speak) and transform
486         startTime = System.currentTimeMillis();
487         transformer.transform(xmlSource, byteResult);
488         transform = System.currentTimeMillis() - startTime;
489 
490         // Timed: writeResults from the byte array
491         startTime = System.currentTimeMillis();
492         byte[] writeBytes = outBytes.toByteArray(); // Should this be timed too or not?
493         FileOutputStream writeStream = new FileOutputStream(resultName);
494         writeStream.write(writeBytes);
495         writeStream.close();
496         resultWrite = System.currentTimeMillis() - startTime;
497 
498         long[] times = getTimeArray();
499         times[IDX_OVERALL] = xslRead + xslBuild + xmlRead
500                              + transform + resultWrite;
501         times[IDX_XSLREAD] = xslRead;
502         times[IDX_XSLBUILD] = xslBuild;
503         times[IDX_XMLREAD] = xmlRead;
504         times[IDX_TRANSFORM] = transform;
505         times[IDX_RESULTWRITE] = resultWrite;
506         return times;
507 
508     }
509 
510 
511     /**
512      * Reset our parameters and wrapper state, and optionally
513      * force creation of a new underlying processor implementation.
514      *
515      * This always clears our built stylesheet and any parameters
516      * that have been set.  If newProcessor is true, also forces a
517      * re-creation of our underlying processor as if by calling
518      * newProcessor().
519      *
520      * @param newProcessor if we should reset our underlying
521      * processor implementation as well
522      */
reset(boolean newProcessor)523     public void reset(boolean newProcessor)
524     {
525         super.reset(newProcessor); // clears indent and parameters
526         m_stylesheetReady = false;
527         builtTemplates = null;
528         if (newProcessor)
529         {
530             try
531             {
532                 newProcessor(newProcessorOpts);
533             }
534             catch (Exception e)
535             {
536                 //@todo Hmm: what should we do here?
537             }
538         }
539     }
540 
541 
542     /**
543      * Apply a single parameter to a Transformer.
544      *
545      * Overridden to take a Transformer and call setParameter().
546      *
547      * @param passThru to be passed to each applyParameter() method
548      * call - for TrAX, you might pass a Transformer object.
549      * @param namespace for the parameter, may be null
550      * @param name for the parameter, should not be null
551      * @param value for the parameter, may be null
552      */
applyParameter(Object passThru, String namespace, String name, Object value)553     protected void applyParameter(Object passThru, String namespace,
554                                   String name, Object value)
555     {
556         try
557         {
558             Transformer t = (Transformer)passThru;
559             // Munge the namespace into the name per
560             //  javax.xml.transform.Transformer.setParameter()
561             if (null != namespace)
562             {
563                 name = "{" + namespace + "}" + name;
564             }
565             t.setParameter(name, value);
566         }
567         catch (Exception e)
568         {
569             throw new IllegalArgumentException("applyParameter threw: " + e.toString());
570         }
571     }
572 
573 
574     /**
575      * Ensure newProcessor has been called when needed.
576      *
577      * Prevent users from shooting themselves in the foot by
578      * calling a transform* API before newProcessor().
579      *
580      * (Sorry, I couldn't resist)
581      */
preventFootShooting()582     public void preventFootShooting() throws Exception
583     {
584         if (null == factory)
585             newProcessor(newProcessorOpts);
586     }
587 }
588