• 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 
22 /*
23  *
24  * ThreadedStylesheetTestlet.java
25  *
26  */
27 package org.apache.qetest.xsl;
28 
29 import java.io.File;
30 
31 import org.apache.qetest.CheckService;
32 import org.apache.qetest.Datalet;
33 import org.apache.qetest.Logger;
34 import org.apache.qetest.TestletImpl;
35 import org.apache.qetest.xslwrapper.TransformWrapper;
36 import org.apache.qetest.xslwrapper.TransformWrapperFactory;
37 
38 /**
39  * Testlet for basic thread testing of xsl stylesheet files.
40  *
41  * This class provides a simple testing algorithim for verifying
42  * that Xalan functions properly when run on multiple threads
43  * simultaneously.  Currently it simply does most of what the
44  * normal StylesheetTestlet does, except it does it in the run()
45  * method on a thread - thus you'd commonly use this with
46  * ThreadedTestletDriver to start multiple of these testlets
47  * up at the same time.
48  * We implement Runnable, so classically a test driver would
49  * create us then pass us to new Thread(us).start(), instead
50  * of calling execute(). @todo find a better way to integrate!
51  *
52  * @author Shane_Curcuru@lotus.com
53  * @version $Id$
54  */
55 public class ThreadedStylesheetTestlet
56         extends TestletImpl
57         implements Runnable
58 {
59     // Initialize our classname for TestletImpl's main() method
60     static { thisClassName = "org.apache.qetest.xsl.ThreadedStylesheetTestlet"; }
61 
62     // Initialize our defaultDatalet
63     { defaultDatalet = (Datalet)new StylesheetDatalet(); }
64 
65     /** Accessor for our Datalet instead of calling execute().  */
setDefaultDatalet(StylesheetDatalet d)66     public void setDefaultDatalet(StylesheetDatalet d)
67     {
68         defaultDatalet = d;
69     }
70 
71     /* Special ThreadedStylesheetDatalet with a Templates.  */
72     public ThreadedStylesheetDatalet sharedDatalet = new ThreadedStylesheetDatalet();
73 
74     /* Description of our current state; changes during our lifecycle.  */
75     protected String description = "ThreadedStylesheetTestlet - before execute()";
76 
77     /**
78      * Accesor method for a brief description of this test.
79      * When created, this returns a description of this testlet.
80      * After you've called execute(), this returns a brief
81      * description of our current result or status.
82      *
83      * @return String describing what this ThreadedStylesheetTestlet does.
84      * @see #getResult()
85      */
getDescription()86     public String getDescription()
87     {
88         return description;
89     }
90 
91     /**
92      * Accesor method for a brief description of this test.
93      * Automatically adds our identifier at the start.
94      *
95      * @param d String to set as our current description.
96      */
setDescription(String d)97     protected void setDescription(String d)
98     {
99         description = "[" + threadIdentifier + "]" + d;
100     }
101 
102     /* Our 'final' test result; actually changes during our lifecycle.  */
103     protected int result = Logger.DEFAULT_RESULT;
104 
105     /**
106      * Accesor method for the final result of this test.
107      * Note: this starts as INCP_RESULT, and given that we're
108      * threaded, may end up as INCP_RESULT and you may not know
109      * the difference.  Could use more thought.
110      *
111      * @return int one of of Logger.*_RESULT.
112      */
getResult()113     public int getResult()
114     {
115         return result;
116     }
117 
118     /* Cheap-o counter: so driver can differentiate each thread.  */
119     public int threadIdentifier = 0;
120 
121     /**
122      * Run this ThreadedStylesheetTestlet: start this test as
123      * a thread and return immediately.
124      * Note that you must join() this thread later if you want
125      * to wait until we're done.
126      * //@todo improve docs on how to communicate between threads
127      *
128      * @param Datalet to use as data point for the test.
129      */
execute(Datalet d)130     public void execute(Datalet d)
131 	{
132         StylesheetDatalet datalet = null;
133         try
134         {
135             datalet = (StylesheetDatalet)d;
136         }
137         catch (ClassCastException e)
138         {
139             logger.checkErr("Datalet provided is not a StylesheetDatalet; cannot continue with " + d);
140             return;
141         }
142 
143         logger.logMsg(Logger.STATUSMSG, "About to test: "
144                       + (null == datalet.inputName
145                          ? datalet.xmlName
146                          : datalet.inputName)
147                       + " plus " + sharedDatalet.xmlName);
148 
149         // All the rest of the test is executed in our thread.
150         //if (true) //@todo check defaultDatalet.options...
151         //    this.start();
152         //@todo Um, how do we do this? Or do we just ask caller to do it?
153         logger.logMsg(Logger.CRITICALMSG, "//@todo execute() is not yet implemented - you must start our thread yourself");
154 
155         // Return to caller; they must join() us later if they
156         //  want to know when we're actually complete
157         return;
158     }
159 
160     /**
161      * Called by execute() to perform the looping and actual test.
162      * Note: You must have set our defaultDatalet first!
163      */
run()164     public void run()
165     {
166         // Relies on defaultDatalet being set!
167         logger.logMsg(Logger.STATUSMSG, "Beginning thread shared output into: "
168                       + ((StylesheetDatalet)defaultDatalet).outputName);
169         // Also set our description so outside users know what
170         // point in our Thread lifetime we're at
171         setDescription("ThreadedStylesheetTestlet.run() just started...");
172 
173         StylesheetDatalet datalet = null;
174         try
175         {
176             datalet = (StylesheetDatalet)defaultDatalet;
177         }
178         catch (ClassCastException e)
179         {
180             setDescription("Datalet provided is not a StylesheetDatalet; cannot continue with " + datalet);
181             logger.checkErr(description);
182             return;
183         }
184         //@todo validate our Datalet - ensure it has valid
185         //  and/or existing files available.
186 
187         // Cleanup outName(s) only if asked to - delete the file on disk
188         // Optimization: this takes extra time and often is not
189         //  needed, so only do this if the option is set
190         if ("true".equalsIgnoreCase(datalet.options.getProperty("deleteOutFile")))
191         {
192             try
193             {
194                 boolean btmp = (new File(datalet.outputName)).delete();
195                 logger.logMsg(Logger.TRACEMSG, "Deleting OutFile of::" + datalet.outputName
196                                      + " status: " + btmp);
197             }
198             catch (SecurityException se)
199             {
200                 logger.logMsg(Logger.WARNINGMSG, "Deleting OutFile of::" + datalet.outputName
201                                        + " threw: " + se.toString());
202                 // But continue anyways...
203             }
204             //@todo make sure all sharedDatalets use different
205             //  output files! No sense in having them use
206             //  the same file all the time in all threads!
207         }
208 
209         // Ask our independent datalet how many iterations to use
210         int iterations = sharedDatalet.iterations; // default value
211         try
212         {
213             iterations = Integer.parseInt(datalet.options.getProperty("iterations"));
214         }
215         catch (NumberFormatException numEx)
216         {
217             // no-op; leave as default
218         }
219 
220         // Now loop a number of times as specified, and for each
221         //  loop, use the presupplied Templates object, then run
222         //  one independent process, plus validate on first & last
223         setDescription("...about to iterate... " + datalet.outputName);
224         for (int ctr = 1; (ctr <= iterations); ctr++)
225         {
226             // Only validate on first and last iteration
227             boolean doValidation = ((1 == ctr) || (iterations == ctr));
228             logger.logMsg(Logger.TRACEMSG, "About to do iteration " + ctr);
229             // Note: logic moved to worker methods for clarity;
230             //  these methods just use our local vars and datalet
231             //@todo Note: while I've tried to mirror as much
232             //  structure from StylesheetTestlet as possible,
233             //  this has made this class very inefficent (note
234             //  we iterate, and within each method do the same
235             //  name munging and some basic setup in each worker
236             //  method every time!)
237             // Long-term, we should just redesign this to be
238             //  a custom class with it's own algorithim
239             processExistingTemplates(sharedDatalet, doValidation);
240             processNewStylesheet(datalet, doValidation);
241             setDescription("...done iteration # " + ctr);
242         }
243         // That's it! We're done!
244         logger.logMsg(Logger.STATUSMSG, "Completed thread with: " + datalet.getDescription());
245         setDescription("All iterations complete! " + datalet.outputName);
246         // Also set our result, now that we think we're done
247         try
248         {
249             result = ((org.apache.qetest.Reporter)logger).getCurrentCaseResult();
250         }
251         catch (ClassCastException cce)
252         {
253             // Basically a no-op; just log out for info
254             logger.logMsg(Logger.WARNINGMSG, "logger is not a Reporter; overall result may be incorrect!");
255         }
256 	}
257 
258 
259     /**
260      * Worker method to process premade Templates object.
261      * Uses various local variables.
262      * //@todo Should we simply propagate exceptions instead of just logging them?
263      */
processExistingTemplates(ThreadedStylesheetDatalet datalet, boolean doValidation)264     private void processExistingTemplates(ThreadedStylesheetDatalet datalet, boolean doValidation)
265     {
266         // First: use our (presumably) shared Templates object to
267         // perform a Transformation - using the existing
268         // TransformWrapper object that already has built a stylesheet
269         if (!datalet.transformWrapper.isStylesheetReady())
270         {
271             // Can't continue if the Templates is not ready
272             logger.logMsg(Logger.WARNINGMSG, "datalet shared Templates isStylesheetReady false!");
273             // Anything else we should log out here?  In case someone
274             //  care about this, don't have it fail
275             return;
276         }
277 
278         // Since the wrapper's ready (and flavor, etc. setup) then
279         //  just go ahead and ask it to transform
280         try
281         {
282             String outputName = datalet.outputName + threadIdentifier;
283 
284             //@todo Should we log a custom logElement here instead?
285             logger.logMsg(Logger.TRACEMSG, "About to test shared Templates: "
286                           + " xmlName=" + datalet.xmlName
287                           + " outputName=" + outputName
288                           + " goldName=" + datalet.goldName);
289 
290             // Simply have the wrapper do all the transforming
291             //  or processing for us - we handle either normal .xsl
292             //  stylesheet tests or just .xml embedded tests
293             long retVal = 0L;
294             // Here, we only use the existing Templates to do the transform
295             long[] times = datalet.transformWrapper.transformWithStylesheet(datalet.xmlName, outputName);
296             retVal = times[TransformWrapper.IDX_OVERALL];
297 
298             if (!doValidation)
299             {
300                 logger.logMsg(Logger.TRACEMSG, "Skipping validation of outputName=" + outputName);
301                 // Only bother to validate the output if asked
302                 return;
303             }
304             // If we get here, attempt to validate the contents of
305             //  the last outputFile created - only first and last time through loop!
306             CheckService fileChecker = (CheckService)datalet.options.get("fileCheckerImpl");
307             // Supply default value
308             if (null == fileChecker)
309                 fileChecker = new XHTFileCheckService();
310             fileChecker.check(logger,
311                               new File(outputName),
312                               new File(datalet.goldName),
313                               "Shared Templates of: " + datalet.getDescription());
314         }
315         // Note that this class can only validate positive test
316         //  cases - we don't handle ExpectedExceptions
317         catch (Throwable t)
318         {
319             // Put the logThrowable first, so it appears before
320             //  the Fail record, and gets color-coded
321             logger.logThrowable(Logger.ERRORMSG, t, "Shared Templates of: " + datalet.getDescription());
322             logger.checkFail("Shared Templates of: " + datalet.getDescription()
323                              + " threw: " + t.toString());
324         }
325     }
326     /**
327      * Worker method to process a new stylesheet/xml document.
328      * Uses various local variables.
329      * Note this is essentially a copy of StylesheetTestlet.execute().
330      * //@todo Should we simply propagate exceptions instead of just logging them?
331      */
processNewStylesheet(StylesheetDatalet datalet, boolean doValidation)332     private void processNewStylesheet(StylesheetDatalet datalet, boolean doValidation)
333     {
334         // Test our supplied input file, and compare with gold
335         try
336         {
337             String outputName =  datalet.outputName + threadIdentifier;
338 
339             //@todo Should we log a custom logElement here instead?
340             logger.logMsg(Logger.TRACEMSG, "About to test: inputName=" + datalet.inputName
341                           + " xmlName=" + datalet.xmlName + " outputName=" + outputName
342                           + " goldName=" + datalet.goldName + " flavor="  + datalet.flavor);
343 
344             // Create a new TransformWrapper of appropriate flavor
345             //  null arg is unused liaison for TransformWrapper
346             TransformWrapper transformWrapper = null;
347             try
348             {
349                 transformWrapper = TransformWrapperFactory.newWrapper(datalet.flavor);
350                 transformWrapper.newProcessor(null);
351             }
352             catch (Throwable t)
353             {
354                 logger.logThrowable(Logger.ERRORMSG, t, getDescription() + " newWrapper/newProcessor threw");
355                 logger.checkErr(getDescription() + " newWrapper/newProcessor threw: " + t.toString());
356                 return;
357             }
358 
359             // Simply have the wrapper do all the transforming
360             //  or processing for us - we handle either normal .xsl
361             //  stylesheet tests or just .xml embedded tests
362             long retVal = 0L;
363             if (null == datalet.inputName)
364             {
365                 // presume it's an embedded test
366                 long [] times = transformWrapper.transformEmbedded(datalet.xmlName, outputName);
367                 retVal = times[TransformWrapper.IDX_OVERALL];
368             }
369             else
370             {
371                 // presume it's a normal stylesheet test
372                 long[] times = transformWrapper.transform(datalet.xmlName, datalet.inputName, outputName);
373                 retVal = times[TransformWrapper.IDX_OVERALL];
374             }
375 
376             if (!doValidation)
377             {
378                 logger.logMsg(Logger.TRACEMSG, "Skipping validation of outputName=" + outputName);
379                 // Only bother to validate the output if asked
380                 return;
381             }
382             // If we get here, attempt to validate the contents of
383             //  the last outputFile created
384             CheckService fileChecker = (CheckService)datalet.options.get("fileCheckerImpl");
385             // Supply default value
386             if (null == fileChecker)
387                 fileChecker = new XHTFileCheckService();
388             fileChecker.check(logger,
389                               new File(outputName),
390                               new File(datalet.goldName),
391                               getDescription() + " " + datalet.getDescription());
392         }
393         // Note that this class can only validate positive test
394         //  cases - we don't handle ExpectedExceptions
395         catch (Throwable t)
396         {
397             // Put the logThrowable first, so it appears before
398             //  the Fail record, and gets color-coded
399             logger.logThrowable(Logger.ERRORMSG, t, "New stylesheet of: " + datalet.getDescription());
400             logger.checkFail("New stylesheet of: " + datalet.getDescription()
401                              + " threw: " + t.toString());
402         }
403     }
404 
405 }  // end of class ThreadedStylesheetTestlet
406 
407