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