View Javadoc

1   /*
2       Jameleon - An automation testing tool..
3       Copyright (C) 2003-2006 Christian W. Hargraves (engrean@hotmail.com)
4       
5       This library is free software; you can redistribute it and/or
6       modify it under the terms of the GNU Lesser General Public
7       License as published by the Free Software Foundation; either
8       version 2.1 of the License, or (at your option) any later version.
9   
10      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13      Lesser General Public License for more details.
14  
15      You should have received a copy of the GNU Lesser General Public
16      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19  package net.sf.jameleon;
20  
21  import net.sf.jameleon.bean.TestCase;
22  import net.sf.jameleon.data.DataDrivable;
23  import net.sf.jameleon.event.TestCaseEventHandler;
24  import net.sf.jameleon.exception.JameleonScriptException;
25  import net.sf.jameleon.result.*;
26  import net.sf.jameleon.util.*;
27  import org.apache.commons.beanutils.BeanUtils;
28  import org.apache.commons.jelly.*;
29  import org.apache.commons.jelly.expression.CompositeExpression;
30  import org.apache.commons.jelly.expression.Expression;
31  import org.apache.commons.jelly.expression.jexl.JexlExpressionFactory;
32  import org.apache.log4j.Logger;
33  
34  import java.io.File;
35  import java.io.FileNotFoundException;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.util.*;
39  
40  /***
41   * Every test case script must have at least one testcase tag containing all other Jameleon tags. 
42   * <p>
43   * Some of this tags attribute may affect every nested Jameleon tag. 
44   * Many of the attributes in this tag can be set globally via a jameleon.conf file.
45   * </p>
46   * <p>
47   * The order of setting variables in the context follows:
48   * <ol>
49   * <li> Load the CSV file variables and put them in the context.</li>
50   * <li> Load the $testEnvironment-Applications.properties and then Applications.properties and 
51   *      only set the variables that aren't set in the previous files.  In other words, if there
52   *      are variables that are going to be the same ( like the page title ) across multiple test 
53   *      cases, then first variable set wins.</li>
54   * <li> Execute the function tag and set the attributes in the context. If you want key/values in
55   *      the CSV and properties files to override the script attribute, then the function point 
56   *      author uses the setDefaultVariableValue() method in the set method for that attribute.</li>
57   * <li> If the function point is using a map-variable, then override all settings to set the 
58   *      variable to the mapFrom variable name.</li>
59   * </ol>
60   * </p>
61   * @jameleon.function name="testcase"
62   * @jameleon.function name="test-case"
63   */
64  public class TestCaseTag extends AbstractCsvTag {
65  
66      protected long maxExecutionTime = 0;
67      protected String assertGreaterThanLevel;
68      protected String assertLessThanLevel;
69      protected String assertLevel;
70      protected String assertLevels;
71      protected String testEnvironment = "";
72      protected String organization = "";
73      protected String csvValueSeparator;
74      protected String bugTrackerUrl;
75      protected String genTestCaseDocsEncoding = JameleonDefaultValues.FILE_CHARSET;
76      protected String propsName;
77      protected boolean useCSV;
78      protected boolean trace;
79      protected boolean genTestCaseDocs = true;
80      protected boolean executeTestCase = true;
81  
82      protected String testCaseResultDataRowTemplate = JameleonDefaultValues.TEST_CASE_RESULT_DATA_ROW_TEMPLATE;
83      protected String testCaseResultSessionTemplate = JameleonDefaultValues.TEST_CASE_RESULT_SESSION_TEMPLATE;
84      protected String testCaseResultFunctionTemplate = JameleonDefaultValues.TEST_CASE_RESULT_FUNCTION_TEMPLATE;
85      protected String testCaseMainPageTemplate = JameleonDefaultValues.TEST_CASE_RESULT_MAIN_PAGE_TEMPLATE;
86      protected String testCaseSummaryTemplate = JameleonDefaultValues.TEST_CASE_SUMMARY_TEMPLATE;
87      protected String testCaseResultSummaryTemplate = JameleonDefaultValues.TEST_CASE_RESULT_SUMMARY_TEMPLATE;
88      protected String testCaseResultTemplate = JameleonDefaultValues.TEST_CASE_RESULT_TEMPLATE;
89  
90      protected TestCaseEventHandler eventHandler = TestCaseEventHandler.getInstance();
91      protected long startTime;
92      protected File resultsFile;
93      protected CountableDataDrivableResultContainer rowResultContainer;
94  
95      /***
96       * DEFAULT - true.
97       * If set to false, then don't error and don't even log the test case. If a file is found, then go ahead and log test case results
98       */
99      protected boolean failOnCSVFileNotFound = true;
100     /***
101      * Used to flag if a CSV file is not found. This is used only when failOnCSVFileNotFound is set to false
102      */
103     protected boolean failedOnDataDriver = false;
104     /***
105      * The test case results which are a complete set of results for every tag executed.
106      */
107     protected TestCaseResult results;
108 
109     //TODO: Remove this. This is a workaround to get correct failed and passed results
110     protected DataDrivableRowResult ddRowResult;
111 
112     //TODO: Remove this also. Additional getter required to get row number in session tag if useCSV=true
113     public DataDrivableRowResult getDdResult() {
114         return ddRowResult;
115     }
116 
117     protected ArrayList keysSet = new ArrayList();
118     protected TestCase testCase = new TestCase();
119 
120     /***
121      * Only store the displayed screen being tested to a file on an error.
122      */
123     protected boolean storeDisplayOnError = true; 
124     /***
125      * Store all displayed screens to a file..
126      */
127     protected boolean storeEveryDisplay = false;
128     /***
129      * The baseDir where everything else is based
130      */
131     protected File baseDir = JameleonDefaultValues.BASE_DIR;
132     /***
133      * The directory name to store the results to. Defaults to ./jameleon_test_results
134      */
135     protected File resultsDir = new File(baseDir, JameleonDefaultValues.RESULTS_DIR);
136     /***
137      * The timestamped directory to store the results to.
138      */
139     protected File timestampedResultsDir;
140     /***
141      * The name of the Jameleon configuration file. (default is defined in {@link net.sf.jameleon.util.Configurator})
142      */
143     protected String jameleonConfigName = Configurator.DEFAULT_CONFIG_NAME;
144     /***
145      * Enable/disable validity checking for SSL certificates. Default is true (enabled).
146      * Set to false to prevent exceptions from being thrown for invalid SSL certs.
147      */
148     protected boolean enableSslCertCheck = true;
149 
150     protected final static String APPLICATIONS_PROPERTIES = "Applications";
151     protected final static int DEFAULT_ROW = 0;
152     protected final static String DEFAULT_VALUE_SEPARATOR = ",";
153 
154     /***
155      * Gets the logger used for this tag
156      * @return the logger used for this tag.
157      */
158     protected Logger getLogger(){
159         return Logger.getLogger(TestCaseTag.class.getName());
160     }
161 
162     /***
163      * Gets the trace message when the execution is beginning and ending.
164      * The message displayed will already start with BEGIN: or END:
165      * @return the trace message when the execution is just beginning and ending.
166      */
167     protected String getTagTraceMsg(){
168         return "parsing " + getCsvFile();
169     }
170 
171     /***
172      * Describe the tag when error messages occur.
173      * The most appropriate message might be the tag name itself.
174      * @return A brief description of the tag or the tag name itself.
175      */
176     public String getTagDescription(){
177         return "testcase tag";
178     }
179 
180     protected TestCaseTag getTestCaseTag(){
181         return this;
182     }
183 
184     /***
185      * Used to add key/values to a local context for multiple variable
186      * substitution. Since a test case can have multiple sessions and sessions
187      * are application specific, then variables with ${varName} in them can
188      * be different values depending on the application settings in the Applications.properties.
189      */
190     public void addVariablesToRowData(Map vars){
191         rowData.putAll(vars);
192         substituteKeyValues();
193         traceMsg(getTraceKeyValuePairs(vars.keySet(), context.getVariables()));
194     }
195 
196     /***
197      * Gets the session template to be used to generate the session result.
198      * This is searched for in the classpath.
199      * @return the session template to be used to generate the test case docs.
200      */
201     public String getTestCaseResultSessionTemplate() {
202         return testCaseResultSessionTemplate;
203     }
204 
205     /***
206      * Sets the session template to be used to generate the session result.
207      * This is searched for in the classpath.
208      * @param testCaseResultSessionTemplate the session template to be used to generate the test case docs.
209      * @jameleon.attribute
210      */
211     public void setTestCaseResultSessionTemplate(String testCaseResultSessionTemplate) {
212         this.testCaseResultSessionTemplate = testCaseResultSessionTemplate;
213     }
214 
215     /***
216      * Gets the data row template to be used to generate the data drivable result.
217      * This is searched for in the classpath.
218      * @return the data-drivable template to be used to generate the test case docs.
219      */
220     public String getTestCaseResultDataRowTemplate() {
221         return testCaseResultDataRowTemplate;
222     }
223 
224     /***
225      * Sets the data row template to be used to generate the data drivable result.
226      * This is searched for in the classpath.
227      * @param testCaseResultDataRowTemplate the data-drivable template to be used to generate the test case docs.
228      * @jameleon.attribute
229      */
230     public void setTestCaseResultDataRowTemplate(String testCaseResultDataRowTemplate) {
231         this.testCaseResultDataRowTemplate = testCaseResultDataRowTemplate;
232     }
233 
234     /***
235      * Gets the template to be used to generate the function result.
236      * This is searched for in the classpath.
237      * @return the entry leaf template to be used to generate the test case docs.
238      */
239     public String getTestCaseResultFunctionTemplate() {
240         return testCaseResultFunctionTemplate;
241     }
242 
243     /***
244      * Sets the function result template to be used to generate the test case result.
245      * This is searched for in the classpath.
246      * @param testCaseResultFunctionTemplate The entry leaf template to be used to generate the test case docs.
247      * @jameleon.attribute
248      */    
249     public void setTestCaseResultFunctionTemplate(String testCaseResultFunctionTemplate) {
250         this.testCaseResultFunctionTemplate = testCaseResultFunctionTemplate;
251     }
252 
253     /***
254      * Gets the template to be used to generate the main page of the results..
255      * This is searched for in the classpath.
256      * @return the template to be used to generate the test case main page.
257      */
258     public String getTestCaseMainPageTemplate() {
259         return testCaseMainPageTemplate;
260     }
261 
262     /***
263      * Sets the template to be used to generate the main page of the results..
264      * This is searched for in the classpath.
265      * @param testCaseMainPageTemplate The template.
266      * @jameleon.attribute
267      */
268     public void setTestCaseMainPageTemplate(String testCaseMainPageTemplate) {
269         this.testCaseMainPageTemplate = testCaseMainPageTemplate;
270     }
271 
272     /***
273      * Gets the template to be used to generate the test case summary page of the results..
274      * This is searched for in the classpath.
275      * @return the template to be used to generate the test case summary docs.
276      */
277     public String getTestCaseSummaryTemplate() {
278         return testCaseSummaryTemplate;
279     }
280 
281     /***
282      * Sets the template to be used to generate the test case summary page of the results.
283      * This is searched for in the classpath.
284      * @param testCaseSummaryTemplate The template to be used to generate the test case summary docs.
285      * @jameleon.attribute
286      */
287     public void setTestCaseSummaryTemplate(String testCaseSummaryTemplate) {
288         this.testCaseSummaryTemplate = testCaseSummaryTemplate;
289     }
290 
291     /***
292      * Gets the template to be used to generate the test case result summary page.
293      * This is searched for in the classpath.
294      * @return The template to be used to generate the test case result summary page.
295      */
296     public String getTestCaseResultSummaryTemplate() {
297         return testCaseResultSummaryTemplate;
298     }
299 
300     /***
301      * Sets the template to be used to generate the test case result summary page.
302      * This is searched for in the classpath.
303      * @param testCaseResultTemplate The template to be used to generate the test case result page.
304      * @jameleon.attribute
305      */
306     public void setTestCaseResultTemplate(String testCaseResultTemplate) {
307         this.testCaseResultTemplate = testCaseResultTemplate;
308     }
309 
310     /***
311      * Gets the template to be used to generate the test case result page.
312      * This is searched for in the classpath.
313      * @return The template to be used to generate the test case result page.
314      */
315     public String getTestCaseResultTemplate() {
316         return testCaseResultTemplate;
317     }
318 
319     /***
320      * Sets the template to be used to generate the test case result summary page.
321      * This is searched for in the classpath.
322      * @param testCaseResultSummaryTemplate The template to be used to generate the test case result summary page.
323      * @jameleon.attribute
324      */
325     public void setTestCaseResultSummaryTemplate(String testCaseResultSummaryTemplate) {
326         this.testCaseResultSummaryTemplate = testCaseResultSummaryTemplate;
327     }
328 
329     /***
330      * Sets the directory where the results will be written to.
331      * @param resultsDir - the directory where the results will be written to.
332      * @jameleon.attribute
333      */
334     public void setResultsDir(File resultsDir){
335         this.resultsDir = new File(resultsDir.getPath());
336     }
337 
338     /***
339      * Sets the timestamped results directory where the test case result documentation will be stored.
340      * @param timestampedResultsDir The timestamped results directory where the test case result
341      * documentation will be stored.
342      */
343     public void setTimestampedResultsDir(File timestampedResultsDir){
344         this.timestampedResultsDir = timestampedResultsDir;
345     }
346 
347     /***
348      * Gets the timestamped results directory where the test case result documentation will be stored.
349      * @return the timestamped results directory where the test case result documentation will be stored.
350      */
351     public File getTimestampedResultsDir(){
352         return timestampedResultsDir;
353     }
354 
355     /***
356      * Sets the test case to record the state of the application at a defined <code>event</code>
357      * @param event - An event at which the state of the application will be stored. Valid values are:
358      * <ul>
359      *      <li><code>storeStateNever</code> - never store the state of the application.</li>
360      *      <li><code>storeStateOnChange</code> - store the state of the applcation on any state change.</li>
361      *      <li><code>storeStateOnError</code> - store the state of the application only on an error. DEFAULT</li>
362      * </ul>
363      * If the <code>event</code> is not valid, then it is not set.
364      * @jameleon.attribute
365      */
366     public void setStoreStateEvent(String event){
367         if ("storeStateNever".equalsIgnoreCase(event) ){
368             setStoreStateNever(true);
369         }else if ("storeStateOnChange".equalsIgnoreCase(event)) {
370             setStoreStateOnChange(true);
371         }else if ("storeStateOnError".equalsIgnoreCase(event) ){
372             setStoreStateOnError(true);
373         }else{
374             log.warn(event +" not recognized as a valid option for storeStateEvent! Valid options are: storeStateOnError, storeStateOnChange, storeStateNever.");
375         }
376     }
377 
378     /***
379      * Sets the test case to record the state of the application whenever the application's state changes.
380      * @param all - Set to <code>true</code> to record all responses. Defaults to <code>false</code>
381      * @jameleon.attribute
382      */
383     public void setStoreStateOnChange(boolean all){
384         if (all) {
385             stateStorer.setStorableEvent(StateStorer.ON_STATE_CHANGE_EVENT);
386         }
387     }
388 
389     /***
390      * Sets the test case to record the state of the application on errors.
391      * @param onError - Set to <code>true</code> to record responses ONLY on errors. Defaults to <code>true</code>
392      * @jameleon.attribute
393      */
394     public void setStoreStateOnError(boolean onError){
395         if (onError) {
396             stateStorer.setStorableEvent(StateStorer.ON_ERROR_EVENT);
397         }
398     }
399 
400     /***
401      * Sets the test case to never record the state of the application.
402      * @param none - Set to <code>true</code> to never record responses. Defaults to <code>false</code>
403      * @jameleon.attribute
404      */
405     public void setStoreStateNever(boolean none){
406         if (none) {
407             stateStorer.setStorableEvent(StateStorer.ON_NO_EVENT);
408         }
409     }
410 
411     /***
412      * Gets the directory where the results will be written to.
413      * @return the directory where the results will be written to.
414      */
415     public File getResultsDir(){
416         return getResultsDir(true);
417     }
418 
419     /***
420      * Gets the directory where the results will be written to.
421      * @param includeName set to true to include the test case name in the results directory.
422      * @return the directory where the results will be written to.
423      */
424     public File getResultsDir(boolean includeName){
425         File dir = getTimestampedResultsDir();
426         if (dir == null){
427             if (includeName){
428                 if (baseDir.equals(resultsDir.getParentFile())){
429                     dir = new File(resultsDir, getName());
430                 }else{
431                     dir = new File(baseDir, resultsDir.getPath() + File.separator + getName());
432                 }
433             }else{
434                 dir = resultsDir;
435             }
436         }
437         return dir;
438     }
439 
440     /***
441      * @return the directory where the results will be stored.
442      * @param rowCount The row number the test case is on
443      */
444     protected File getResultsDir(int rowCount){
445         return new File(getResultsDir(),""+rowCount);
446     }
447 
448     /***
449      * Gets the maximum execution time before the test case fails
450      * @return the maximum execution time before the test case fails
451      */
452     public long getMaxExecutionTime(){
453         return maxExecutionTime;
454     }
455 
456     /***
457      * Sets the maximum execution time before the test case fails
458      * @param maxExecutionTime - the maximum execution time before the test case fails
459      * @jameleon.attribute
460      */
461     public void setMaxExecutionTime(long maxExecutionTime){
462         this.maxExecutionTime = maxExecutionTime;
463     }
464 
465     /***
466      * @return the <code>StateStorer</code> instance created by this TestCaseTag.
467      */
468     public StateStorer getStateStorer(){
469         return stateStorer;
470     }
471     
472     /***
473      * Used internally to mark whether the DataDrivable had a problem. This is used for the failOnCSVFileNotFound option only
474      * @param failedOnDataDriver - Set to <code>true</code> if a problem due to DataDrivable occured.
475      * @jameleon.attribute
476      */
477     public void setFailedOnDataDriver(boolean failedOnDataDriver){
478         this.failedOnDataDriver = failedOnDataDriver;
479     }
480 
481     /***
482      * Sets the failOnCSVFileNotFound property
483      * @param failOnCSVFileNotFound - Set to <code>false</code> to not log a failure due to a CSV FileNotFoundException
484      * @jameleon.attribute
485      */
486     public void setFailOnCSVFileNotFound(boolean failOnCSVFileNotFound){
487         this.failOnCSVFileNotFound = failOnCSVFileNotFound;
488     }
489 
490     /***
491      * @return the list of keys that have been put into the context.
492      */
493     public List getKeySet() {
494         return keysSet;
495     }
496 
497     /***
498      * @return the organization or affiliate this application will be tested against.
499      * This would used for when there is one application for many different datasources
500      * like our banking applications that one day will run for multiple banks. This is also
501      * important because the URLs change between organizations as well.
502      */
503     public String getOrganization() {
504         return organization;
505     }
506 
507     /***
508      * Sets the organziation or company that this test will be run against.
509      * This is used the same as the testEnvironment to find the CSV file or use values
510      * from a properties file specific to an organization.
511      * @param organization - The organziation or company that this test will be run against.
512      * @jameleon.attribute
513      */
514     public void setOrganization(String organization) {
515         this.organization = organization;
516     }
517 
518     /***
519      * @return the environment the test cases will be run under
520      */
521     public String getTestEnvironment() {
522         return this.testEnvironment;
523     }
524 
525     /***
526      * Sets the environment to which testing system the testcase will be run in.
527      * Information like the starting url, can be based on this. This is 
528      * also used to find the CSV file if one is used for the test case. 
529      * This can be set as a global variable.
530      * @param testEnvironment Some examples might include, localhost, dev, test, stage, production ...:
531      * @jameleon.attribute
532      */
533     public void setTestEnvironment(String testEnvironment) {
534         this.testEnvironment = testEnvironment;
535     }
536 
537     /***
538      * @return the test cases that were run under this environment
539      */
540     public TestCaseResult getResults() {
541         return results;
542     }
543 
544     public void setResults(TestCaseResult results){
545         this.results = results;
546     }
547 
548     /***
549      * Sets whether a CSV file should be used for this testcase or not.
550      * @param useCSV - If set to true, then this test case will grab all of it's data from a CSV file
551      * @jameleon.attribute
552      */
553     public void setUseCSV(boolean useCSV) {
554         this.useCSV = useCSV;
555     }
556 
557     /***
558      * Sets whether a std out message should be sent before and after the execution of a functional point.
559      * @param trace - If set to true, then this test case will show messages before and after execution of each functional point.
560      * @jameleon.attribute
561      */
562     public void setTrace(boolean trace) {
563         this.trace = trace;
564     }
565 
566     /***
567      * Gets whether a std out message should be sent before and after the execution of a functional point.
568      * @return true, if this test case will show messages before and after execution of each functional point.
569      */
570     public boolean getTrace() {
571         return trace;
572     }
573 
574     /***
575      * Sets the base directory of the project
576      * @param baseDir - The base directory of the project
577      * @jameleon.attribute
578      */ 
579     public void setBaseDir(File baseDir) {
580         this.baseDir = baseDir;
581     }
582 
583     /***
584      * @return The base directory of the project
585      */ 
586     public File getBaseDir() {
587         return baseDir;
588     }
589 
590     /***
591      * Gets the directory of where csv files should be read from,
592      * given the environment settings.
593      * @param calculate - set to true to calculate in testEnvironment and organization if they apply
594      * @return The csv file to run the test against
595      */
596     public File getCsvDir(boolean calculate){
597         File dir;
598         if (calculate) {
599             dir = getCsvDir();
600         }else{
601             String filename = baseDir.getPath() + File.separator + dataDir.getPath() + File.separator;
602             dir = new File(filename);
603         }
604         return dir;
605     }
606 
607 
608     /***
609      * @return the level of the asserts to run that are greater than this number.
610      */
611     public String getAssertGreaterThanLevel() {
612         return this.assertGreaterThanLevel;
613     }
614 
615     /***
616      * @param assertGreaterThanLevel the level of the asserts to run that are greater than this number.
617      * @jameleon.attribute
618      */
619     public void setAssertGreaterThanLevel(String assertGreaterThanLevel) {
620         this.assertGreaterThanLevel = assertGreaterThanLevel;
621     }
622 
623     /***
624      * @return the level of the asserts to run that are less than this number.
625      */
626     public String getAssertLessThanLevel() {
627         return this.assertLessThanLevel;
628     }
629 
630     /***
631      * @param assertLessThanLevel the level of the asserts to run that are less than this number.
632      * @jameleon.attribute
633      */
634     public void setAssertLessThanLevel(String assertLessThanLevel) {
635         this.assertLessThanLevel = assertLessThanLevel;
636     }
637 
638     /***
639      * @return the level of the asserts to run that are equal to this number.
640      */
641     public String getAssertLevel() {
642         return this.assertLevel;
643     }
644 
645     /***
646      * @param assertLevel the level of the asserts to run that are equal to this number.
647      * @jameleon.attribute
648      */
649     public void setAssertLevel(String assertLevel) {
650         this.assertLevel = assertLevel;
651     }
652 
653     /***
654      * @return the level of the asserts to run that are equal to this list of numbers.
655      */
656     public String getAssertLevels() {
657         return this.assertLevels;
658     }
659 
660     /***
661      * Sets the level of the asserts to run that are equal to this list of numbers.
662      * @param assertLevels A list of assert levels to run in the this test case
663      * @jameleon.attribute
664      */
665     public void setAssertLevels(String assertLevels) {
666         this.assertLevels = assertLevels;
667     }
668 
669     /***
670      * Sets the test case to generate the test case docs based on the javadocs of the functional points
671      * and the functionId's of the functional points in the test case.
672      * @param genTestCaseDocs - set to true if the test case docs are to be generated
673      * @jameleon.attribute
674      */
675     public void setGenTestCaseDocs(boolean genTestCaseDocs){
676         this.genTestCaseDocs = genTestCaseDocs;
677     }
678 
679     /***
680      * @return The test case for documentation purposes.
681      */
682     public TestCase getTestCase(){
683         return testCase;
684     }
685 
686     /***
687      * @return true if the test is actually supposed to be executed. The default is <code>true</code>
688      */
689     public boolean isExecuteTestCase(){
690         return executeTestCase;
691     }
692 
693     /***
694      * @return true if the test case results docs are to be generated.
695      * The default is <code>true</code>
696      */
697     public boolean isGenTestCaseDocs(){
698         return genTestCaseDocs;
699     }
700 
701     /***
702      * Sets the test case to be executed or not.
703      * @param executeTestCase - Set to <code>false</code> if the functionality of the test case is not to be executed.
704      * The default is <code>true</code>. This would be set to false if only the test case docs are to be generated.
705      * @jameleon.attribute
706      */
707     public void setExecuteTestCase(boolean executeTestCase){
708         this.executeTestCase = executeTestCase;
709     }
710 
711     /***
712      * Sets the url of the bugtracking tool used so the bug's listed for the test case are linked.
713      * @return the bug tracker url
714      */
715     public String getBugTrackerUrl(){
716         return this.bugTrackerUrl;
717     }
718 
719     /***
720      * Sets the url of the bugtracking tool used so the bug's listed for the test case are linked.
721      * @param bugTrackerUrl The url of the bugtracking tool used so the bug's listed for the test case are linked. 
722      * @jameleon.attribute
723      */
724     public void setBugTrackerUrl(String bugTrackerUrl){
725         this.bugTrackerUrl = bugTrackerUrl;
726     }
727 
728     /***
729      * @return the character set encoding to use for the test case in XML.
730      */
731     public String getGenTestCaseDocsEncoding(){
732         return genTestCaseDocsEncoding;
733     }
734 
735     /***
736      * Sets the charset encoding.
737      * @param encoding - the character set encoding to use for the test case in XML.
738      * @jameleon.attribute
739      */
740     public void setGenTestCaseDocsEncoding(String encoding){
741         this.genTestCaseDocsEncoding = encoding;
742     }
743 
744     /***
745      * Gets the name of the properties file (minus the .properties) to read in into the context
746      * @return the name of the properties file (minus the .properties) to read in into the context
747      */
748     public String getPropsName(){
749         return propsName;
750     }
751 
752     /***
753      * Sets the name of the properties file (minus the .properties) to read in into the context
754      * @param propsName - name of the properties file (minus the .properties) to read in into the context
755      * @jameleon.attribute
756      */
757     public void setPropsName(String propsName){
758         this.propsName = propsName;
759     }
760 
761     /***
762      * Sets the configuration file Jameleon uses to configure itself.
763      * (default is defined in {@link net.sf.jameleon.util.Configurator})
764      * Do not use. This is used for internal testing purposes only.
765      * @param configName - the configuration file Jameleon uses to configure itself.
766      * @jameleon.attribute
767     */
768     public void setJameleonConfigName(String configName) {
769         jameleonConfigName = configName;
770         Configurator.clearInstance();
771         Configurator config = Configurator.getInstance();
772         config.setConfigName(jameleonConfigName);
773     }
774 
775     public String getJameleonConfigName(){
776         return jameleonConfigName;
777     }
778 
779     /***
780      * Query whether SSL cert validity checking is enabled.
781      * @return - If true, an exception will be thrown when an invalid SSL cert is encountered.
782      * If false, invalid SSL certs will be accepted.
783     */
784     public boolean isEnableSslCertCheck() {
785         return enableSslCertCheck;
786     }
787     /***
788      * Enable or disable validity checking of SSL certificates.
789      * @param enable - If "true", an exception will be thrown when an invalid SSL cert is encountered.
790      * If "false", invalid SSL certs will be accepted. If not set, the default is "true".
791      * @jameleon.attribute
792      */
793     public void setEnableSslCertCheck(boolean enable) {
794         enableSslCertCheck = enable;
795     }
796 
797     protected void setLocationAware(LocationAware la){
798         if (la.getLineNumber() == -1) {
799             la.setColumnNumber(getColumnNumber());
800             la.setLineNumber(getLineNumber());
801         }
802         if (la.getFileName() == null) {
803             la.setFileName(getFileName());
804         }
805     }
806 
807     /***
808      * Execute the rest of the test script.
809      * @param rowNum - if a csv file is used, then this pertains to the row number
810      *                 the test case is being executed against. If a CSV file is not
811      *                 used, then 0 is sent.
812      * @param result - the results to run with.
813      */
814     public void invokeChildren(int rowNum, JameleonTestResult result){
815         try {
816             invokeBody(xmlOut);
817         } catch (JellyTagException jte){
818             Throwable err = jte;
819             LocationAware la = jte;
820             if (err.getCause() != null && err.getCause() instanceof LocationAware) {
821                 err = err.getCause();
822                 la = (LocationAware)err;
823             }
824             setLocationAware(la);
825             JameleonScriptException jse = new JameleonScriptException(err.getMessage(), la);
826             logError(err);
827             result.setError(jse);
828         } catch (ThreadDeath td){
829             throw td;
830         } catch (Throwable t) {
831             if (trace){
832                 log.info(JameleonUtility.getStack(t));
833             }
834             LocationAware la = null;
835             Throwable err = t;
836             if (t.getCause() != null && t.getCause() instanceof LocationAware) {
837                 err = t.getCause();
838                 la = (LocationAware)err;
839             }else if (t instanceof LocationAware) {
840                 la = (LocationAware)t;
841             }
842 
843             JameleonScriptException jse = null;
844             if (la != null) {
845                 setLocationAware(la);
846                 jse = new JameleonScriptException(err.getMessage(), la);
847             }
848             if (jse != null) {
849                 err = jse;
850             }
851             logError(err);
852             result.setError(t);
853         } finally{
854             result.setExecutionTime(System.currentTimeMillis() - startTime);
855         }
856         failedOnCurrentRow = false;
857     }
858 
859     /***
860      * A CsvExecutable implementation method that gets called once for every row in the csv file.
861      * This does not include the top row which should only define the variable names
862      */
863     public void executeDrivableRow(int rowNum){
864         traceMsg("BEGIN executing row number "+rowNum);
865         TestResultWithChildren res = results;
866         if (rowResultContainer != null) {
867             res = rowResultContainer;
868         }
869         ddRowResult = new CountableDataDrivableRowResult(fp, res);
870         ddRowResult.copyLocationAwareProperties(this);
871         ddRowResult.setRowData(new HashMap(rowData));
872         ddRowResult.setRowNum(rowNum);
873         stateStorer.setStoreDir(getResultsDir(rowNum));
874         invokeChildren(rowNum, ddRowResult);
875         traceMsg("END executing row number "+rowNum);
876     }
877 
878     protected void initResults(){
879         results = new TestCaseResult(getFunctionalPoint());
880         results.copyLocationAwareProperties(this);
881         if (useCSV) {
882             rowResultContainer = new CountableDataDrivableResultContainer(getFunctionalPoint(), results);
883             rowResultContainer.copyLocationAwareProperties(this);
884         }
885     }
886 
887     /***
888      * Set up the test environment.
889      *
890      * @throws JellyTagException if the tag is not in the correct state.
891      */
892     public void setUp() throws JellyTagException{
893         initResults();
894         String script = getFileName();
895         if (script != null) {
896             testCase.readFromScript(script);
897         }
898         //Some strangeness here. The problem is I want the logic to figure out the 
899         // test case name in only one spot.
900         if (testCase.getName() != null) {
901             //testCase.getName() should only be null for the unit tests
902             name = testCase.getName();
903         }
904         results.setTestName(name);
905         loadJameleonConfig();
906         //Must load this after loading the values from jameleon.conf
907         //so that it can be set from both places.
908         testCase.setOrganization(organization);
909         testCase.setTestEnvironment(testEnvironment);
910 
911         if (propsName != null) {
912             Properties p = new Properties();
913             getPropertiesForName(propsName, p);
914             inilializeProps(p);
915         }
916         validateAttributes();
917         setAssertLevels();
918         if (organization != null && organization.trim().length() > 0) {
919             context.setVariable("organization", organization);
920         }
921         setUpDataDrivable();
922     }
923     /***
924      * Records a child result. Used as a helper method for the ResultRecordable
925      * implementation methods. This method overrides the default behavior of 
926      * AbstractDataDrivableTag.
927      * @param result - the result to record
928      */
929     protected void recordResult(JameleonTestResult result){
930         if (ddRowResult != null) {
931             ddRowResult.addChildResult(result);
932             result.setParentResults(ddRowResult);
933         }else if (results != null) {
934             results.addChildResult(result);
935             result.setParentResults(results);
936         }
937     }
938 
939     /***
940      * This method executes the tags inside the test-case tag. If <code>useCSV=true</code>, then the data for
941      * the functional points will be grabbed from a CSV file. Otherwise the attributes are all expected to be set
942      * the contained functional points. If the functional points' attributes are set and useCSV is set to true,
943      * then the data in the CSV file will override the data set in the function points' attributes.
944      */
945     public void doTag(XMLOutput out) throws MissingAttributeException, JellyTagException{
946         xmlOut = out;
947         init();
948         setUp();
949         eventHandler.beginTestCase(this);
950         //Call this after beginTestCase so that the timestamp directory can be set up
951         setStateStoreOptions();
952         try{
953             testForUnsupportedAttributesCaught();
954             broker.transferAttributes(context);
955             broker.validate(context);
956         }catch(JameleonScriptException jse){
957             results.setError(jse);
958             log.info(results);
959             eventHandler.endTestCase(this);
960             tearDown();
961             throw jse;
962         }
963         try{
964             if (executeTestCase) {
965                 if (useCSV) {
966                     spanCSV();
967                 }else{
968                     executeNoCSV();
969                 }
970                 if (maxExecutionTime > 0 && maxExecutionTime < results.getExecutionTime()) {
971                     String msg = "The maximum execution time <"+maxExecutionTime+"> was exceeded <"+results.getExecutionTime()+">!";
972                     results.setError(new JameleonScriptException(msg, this));
973                 }
974                 if ( failOnCSVFileNotFound || (!failOnCSVFileNotFound && !failedOnDataDriver) ) {
975                     if (results.failed()) {
976                         JellyTagException jte = null;
977                         if (results.getError() != null) {
978                             jte = createExceptionFromResult(results);
979                         }else{
980                             StringBuffer sb = new StringBuffer();
981                             Iterator it = results.getFailedResults().iterator();
982                             JameleonTestResult result;
983                             while (it.hasNext()) {
984                                 result = (JameleonTestResult)it.next();
985                                 sb.append(result.getTag().getDefaultTagName()).append(":");
986                                 sb.append(result.getErrorMsg());
987                                 if (jte == null) {
988                                     jte = createExceptionFromResult(result);
989                                 }
990                             }
991                         }
992                         if (jte != null) {
993                             if (results.getError() != null) {
994                                 results.setError(jte);
995                             }
996                             throw jte;
997                         }
998                     }
999                 }else{
1000                     setGenTestCaseDocs(false);                    
1001                 }
1002             }
1003         }finally{
1004             eventHandler.endTestCase(this);
1005             if ( failOnCSVFileNotFound || (!failOnCSVFileNotFound && !failedOnDataDriver) ) {
1006                 log.info(results);
1007             }
1008             tearDown();
1009         }
1010     }
1011 
1012     protected JellyTagException createExceptionFromResult(JameleonTestResult jtr){
1013         return createExceptionFromResult(jtr.getErrorMsg(), jtr);
1014     }
1015 
1016     protected JellyTagException createExceptionFromResult(String message, JameleonTestResult jtr){
1017         return new JellyTagException(message, jtr.getError(), jtr.getFileName(), jtr.getElementName(), jtr.getColumnNumber(), jtr.getLineNumber());
1018     }
1019 
1020     protected void executeNoCSV(){
1021         executeBody(new Runnable(){
1022                 public void run() {
1023                     invokeChildren(1, results);
1024                 }});
1025     }
1026 
1027     /***
1028      * useCSV is set to true, data-drive this CSV file.
1029      */
1030     protected void spanCSV(){
1031         final DataDrivable dd = this;
1032         csv.setFile(getCsvFile());
1033         executeBody(new Runnable(){
1034                 public void run() {
1035                     try{
1036                         traceMsg("Begin parsing: \""+getCsvFile()+"\"");
1037                         executer.executeData(dd, false);
1038                         traceMsg("End parsing \""+getCsvFile()+"\"");
1039                     }catch(FileNotFoundException fnfe){
1040                         failedOnDataDriver = true;
1041                         results.setError(fnfe);
1042                     }catch(IOException ioe){
1043                         failedOnDataDriver = true;
1044                         results.setError(new JameleonScriptException(" Trouble parsing "+getCsvFile()));
1045                     }catch(IllegalStateException ise){
1046                         failedOnDataDriver = true;
1047                         results.setError(ise);
1048                     }catch(RuntimeException re){
1049                         if (re.getCause() != null) {
1050                             results.setError(re.getCause());
1051                         }
1052                     }
1053                 }});
1054     }
1055 
1056     /***
1057      * Clean things up after the test case has been executed.
1058      */
1059     public void tearDown(){
1060         Configurator.clearInstance();
1061         Iterator it = keysSet.iterator();
1062         while (it.hasNext()) {
1063             context.removeVariable((String)it.next());
1064         }
1065         results.setTag(fp.cloneFP());
1066         resetFunctionalPoint();
1067     }
1068 
1069     protected void setStateStoreOptions(){
1070         stateStorer.setStoreDir(getTimestampedResultsDir());
1071     }
1072 
1073     protected void executeBody(Runnable r){
1074         startTime = System.currentTimeMillis();
1075         try{
1076             r.run();
1077         }finally{
1078             results.setExecutionTime(System.currentTimeMillis() - startTime);
1079         }
1080     }
1081 
1082     /***
1083      * Gets the results file that represents the test case execution.
1084      * @return the results file that represents the test case execution.
1085      */
1086     public File getResultsFile(){
1087         if (genTestCaseDocs && resultsFile == null) {
1088             resultsFile = new File(getResultsDir(), File.separator+"index.html");
1089         }
1090         return resultsFile;
1091     }
1092 
1093     private InputStream getInputStream(String fileName){
1094         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
1095         InputStream input = null;
1096         if (classLoader == null) {
1097             classLoader = this.getClass().getClassLoader();
1098         } else {
1099             if (classLoader.getResourceAsStream( fileName ) == null) {
1100                 classLoader = this.getClass().getClassLoader();
1101             }
1102         }
1103         if (classLoader != null) {
1104             input = classLoader.getResourceAsStream(fileName);
1105         }
1106         return input;
1107     }
1108 
1109 
1110     /***
1111      * This method creates a new Properties Object populated only with the
1112      * properties for application desired. The keys for these properties are
1113      * no longer start with the application name. For example, if a property
1114      * named "personalBanking.host" exists in the original Properties Object
1115      * and this method is called with "personalBanking" as the <code>applicationName</code>
1116      * then the new property name will be "host".
1117      * @param applicationName The name of the application that you want properties for.
1118      * @return a Properties Object populated only with the
1119      * properties for application desired
1120      */ 
1121     public Properties getPropertiesForApplication(String applicationName) {
1122         ResourceBundle props = null;
1123         try {
1124             props = loadApplicationProperties();
1125         } catch (IOException mre) {
1126             if (testEnvironment != null && testEnvironment.length() > 1) {
1127                 traceMsg("not reading in "+testEnvironment+"-Applications.properties");
1128             }
1129         }
1130         Properties p = new Properties();
1131         String keyWithOrg = null;
1132         if (organization != null && !organization.trim().equals("")) {
1133             keyWithOrg = organization+"."+applicationName;
1134         }
1135         if (props != null) {
1136             addValuesFromResourceBundle(props, p, keyWithOrg, true);
1137             addValuesFromResourceBundle(props, p, applicationName, false);
1138         }
1139         getPropertiesForName(applicationName, p);
1140 
1141         try{
1142             ResourceBundle appProps = getResourceBundle(applicationName);
1143             if (organization != null && organization.trim().length() > 0) {
1144                 addValuesFromResourceBundle(appProps, p, organization, false);
1145             }
1146             addValuesFromResourceBundle(appProps, p, "", false);
1147         }catch(IOException mre){
1148             //Do nothing since this file is optional
1149         }
1150         try{
1151             ResourceBundle baseProps = getResourceBundle("Applications");
1152             addValuesFromResourceBundle(baseProps, p, keyWithOrg, false);
1153             addValuesFromResourceBundle(baseProps, p, applicationName, false);
1154         }catch(IOException mre){
1155             //Do nothing since this file is optional
1156         }
1157         inilializeProps(p);
1158         substituteValues(p);
1159         return p;
1160     }
1161 
1162     private void inilializeProps(Properties p){
1163         Iterator it = rowData.keySet().iterator();
1164         String key, value;
1165         while (it.hasNext()) {
1166             key = (String)it.next();
1167             value = (String)rowData.get(key);
1168             if (value != null) {
1169                 p.setProperty(key, value);
1170             }
1171         }
1172     }
1173 
1174     private void getPropertiesForName(String propsName, Properties p){
1175         try{
1176             ResourceBundle appProps = getResourceBundle(propsName);
1177             if (organization != null && organization.trim().length() > 0) {
1178                 addValuesFromResourceBundle(appProps, p, organization, false);
1179             }
1180             addValuesFromResourceBundle(appProps, p, "", false);
1181         }catch(IOException mre){
1182             //Do nothing since this file is optional
1183         }
1184     }
1185 
1186     /***
1187      * Do variable substition based on keys in the iterator.
1188      * @param p - the properties to be substituted.
1189      */
1190     protected void substituteValues(Properties p){
1191         JexlExpressionFactory exfact = new JexlExpressionFactory();
1192         String key, value;
1193         Iterator it = p.keySet().iterator();
1194         while (it.hasNext()) {
1195             key = (String) it.next();
1196             value = p.getProperty(key);
1197             if (value != null) {
1198                 try{
1199                     Expression ex = CompositeExpression.parse(value, exfact);
1200                     context.setVariable(key, ex.evaluateAsString(context));
1201                 }catch(JellyException je){
1202                     je.printStackTrace();
1203                 }
1204             }
1205         }
1206     }
1207 
1208     /***
1209      * Do variable subsitution on values stored from data file.
1210      */
1211     public void substituteKeyValues(){
1212         JexlExpressionFactory exfact = new JexlExpressionFactory();
1213         String key, value;
1214         Object obj;
1215         Set keys = rowData.keySet();
1216         Iterator it = keys.iterator();
1217         while (it.hasNext()) {
1218             key = (String) it.next();
1219             obj = rowData.get(key);
1220             //Only try to substitute values on String objects
1221             if (obj instanceof String) {
1222                 value = (String)rowData.get(key);
1223                 if (value != null) {
1224                     try{
1225                         Expression ex = CompositeExpression.parse(value, exfact);
1226                         context.setVariable(key, ex.evaluateAsString(context));
1227                     }catch(JellyException je){
1228                         je.printStackTrace();
1229                     }
1230                 }
1231             }
1232         }
1233     }
1234 
1235 
1236     protected void addValuesFromResourceBundle(ResourceBundle props, Properties p, String startOfKey, boolean overrideValue){
1237         Enumeration e = props.getKeys();
1238         String key, newKey;
1239         String keyToFind = (startOfKey != null && startOfKey.length() > 0 ) ? startOfKey+"." : "";
1240         while (e.hasMoreElements()) {
1241             key = (String)e.nextElement();
1242             if (key != null && key.startsWith(keyToFind)) {
1243                 newKey = key.substring(keyToFind.length());
1244                 if (p.getProperty(newKey) == null || overrideValue) {
1245                     p.setProperty(newKey, props.getString(key));
1246                 }
1247                 //Put the variables in the session if they haven't already been set.
1248                 if ( context != null && (overrideValue || context.getVariable(newKey) == null) ) {
1249                     keysSet.add(newKey);
1250                     context.setVariable(newKey, props.getString(key));
1251                 }
1252             }
1253         }
1254     }
1255 
1256     /***
1257      * Validate the parameters
1258      * @throws MissingAttributeException If a parameter is invalid.
1259      */
1260     protected void validateAttributes() throws MissingAttributeException{
1261         if (name == null) {
1262             throw new MissingAttributeException("name");
1263         }
1264         if (!isValidateAssertLevel(assertGreaterThanLevel)) {
1265             throw new MissingAttributeException("assertGreaterThanLevel must be between SMOKE and REGRESSION");
1266         }
1267         if (!isValidateAssertLevel(assertLessThanLevel)) {
1268             throw new MissingAttributeException("assertLessThanLevel must be between SMOKE and REGRESSION");
1269         }
1270         if (!isValidateAssertLevel(assertLevel)) {
1271             throw new MissingAttributeException("assertLevel must be between SMOKE and REGRESSION");
1272         }
1273         if (assertLevels != null) {
1274             String[] levels = parseAssertLevels();
1275             for (int i = 0; i < levels.length; i++) {
1276                 if (!isValidateAssertLevel(levels[i])) {
1277                     throw new MissingAttributeException("assertLevels must be between SMOKE and REGRESSION");
1278                 }
1279             }
1280         }
1281     }
1282 
1283     protected String[] parseAssertLevels() {
1284         return assertLevels.split(", ?");
1285     }
1286 
1287     /***
1288      * @param level - the assert level to check
1289      * @return true if the assertLevel is a valid assert level. Must begin with "LEVEL" followed by a digit 1-9.
1290      */
1291     protected boolean isValidateAssertLevel(String level) {
1292         return(getAssertLevelFromString(level) != AssertLevel.INVALID_LEVEL);
1293     }
1294 
1295     protected void setAssertLevels() {
1296         AssertLevel al = AssertLevel.getInstance();
1297         if (assertGreaterThanLevel != null) {
1298             al.setGreaterThanLevel(getAssertLevelFromString(assertGreaterThanLevel));
1299         }
1300         if (assertLessThanLevel != null) {
1301             al.setLessThanLevel(getAssertLevelFromString(assertLessThanLevel));
1302         } 
1303         if (assertLevel != null) {
1304             al.setLevel(getAssertLevelFromString(assertLevel));
1305         } 
1306         if (assertLevels != null && assertLevels.length() > 0) {
1307             String[] levels = parseAssertLevels();
1308             for (int i = 0; i < levels.length; i++) {
1309                 al.addLevel(getAssertLevelFromString(levels[i]));
1310             }
1311         }
1312     }
1313 
1314     protected int getAssertLevelFromString(String assertLevel) {
1315         int lvl = -1;
1316 
1317         if (assertLevel == null || "".equals(assertLevel)) {
1318             lvl = AssertLevel.NO_LEVEL;
1319         } else if ("SMOKE".equalsIgnoreCase(assertLevel)) {
1320             lvl = AssertLevel.SMOKE;
1321         } else if ("LEVEL1".equalsIgnoreCase(assertLevel)) {
1322             lvl = AssertLevel.LEVEL1;
1323         } else if ("LEVEL2".equalsIgnoreCase(assertLevel)) {
1324             lvl = AssertLevel.LEVEL2;
1325         } else if ("FUNCTION".equalsIgnoreCase(assertLevel)) {
1326             lvl = AssertLevel.FUNCTION;
1327         } else if ("LEVEL4".equalsIgnoreCase(assertLevel)) {
1328             lvl = AssertLevel.LEVEL4;
1329         } else if ("LEVEL5".equalsIgnoreCase(assertLevel)) {
1330             lvl = AssertLevel.LEVEL5;
1331         } else if ("REGRESSION".equalsIgnoreCase(assertLevel)) {
1332             lvl = AssertLevel.REGRESSION;
1333         } else if ("LEVEL7".equalsIgnoreCase(assertLevel)) {
1334             lvl = AssertLevel.LEVEL7;
1335         } else if ("LEVEL8".equalsIgnoreCase(assertLevel)) {
1336             lvl = AssertLevel.LEVEL8;
1337         } else if ("ACCEPTANCE".equalsIgnoreCase(assertLevel)) {
1338             lvl = AssertLevel.ACCEPTANCE;
1339         } else {
1340             lvl = AssertLevel.INVALID_LEVEL;
1341         }
1342         return lvl;
1343     }
1344 
1345     /***
1346      * Loads the properties for all applications.
1347      * @return The ResourceBundle representing the given application.
1348      * @throws IOException When the given file can not be found in the classpath
1349      */
1350     protected ResourceBundle loadApplicationProperties() throws IOException{
1351         String resName = testEnvironment+"-"+APPLICATIONS_PROPERTIES;
1352         return getResourceBundle(resName);
1353     }
1354 
1355     /***
1356      * Loads the properties for all applications.
1357      * @param filename - the name of the file
1358      * @return a ResourceBundle loaded from the class path.
1359      * @throws IOException when the file can not be found or loaded
1360      */
1361     private ResourceBundle getResourceBundle(String filename) throws IOException{
1362         String resName = filename+".properties";
1363         InputStream in = getInputStream(resName);
1364         if (in != null) {
1365             return new PropertyResourceBundle (getInputStream(resName));
1366         }else{
1367             throw new IOException("Couldn't find "+filename);
1368         }
1369     }
1370 
1371     protected void setValueFromEnvironment(Configurator config, String key){
1372             String value = config.getValue(key);
1373             if (value != null){
1374                 try{
1375                     BeanUtils.copyProperty(this, key, value);
1376                 }catch (Exception e){
1377                     //Do nothing
1378                 }
1379             }
1380     }
1381 
1382     protected void setFileFromEnvironment(Configurator config, String key){
1383         String value = config.getValue(key);
1384         if (value != null && key != null){
1385             try{
1386                 File file = new File(value);
1387                 File tmpFile;
1388                 if (key.equals("baseDir")) {
1389                     tmpFile = file;
1390                 }else{
1391                     tmpFile = new File(baseDir, value);
1392                 }
1393                 if ( tmpFile.exists() ) {
1394                     BeanUtils.copyProperty(this, key, file);
1395                 }
1396                 if (!tmpFile.exists()) {
1397                     log.warn("The " + value + " directory does not exist for "+key);
1398                     log.warn("Leaving "+key+" to it's default value!");
1399                 }
1400             }catch(Exception iae){
1401                 //Just means I couldn't set the variable.
1402             }
1403         }
1404     }
1405 
1406     /***
1407      * Loads the properties for all applications.
1408      */
1409     protected void loadJameleonConfig() {
1410         String[] vars = {"testEnvironment","organization","assertGreaterThanLevel",
1411                          "assertLessThanLevel","assertLevels","assertLevel","bugTrackerUrl",
1412                          "genTestCaseDocsEncoding", "csvCharset", "trace","genTestCaseDocs",
1413                          "failOnCSVFileNotFound","executeTestCase", "enableSslCertCheck",
1414                          "storeStateEvent","genTestCaseDocsTemplate", "includeTagDetailsInResults"};
1415         String[] fileVars = {"baseDir","resultsDir","csvDir"};
1416         try {
1417             Configurator config = Configurator.getInstance();
1418             config.setConfigName(getJameleonConfigName());
1419             for (int i = 0; i < vars.length; i++) {
1420                 setValueFromEnvironment(config, vars[i]);
1421             }
1422             for (int i = 0; i < fileVars.length; i++) {
1423                 setFileFromEnvironment(config, fileVars[i]);
1424             }
1425         } catch (ThreadDeath td){
1426             throw td;
1427         } catch (Throwable t)  {
1428                 //Simply means jameleon.conf is not being used.
1429         }
1430     }
1431 
1432     /***
1433      * Logs an error in XML format for easy interpreting later. The error message prints out all known
1434      * environment variables and information.
1435      * @param t The Error to log and set in the result
1436      */
1437     protected void logError(Throwable t) {
1438         results.setError(t);
1439         log.debug(JameleonUtility.getStack(t));
1440     }
1441 
1442 }