• 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  * TestThreads.java
25  *
26  */
27 package org.apache.qetest.trax;
28 
29 import java.io.File;
30 import java.io.FileInputStream;
31 import java.io.FileOutputStream;
32 import java.io.FileWriter;
33 import java.io.PrintWriter;
34 import java.util.Properties;
35 
36 import javax.xml.transform.Result;
37 import javax.xml.transform.Templates;
38 import javax.xml.transform.Transformer;
39 import javax.xml.transform.TransformerException;
40 import javax.xml.transform.TransformerFactory;
41 import javax.xml.transform.stream.StreamResult;
42 import javax.xml.transform.stream.StreamSource;
43 //-------------------------------------------------------------------------
44 
45 /**
46  * Testing multiple simultaneous processors on different threads with TRAX.
47  * <p>No validation of output files is currently done!  You must manually
48  * inspect any logfiles.  Most options can be passed in with a Properties file.</p>
49  * <p>Note: Most automated tests extend XSLProcessorTestBase, and
50  * are named *Test.java.  Since we are semi-manual, we're
51  * named Test*.java instead.</p>
52  * We assume Features.STREAM.
53  * @author shane_curcuru@lotus.com
54  */
55 public class TestThreads
56 {
57 
58     /**
59      * Convenience method to print out usage information.
60      *
61      * NEEDSDOC ($objectName$) @return
62      */
usage()63     public static String usage()
64     {
65 
66         return ("Usage: TestThreads [-load] file.properties :\n"
67                 + "    where the properties file can set:,\n"
68                 + "    inputDir=e:\\builds\\xsl-test\n"
69                 + "    outputDir=e:\\builds\\xsl-test\\results\n"
70                 + "    logFile=e:\\builds\\xsl-test\\results\\TestThreads.xml\n"
71                 + "    numRunners=5\n" + "    numRunnerCalls=10\n"
72                 + "    setOneFile=bool01\n" + "    setTwoFile=expr01\n"
73                 + "    setThreeFile=numb01\n" + "    paramName=SomeParam\n"
74                 + "    paramVal=TheValue\n");
75     }
76 
77     /** NEEDSDOC Field debug          */
78     public boolean debug = true;  // for adhoc debugging
79 
80     /**
81      * Number of sets of worker threads to create and loops per runner.
82      * <p>'numRunners=xx', default is 10; 'numRunnerCalls=xx', default is 50.</p>
83      */
84     protected int numRunners = 10;
85 
86     /**
87      * Number of sets of worker threads to create and loops per runner.
88      * <p>'numRunners=xx', default is 10; 'numRunnerCalls=xx', default is 50.</p>
89      */
90     protected int numRunnerCalls = 50;
91 
92     /**
93      * Root input filenames that certain runners should use, in the inputDir.
94      * <p>'setOneFile=File'; 'setTwoFile=File'; 'setThreeFile=File'
95      * in .prop file to set; default is TestThreads1, TestThreads2, TestThreads3.</p>
96      * <p>Files are found in 'inputDir=c:\bar\baz' from .prop file.</p>
97      */
98     protected String inputDir = null;
99 
100     /** NEEDSDOC Field setOneFilenameRoot          */
101     protected String setOneFilenameRoot = "TestThreads1";
102 
103     /** NEEDSDOC Field setTwoFilenameRoot          */
104     protected String setTwoFilenameRoot = "TestThreads2";
105 
106     /** NEEDSDOC Field setThreeFilenameRoot          */
107     protected String setThreeFilenameRoot = "TestThreads3";
108 
109     /**
110      * All output logs and files get put in the outputDir.
111      */
112     protected String outputDir = null;
113 
114     /**
115      * Sample PARAM name that certain runners should use.
116      * <p>Use 'paramName=xx' in .prop file to set, default is test1.</p>
117      */
118     protected String paramName = "test1";
119 
120     /**
121      * Sample PARAM value that certain runners should use.
122      * <p>Use 'paramVal=xx' in .prop file to set, default is bar.</p>
123      */
124     protected String paramVal = "bar";
125 
126     /**
127      * liaisonClassName that just the *second* set of runners should use.
128      * <p>Use 'liaison=xx' in .prop file to set, default is null (whatever the processor's default is).</p>
129      */
130     protected String liaison = null;  // TRAX unused
131 
132     // Used to pass info to runners; simpler to update than changing ctors
133 
134     /** RunnerID offset in ctor's array initializer.   */
135     public static final int ID = 0;
136 
137     /** NEEDSDOC Field XMLNAME          */
138     public static final int XMLNAME = 1;
139 
140     /** NEEDSDOC Field XSLNAME          */
141     public static final int XSLNAME = 2;
142 
143     /** NEEDSDOC Field OUTNAME          */
144     public static final int OUTNAME = 3;
145 
146     /** NEEDSDOC Field PARAMNAME          */
147     public static final int PARAMNAME = 4;
148 
149     /** NEEDSDOC Field PARAMVAL          */
150     public static final int PARAMVAL = 5;
151 
152     /** NEEDSDOC Field OPTIONS          */
153     public static final int OPTIONS = 6;
154 
155     /** NEEDSDOC Field LIAISON          */
156     public static final int LIAISON = 7;
157 
158     /** NEEDSDOC Field FUTUREUSE          */
159     public static final int FUTUREUSE = 8;
160 
161     /**
162      * Name of main file's output logging; each runner also has separate output.
163      */
164     protected String logFileName = "TestThreads.xml";
165 
166     /**
167      * Construct multiple threads with processors and run them all.
168      * @author Shane Curcuru & Scott Boag
169      * <p>Preprocesses some stylesheets, then creates lots of worker threads.</p>
170      */
runTest()171     public void runTest()
172     {
173 
174         // Prepare a log file and dump out some basic info
175         createLogFile(logFileName);
176         println("<?xml version=\"1.0\"?>");
177         println("<resultsfile logFile=\"" + logFileName + "\">");
178         println("<message desc=\"threads=" + (3 * numRunners)
179                 + " iterations=" + numRunnerCalls + "\"/>");
180         println("<message desc=\"oneF=" + setOneFilenameRoot + " twof="
181                 + setTwoFilenameRoot + " threef=" + setThreeFilenameRoot
182                 + "\"/>");
183         println("<message desc=\"param=" + paramName + " val=" + paramVal
184                 + " liaison=" + liaison + "\"/>");
185 
186         // Preprocess some stylesheets for use by the runners
187         String errStr = "Create processor threw: ";
188         Templates stylesheet1, stylesheet2, stylesheet3;
189 
190         try
191         {
192             String setOneURL = filenameToURI(inputDir + setOneFilenameRoot + ".xsl");
193             String setTwoURL = filenameToURI(inputDir + setTwoFilenameRoot + ".xsl");
194             String setThreeURL = filenameToURI(inputDir + setThreeFilenameRoot + ".xsl");
195 
196             TransformerFactory factory = TransformerFactory.newInstance();
197 
198             errStr = "Processing stylesheet1 threw: ";
199             stylesheet1 =
200                 factory.newTemplates(new StreamSource(setOneURL));
201             errStr = "Processing stylesheet2 threw: ";
202             stylesheet2 =
203                 factory.newTemplates(new StreamSource(setTwoURL));
204             errStr = "Processing stylesheet3 threw: ";
205             stylesheet3 =
206                 factory.newTemplates(new StreamSource(setThreeURL));
207         }
208         catch (Exception e)
209         {
210             println("<arbitrary desc=\"" + errStr + e.toString() + "\">");
211 
212             if (pWriter != null)
213             {
214                 e.printStackTrace(pWriter);
215             }
216 
217             e.printStackTrace();
218             println("</arbitrary>");
219 
220             return;
221         }
222 
223         errStr = "PreCreating runners threw: ";
224 
225         try
226         {
227             String[] rValues = new String[FUTUREUSE];
228 
229             // Create a whole bunch of worker threads and run them
230             for (int i = 0; i < numRunners; i++)
231             {
232                 TestThreadsRunner r1, r2, r3;
233                 Thread t1, t2, t3;
234 
235                 // First set of runners reports on memory usage periodically
236                 rValues[ID] = "one-" + i;
237                 rValues[XMLNAME] = filenameToURI(inputDir + setOneFilenameRoot + ".xml");
238                 rValues[XSLNAME] = filenameToURI(inputDir + setOneFilenameRoot + ".xsl");
239                 rValues[OUTNAME] = outputDir + setOneFilenameRoot + "r" + i;
240                 rValues[PARAMNAME] = paramName;
241                 rValues[PARAMVAL] = paramVal;
242                 rValues[OPTIONS] = "memory;param";
243                 errStr = "Creating runnerone-" + i + " threw: ";
244                 r1 = new TestThreadsRunner(rValues, stylesheet1,
245                                            numRunnerCalls);
246                 t1 = new Thread(r1);
247 
248                 t1.start();
249 
250                 // Second set of runners is polite; uses optional liaison
251                 rValues[ID] = "two-" + i;
252                 rValues[XMLNAME] = filenameToURI(inputDir + setTwoFilenameRoot + ".xml");
253                 rValues[XSLNAME] = filenameToURI(inputDir + setTwoFilenameRoot + ".xsl");
254                 rValues[OUTNAME] = outputDir + setTwoFilenameRoot + "r" + i;
255                 rValues[PARAMNAME] = paramName;
256                 rValues[PARAMVAL] = paramVal;
257                 rValues[OPTIONS] = "polite;param";
258 
259                 if ((liaison != null) &&!(liaison.equals("")))
260                     rValues[LIAISON] = liaison;
261 
262                 errStr = "Creating runnertwo-" + i + " threw: ";
263                 r2 = new TestThreadsRunner(rValues, stylesheet2,
264                                            numRunnerCalls);
265                 t2 = new Thread(r2);
266 
267                 t2.start();
268 
269                 rValues[LIAISON] = null;
270 
271                 // Third set of runners will recreate it's processor each time
272                 // and report memory usage; but not set the param
273                 // Note: this causes lots of calls to System.gc
274                 rValues[ID] = "thr-" + i;
275                 rValues[XMLNAME] = filenameToURI(inputDir + setThreeFilenameRoot + ".xml");
276                 rValues[XSLNAME] = filenameToURI(inputDir + setThreeFilenameRoot + ".xsl");
277                 rValues[OUTNAME] = outputDir + setThreeFilenameRoot + "r" + i;
278                 rValues[PARAMNAME] = paramName;
279                 rValues[PARAMVAL] = paramVal;
280                 rValues[OPTIONS] = "recreate;memory";
281                 errStr = "Creating runnerthree-" + i + " threw: ";
282                 r3 = new TestThreadsRunner(rValues, stylesheet3,
283                                            numRunnerCalls);
284                 t3 = new Thread(r3);
285 
286                 t3.start();
287                 println("<message desc=\"Created " + i
288                         + "th set of runners.\"/>");
289             }
290         }
291         catch (Exception e)
292         {
293             println("<arbitrary desc=\"" + errStr + e.toString() + "\">");
294 
295             if (pWriter != null)
296             {
297                 e.printStackTrace(pWriter);
298             }
299 
300             e.printStackTrace();
301             println("</arbitrary>");
302         }
303 
304         // Clean up our own references, just for completeness
305         stylesheet1 = null;
306         stylesheet2 = null;
307         stylesheet3 = null;
308         errStr = null;
309 
310         println("<message desc=\"Created all our runners!\"/>");
311         println("<message desc=\"TestThreads main thread now complete\"/>");
312         println("</resultsfile>");
313 
314         if (pWriter != null)
315             pWriter.flush();
316     }
317 
318     /**
319      * Read in properties file and set instance variables.
320      *
321      * @param fName name of .properties file to read
322      * @return false if error occoured
323      */
initPropFile(String fName)324     protected boolean initPropFile(String fName)
325     {
326 
327         Properties p = new Properties();
328 
329         try
330         {
331 
332             // Load named file into our properties block
333             FileInputStream fIS = new FileInputStream(fName);
334 
335             p.load(fIS);
336 
337             // Parse out any values that match our internal convenience variables
338             outputDir = p.getProperty("outputDir", outputDir);
339 
340             // Validate the outputDir and use it to reset the logFileName
341             File oDir = new File(outputDir);
342 
343             if (!oDir.exists())
344             {
345                 if (!oDir.mkdirs())
346                 {
347 
348                     // Error, we can't create the outputDir, default to current dir
349                     println("<message desc=\"outputDir(" + outputDir
350                             + ") does not exist, defaulting to .\"/>");
351 
352                     outputDir = ".";
353                 }
354             }
355 
356             // Verify inputDir as well
357             inputDir = p.getProperty("inputDir", inputDir);
358 
359             File tDir = new File(inputDir);
360 
361             if (!tDir.exists())
362             {
363                 if (!tDir.mkdirs())
364                 {
365 
366                     // Error, we can't create the inputDir, abort
367                     println("<message desc=\"inputDir(" + inputDir
368                             + ") does not exist, terminating test\"/>");
369 
370                     return false;
371                 }
372             }
373 
374             // Add on separators
375             inputDir += File.separator;
376             outputDir += File.separator;
377 
378             // Each defaults to variable initializers
379             logFileName = p.getProperty("logFile", logFileName);
380             setOneFilenameRoot = p.getProperty("setOneFile",
381                                                setOneFilenameRoot);
382             setTwoFilenameRoot = p.getProperty("setTwoFile",
383                                                setTwoFilenameRoot);
384             setThreeFilenameRoot = p.getProperty("setThreeFile",
385                                                  setThreeFilenameRoot);
386             paramName = p.getProperty("paramName", paramName);
387             paramVal = p.getProperty("paramVal", paramVal);
388             liaison = p.getProperty("liaison", liaison);
389 
390             String numb;
391 
392             numb = p.getProperty("numRunners");
393 
394             if (numb != null)
395             {
396                 try
397                 {
398                     numRunners = Integer.parseInt(numb);
399                 }
400                 catch (NumberFormatException numEx)
401                 {
402 
403                     // no-op, leave set as default
404                     println("<message desc=\"numRunners threw: "
405                             + numEx.toString() + "\"/>");
406                 }
407             }
408 
409             numb = p.getProperty("numRunnerCalls");
410 
411             if (numb != null)
412             {
413                 try
414                 {
415                     numRunnerCalls = Integer.parseInt(numb);
416                 }
417                 catch (NumberFormatException numEx)
418                 {
419 
420                     // no-op, leave set as default
421                     println("<message desc=\"numRunnerCalls threw: "
422                             + numEx.toString() + "\"/>");
423                 }
424             }
425         }
426         catch (Exception e)
427         {
428             println("<arbitrary=\"initPropFile: " + fName + " threw: "
429                     + e.toString() + "\">");
430 
431             if (pWriter != null)
432             {
433                 e.printStackTrace(pWriter);
434             }
435 
436             e.printStackTrace();
437             println("</arbitrary>");
438 
439             return false;
440         }
441 
442         return true;
443     }
444 
445     /**
446      * Bottleneck output; goes to System.out and main's pWriter.
447      *
448      * NEEDSDOC @param s
449      */
println(String s)450     protected void println(String s)
451     {
452 
453         System.out.println(s);
454 
455         if (pWriter != null)
456             pWriter.println(s);
457     }
458 
459     /** A simple log output file for the main thread; each runner also has it's own. */
460     protected PrintWriter pWriter = null;
461 
462     /**
463      * Worker method to setup a simple log output file.
464      *
465      * NEEDSDOC @param n
466      */
createLogFile(String n)467     protected void createLogFile(String n)
468     {
469 
470         try
471         {
472             pWriter = new PrintWriter(new FileWriter(n, true));
473         }
474         catch (Exception e)
475         {
476             System.err.println("<message desc=\"createLogFile threw: "
477                                + e.toString() + "\"/>");
478             e.printStackTrace();
479         }
480     }
481 
482     /**
483      * Startup the test from the command line.
484      *
485      * NEEDSDOC @param args
486      */
main(String[] args)487     public static void main(String[] args)
488     {
489 
490         if (args.length < 1)
491         {
492             System.err.println("ERROR! Must have at least one argument\n" + usage());
493 
494             return;  // Don't System.exit, it's not polite
495         }
496 
497         TestThreads app = new TestThreads();
498         // semi-HACK: accept and ignore -load as first arg only
499         String propFileName = null;
500         if ("-load".equalsIgnoreCase(args[0]))
501         {
502             propFileName = args[1];
503         }
504         else
505         {
506             propFileName = args[0];
507         }
508         if (!app.initPropFile(propFileName))  // Side effect: creates pWriter for logging
509         {
510             System.err.println("ERROR! Could not read properties file: "
511                                + propFileName);
512 
513             return;
514         }
515 
516         app.runTest();
517     }
518 
519     /**
520      * Worker method to translate String to URI.
521      * Note: Xerces and Crimson appear to handle some URI references
522      * differently - this method needs further work once we figure out
523      * exactly what kind of format each parser wants (esp. considering
524      * relative vs. absolute references).
525      * @param String path\filename of test file
526      * @return URL to pass to SystemId
527      */
filenameToURI(String filename)528     public static String filenameToURI(String filename)
529     {
530         File f = new File(filename);
531         String tmp = f.getAbsolutePath();
532 	    if (File.separatorChar == '\\') {
533 	        tmp = tmp.replace('\\', '/');
534 	    }
535         return "file:///" + tmp;
536     }
537 }  // end of class TestThreads
538 
539 /**
540  * Worker class to run a processor on a separate thread.
541  * <p>Currently, no automated validation is done, however most
542  * output files and all error logs are saved to disk allowing for
543  * later manual verification.</p>
544  */
545 class TestThreadsRunner implements Runnable
546 {
547 
548     /** NEEDSDOC Field xslStylesheet          */
549     Templates xslStylesheet;
550 
551     /** NEEDSDOC Field numProcesses          */
552     int numProcesses;
553 
554     /** NEEDSDOC Field runnerID          */
555     String runnerID;
556 
557     /** NEEDSDOC Field xmlName          */
558     String xmlName;
559 
560     /** NEEDSDOC Field xslName          */
561     String xslName;
562 
563     /** NEEDSDOC Field outName          */
564     String outName;
565 
566     /** NEEDSDOC Field paramName          */
567     String paramName;
568 
569     /** NEEDSDOC Field paramVal          */
570     String paramVal;
571 
572     /** NEEDSDOC Field liaison          */
573     String liaison;
574 
575     /** NEEDSDOC Field polite          */
576     boolean polite = false;  // if we should yield each loop
577 
578     /** NEEDSDOC Field recreate          */
579     boolean recreate = false;  // if we should re-create a new processor each time
580 
581     /** NEEDSDOC Field validate          */
582     boolean validate = false;  // if we should attempt to validate output files (FUTUREWORK)
583 
584     /** NEEDSDOC Field reportMem          */
585     boolean reportMem = false;  // if we should report memory usage periodically
586 
587     /** NEEDSDOC Field setParam          */
588     boolean setParam = false;  // if we should set our parameter or not
589 
590     /**
591      * Constructor TestThreadsRunner
592      *
593      *
594      * NEEDSDOC @param params
595      * NEEDSDOC @param xslStylesheet
596      * NEEDSDOC @param numProcesses
597      */
TestThreadsRunner(String[] params, Templates xslStylesheet, int numProcesses)598     TestThreadsRunner(String[] params, Templates xslStylesheet,
599                       int numProcesses)
600     {
601 
602         this.xslStylesheet = xslStylesheet;
603         this.numProcesses = numProcesses;
604         this.runnerID = params[TestThreads.ID];
605         this.xmlName = params[TestThreads.XMLNAME]; // must already be legal URI
606         this.xslName = params[TestThreads.XSLNAME]; // must already be legal URI
607         this.outName = params[TestThreads.OUTNAME]; // must be local path/filename
608         this.paramName = params[TestThreads.PARAMNAME];
609         this.paramVal = params[TestThreads.PARAMVAL];
610 
611         if (params[TestThreads.OPTIONS].indexOf("polite") > 0)
612             polite = true;
613 
614         if (params[TestThreads.OPTIONS].indexOf("recreate") > 0)
615             recreate = true;
616 
617         if (params[TestThreads.OPTIONS].indexOf("validate") > 0)
618             validate = true;
619 
620         // Optimization: only report memory if asked to and we're
621         //  in the first iteration of runners created
622         if ((params[TestThreads.OPTIONS].indexOf("memory") > 0)
623             && (this.runnerID.indexOf("0") >= 0))
624             reportMem = true;
625 
626         if (params[TestThreads.OPTIONS].indexOf("param") > 0)
627             setParam = true;
628 
629         if (params[TestThreads.LIAISON] != null)  // TRAX unused
630             liaison = params[TestThreads.LIAISON];
631     }
632 
633     /**
634      * Bottleneck output; both to System.out and to our private errWriter.
635      *
636      * NEEDSDOC @param s
637      */
println(String s)638     protected void println(String s)
639     {
640 
641         System.out.println(s);
642 
643         if (errWriter != null)
644             errWriter.println(s);
645     }
646 
647     /**
648      * Bottleneck output; both to System.out and to our private errWriter.
649      *
650      * NEEDSDOC @param s
651      */
print(String s)652     protected void print(String s)
653     {
654 
655         System.out.print(s);
656 
657         if (errWriter != null)
658             errWriter.print(s);
659     }
660 
661     /** NEEDSDOC Field errWriter          */
662     PrintWriter errWriter = null;
663 
664     /**
665      * NEEDSDOC Method createErrWriter
666      *
667      */
createErrWriter()668     protected void createErrWriter()
669     {
670 
671         try
672         {
673             errWriter = new PrintWriter(new FileWriter(outName + ".log"),
674                                         true);
675         }
676         catch (Exception e)
677         {
678             System.err.println("<message desc=\"" + runnerID + ":threw: "
679                                + e.toString() + "\"/>");
680         }
681     }
682 
683     /** Main entrypoint; loop and perform lots of processes. */
run()684     public void run()
685     {
686 
687         int i = 0;  // loop counter; used for error reporting
688 
689         createErrWriter();
690         println("<?xml version=\"1.0\"?>");
691         println("<testrunner desc=\"" + runnerID + ":started\" fileName=\""
692                 + xslName + "\">");
693 
694         TransformerFactory factory = null;
695 
696         try
697         {
698 
699             // Each runner creates it's own processor for use and it's own error log
700             factory = TransformerFactory.newInstance();
701             println("<arbitrary desc=\"" + runnerID + ":processing\">");
702         }
703         catch (Throwable ex)
704         {  // If we got here, just log it and bail, no sense continuing
705             println("<throwable desc=\"" + ex.toString() + "\"><![CDATA[");
706             ex.printStackTrace(errWriter);
707             println("\n</throwable>");
708             println("<message desc=\"" + runnerID + ":complete-ERROR:after:"
709                     + i + "\"/>");
710             println("</testrunner>");
711 
712             if (errWriter != null)
713                 errWriter.close();
714 
715             return;
716         }
717 
718         try
719         {
720 
721             // Loop away...
722             for (i = 0; i < numProcesses; i++)
723             {
724 
725                 // Run a process using the pre-compiled stylesheet we were construced with
726                 {
727                     Transformer transformer1 = xslStylesheet.newTransformer();
728                     FileOutputStream resultStream1 =
729                         new FileOutputStream(outName + ".out");
730                     Result result1 = new StreamResult(resultStream1);
731 
732                     if (setParam)
733                         transformer1.setParameter(paramName, paramVal);
734 
735                     print(".");  // Note presence of this in logs shows which process threw an exception
736                     transformer1.transform(new StreamSource(xmlName), result1);
737                     resultStream1.close();
738 
739                     // Temporary vars go out of scope for cleanup here
740                 }
741 
742                 // Now process something with a newly-processed stylesheet
743                 {
744                     Templates templates2 =
745                         factory.newTemplates(new StreamSource(xslName));
746                     Transformer transformer2 = templates2.newTransformer();
747                     FileOutputStream resultStream2 =
748                         new FileOutputStream(outName + "_.out");
749                     Result result2 = new StreamResult(resultStream2);
750 
751                     if (setParam)
752                         transformer2.setParameter(paramName, paramVal);
753 
754                     print("*");  // Note presence of this in logs shows which process threw an exception
755                     transformer2.transform(new StreamSource(xmlName), result2);
756                     resultStream2.close();
757                 }
758 
759                 // if asked, report memory statistics
760                 if (reportMem)
761                 {
762                     Runtime r = Runtime.getRuntime();
763 
764                     r.gc();
765 
766                     long freeMemory = r.freeMemory();
767                     long totalMemory = r.totalMemory();
768 
769                     println("<statistic desc=\"" + runnerID
770                             + ":memory:longval-free:doubleval-total\">");
771                     println("<longval>" + freeMemory + "</longval>");
772                     println("<doubleval>" + totalMemory + "</doubleval>");
773                     println("</statistic>");
774                 }
775 
776                 // if we're polite, let others play for a bit
777                 if (polite)
778                     java.lang.Thread.yield();
779             }
780 
781             // IF we get here, we worked without exceptions (presumably successfully)
782             println("</arbitrary>");
783             println("<message desc=\"" + runnerID + ":complete-OK:after:"
784                     + numProcesses + "\"/>");
785         }
786 
787         // Separate messages for each kind of exception
788         catch (TransformerException te)
789         {
790             println("\n<TransformerException desc=\"" + te.toString() + "\">");
791             logStackTrace(te, errWriter);
792             logContainedException(te, errWriter);
793             println("</TransformerException>");
794             println("</arbitrary>");
795             println("<message desc=\"" + runnerID + ":complete-ERROR:after:"
796                     + i + "\"/>");
797         }
798         catch (Throwable ex)
799         {
800             logThrowable(ex, errWriter);
801             println("</arbitrary>");
802             println("<message desc=\"" + runnerID + ":complete-ERROR:after:"
803                     + i + "\"/>");
804         }
805         finally
806         {
807 
808             // Cleanup our references, etc.
809             println("</testrunner>");
810 
811             if (errWriter != null)
812                 errWriter.close();
813 
814             runnerID = null;
815             xmlName = null;
816             xslName = null;
817             xslStylesheet = null;
818             outName = null;
819         }
820     }  // end of run()...
821 
822     /**
823      * NEEDSDOC Method logContainedException
824      *
825      *
826      * NEEDSDOC @param parent
827      * NEEDSDOC @param p
828      */
logContainedException(TransformerException parent, PrintWriter p)829     private void logContainedException(TransformerException parent, PrintWriter p)
830     {
831 
832         Throwable containedException = parent.getException();
833 
834         if (null != containedException)
835         {
836             println("<containedexception desc=\""
837                     + containedException.toString() + "\">");
838             logStackTrace(containedException, p);
839             println("</containedexception>");
840         }
841     }
842 
843     /**
844      * NEEDSDOC Method logThrowable
845      *
846      *
847      * NEEDSDOC @param t
848      * NEEDSDOC @param p
849      */
logThrowable(Throwable t, PrintWriter p)850     private void logThrowable(Throwable t, PrintWriter p)
851     {
852 
853         println("\n<throwable desc=\"" + t.toString() + "\">");
854         logStackTrace(t, p);
855         println("</throwable>");
856     }
857 
858     /**
859      * NEEDSDOC Method logStackTrace
860      *
861      *
862      * NEEDSDOC @param t
863      * NEEDSDOC @param p
864      */
logStackTrace(Throwable t, PrintWriter p)865     private void logStackTrace(Throwable t, PrintWriter p)
866     {
867 
868         // Should check if (errWriter == null)
869         println("<stacktrace><![CDATA[");
870         t.printStackTrace(p);
871 
872         // Could also echo to stdout, but not really worth it
873         println("]]></stacktrace>");
874     }
875 }  // end of class TestThreadsRunner...
876 
877 // END OF FILE
878