public static List<ServiceResult> runXmlReplayFile(String xmlReplayBaseDir,
                                          String controlFileName,
                                          String testGroupID,
                                          String oneTestID,
                                          Map<String, ServiceResult> serviceResultsMap,
                                          boolean param_autoDeletePOSTS,
                                          Dump dump,
                                          String protoHostPortParam,
                                          AuthsMap defaultAuths,
                                          List<String> reportsList,
                                          String reportsDir)
                                          throws Exception {
        //Internally, we maintain two collections of ServiceResult:
        //  the first is the return value of this method.
        //  the second is the serviceResultsMap, which is used for keeping track of CSIDs created by POSTs, for later reference by DELETE, etc.
        List<ServiceResult> results = new ArrayList<ServiceResult>();
        XmlReplayReport report = new XmlReplayReport(reportsDir);
        String controlFile = Tools.glue(xmlReplayBaseDir, "/", controlFileName);
        org.dom4j.Document document;
        document = getDocument(controlFile); //will check full path first, then checks relative to PWD.
        if (document==null){
            throw new FileNotFoundException("XmlReplay control file ("+controlFileName+") not found in basedir: "+xmlReplayBaseDir+" Exiting test.");
        }
        String protoHostPort;
        if (Tools.isEmpty(protoHostPortParam)){
            protoHostPort = document.selectSingleNode("/xmlReplay/protoHostPort").getText().trim();
            System.out.println("DEPRECATED: Using protoHostPort ('"+protoHostPort+"') from xmlReplay file ('"+controlFile+"'), not master.");
        } else {
            protoHostPort = protoHostPortParam;
        }
        if (Tools.isEmpty(protoHostPort)){
            throw new Exception("XmlReplay control file must have a protoHostPort element");
        }
        String authsMapINFO;
        AuthsMap authsMap = readAuths(document);
        if (authsMap.map.size()==0){
            authsMap = defaultAuths;
            authsMapINFO = "Using defaultAuths from master file: "+defaultAuths;
        } else {
            authsMapINFO = "Using AuthsMap from control file: "+authsMap;
        }
        report.addTestGroup(testGroupID, controlFileName);   //controlFileName is just the short name, without the full path.
        String xmlReplayHeader = "========================================================================"
                          +"\r\nXmlReplay running:"
                          +"\r\n   controlFile: "+ (new File(controlFile).getCanonicalPath())
                          +"\r\n   protoHostPort: "+protoHostPort
                          +"\r\n   testGroup: "+testGroupID
                          + (Tools.notEmpty(oneTestID) ? "\r\n   oneTestID: "+oneTestID : "")
                          +"\r\n   AuthsMap: "+authsMapINFO
                          +"\r\n   param_autoDeletePOSTS: "+param_autoDeletePOSTS
                          +"\r\n   Dump info: "+dump
                          +"\r\n========================================================================"
                          +"\r\n";
        report.addRunInfo(xmlReplayHeader);
        System.out.println(xmlReplayHeader);
        String autoDeletePOSTS = "";
        List<Node> testgroupNodes;
        if (Tools.notEmpty(testGroupID)){
            testgroupNodes = document.selectNodes("//testGroup[@ID='"+testGroupID+"']");
        } else {
            testgroupNodes = document.selectNodes("//testGroup");
        }
        JexlEngine jexl = new JexlEngine();   // Used for expression language expansion from uri field.
        XmlReplayEval evalStruct = new XmlReplayEval();
        evalStruct.serviceResultsMap = serviceResultsMap;
        evalStruct.jexl = jexl;
        for (Node testgroup : testgroupNodes) {
            XmlReplayEval.MapContextWKeys jc = new XmlReplayEval.MapContextWKeys();//MapContext();  //Get a new JexlContext for each test group.
            evalStruct.jc = jc;
            autoDeletePOSTS = testgroup.valueOf("@autoDeletePOSTS");
            List<Node> tests;
            if (Tools.notEmpty(oneTestID)){
                tests = testgroup.selectNodes("test[@ID='"+oneTestID+"']");
            } else {
                tests = testgroup.selectNodes("test");
            }
            String authForTest = "";
            int testElementIndex = -1;
            for (Node testNode : tests) {
                long startTime = System.currentTimeMillis();
                try {
                    testElementIndex++;
                    String testID = testNode.valueOf("@ID");
                    //
                    // Figure out if we will auto delete resources
                    boolean autoDelete = param_autoDeletePOSTS;
                    String autoDeleteValue = testNode.valueOf("@autoDeletePOSTS");
                    if (autoDeleteValue != null && !autoDeleteValue.trim().isEmpty()) {
                    	autoDelete = Boolean.valueOf(autoDeleteValue).booleanValue();
                    }
                    String testIDLabel = Tools.notEmpty(testID) ? (testGroupID+'.'+testID) : (testGroupID+'.'+testElementIndex);
                    String method = testNode.valueOf("method");
                    String contentType = testNode.valueOf("contentType");
                    String uri = testNode.valueOf("uri");
                    String fullURL = Tools.glue(protoHostPort, "/", uri);
                    if (contentType == null || contentType.equals("")) {
                        contentType = XmlReplayTransport.APPLICATION_XML;
                    }
                    String currentAuthForTest = null;
                    String authIDForTest = testNode.valueOf("@auth");
                    if (Tools.notEmpty(authIDForTest)){
                        currentAuthForTest = authsMap.map.get(authIDForTest);
                    }
                    else {
                        String tokenAuthExpression = testNode.valueOf("@tokenauth");
                        if (Tools.notEmpty(tokenAuthExpression)){
                            currentAuthForTest = "Bearer " + evalStruct.eval(tokenAuthExpression, serviceResultsMap, null, jexl, jc);
                        }
                    }
                    if (Tools.notEmpty(currentAuthForTest)){
                        authForTest = currentAuthForTest; //else just run with current from last loop;
                    }
                    if (Tools.isEmpty(authForTest)){
                        authForTest = defaultAuths.getDefaultAuth();
                    }
                    if (uri.indexOf("$")>-1){
                        uri = evalStruct.eval(uri, serviceResultsMap, null, jexl, jc);
                    }
                    fullURL = fixupFullURL(fullURL, protoHostPort, uri);
                    List<Integer> expectedCodes = new ArrayList<Integer>();
                    String expectedCodesStr = testNode.valueOf("expectedCodes");
                    if (Tools.notEmpty(expectedCodesStr)){
                         String[] codesArray = expectedCodesStr.split(",");
                         for (String code : codesArray){
                             expectedCodes.add(new Integer(code.trim()));
                         }
                    }
                    Node responseNode = testNode.selectSingleNode("response");
                    PartsStruct expectedResponseParts = null;
                    if (responseNode!=null){
                        expectedResponseParts = PartsStruct.readParts(responseNode, testID, xmlReplayBaseDir);
                        //System.out.println("reponse parts: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+expectedResponseParts);
                    }
                    ServiceResult serviceResult;
                    boolean isPOST = method.equalsIgnoreCase("POST");
                    boolean isPUT =  method.equalsIgnoreCase("PUT");
                    if ( isPOST || isPUT ) {
                        PartsStruct parts = PartsStruct.readParts(testNode, testID, xmlReplayBaseDir);
                        if (Tools.notEmpty(parts.overrideTestID)) {
                            testID = parts.overrideTestID;
                        }
                        if (isPOST){
                            String csid = CSIDfromTestID(testNode, serviceResultsMap);
                            if (Tools.notEmpty(csid)) uri = Tools.glue(uri, "/", csid+"/items/");
                        } else if (isPUT) {
                            uri = fromTestID(uri, testNode, serviceResultsMap);
                        }
                        //vars only make sense in two contexts: POST/PUT, because you are submitting another file with internal expressions,
                        // and in <response> nodes. For GET, DELETE, there is no payload, so all the URLs with potential expressions are right there in the testNode.
                        Map<String,String> vars = null;
                        if (parts.varsList.size()>0){
                            vars = parts.varsList.get(0);
                        }
                        serviceResult = XmlReplayTransport.doPOST_PUTFromXML(parts.responseFilename, vars, protoHostPort, uri, method, contentType, evalStruct, authForTest, testIDLabel);
                        serviceResult.autoDelete = autoDelete;
                        if (vars!=null) {
                            serviceResult.addVars(vars);
                        }
                        results.add(serviceResult);
                        //if (isPOST){
                            serviceResultsMap.put(testID, serviceResult);      //PUTs do not return a Location, so don't add PUTs to serviceResultsMap.
                        //}
                        fullURL = fixupFullURL(fullURL, protoHostPort, uri);
                    } else if (method.equalsIgnoreCase("DELETE")){
                        String fromTestID = testNode.valueOf("fromTestID");
                        ServiceResult pr = serviceResultsMap.get(fromTestID);
                        if (pr!=null){
                            serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID);
                            serviceResult.fromTestID = fromTestID;
                            if (expectedCodes.size()>0){
                                serviceResult.expectedCodes = expectedCodes;
                            }
                            results.add(serviceResult);
                            if (serviceResult.codeInSuccessRange(serviceResult.responseCode)){  //gotExpectedResult depends on serviceResult.expectedCodes.
                                serviceResultsMap.remove(fromTestID);
                            }
                        } else {
                            if (Tools.notEmpty(fromTestID)){
                                serviceResult = new ServiceResult();
                                serviceResult.responseCode = 0;
                                serviceResult.error = "ID not found in element fromTestID: "+fromTestID;
                                System.err.println("****\r\nServiceResult: "+serviceResult.error+". SKIPPING TEST. Full URL: "+fullURL);
                            } else {
                                serviceResult = XmlReplayTransport.doDELETE(fullURL, authForTest, testID, fromTestID);
                            }
                            serviceResult.fromTestID = fromTestID;
                            results.add(serviceResult);
                        }
                    } else if (method.equalsIgnoreCase("GET")){
                        fullURL = fromTestID(fullURL, testNode, serviceResultsMap);
                        serviceResult = XmlReplayTransport.doGET(fullURL, authForTest, testIDLabel);
                        results.add(serviceResult);
                        serviceResultsMap.put(testID, serviceResult);
                    } else if (method.equalsIgnoreCase("LIST")){
                        fullURL = fixupFullURL(fullURL, protoHostPort, uri);
                        String listQueryParams = ""; //TODO: empty for now, later may pick up from XML control file.
                        serviceResult = XmlReplayTransport.doLIST(fullURL, listQueryParams, authForTest, testIDLabel);
                        results.add(serviceResult);
                        serviceResultsMap.put(testID, serviceResult);
                    } else {
                        throw new Exception("HTTP method not supported by XmlReplay: "+method);
                    }
                    serviceResult.testID = testID;
                    serviceResult.fullURL = fullURL;
                    serviceResult.auth = authForTest;
                    serviceResult.method = method;
                    if (expectedCodes.size()>0){
                        serviceResult.expectedCodes = expectedCodes;
                    }
                    if (Tools.isEmpty(serviceResult.testID)) serviceResult.testID = testIDLabel;
                    if (Tools.isEmpty(serviceResult.testGroupID)) serviceResult.testGroupID = testGroupID;
                    Node expectedLevel = testNode.selectSingleNode("response/expected");
                    if (expectedLevel!=null){
                        String level = expectedLevel.valueOf("@level");
                        serviceResult.payloadStrictness = level;
                    }
                    //=====================================================
                    //  ALL VALIDATION FOR ALL REQUESTS IS DONE HERE:
                    //=====================================================
                    boolean hasError = false;
                    String vError = validateResponse(serviceResult, serviceResultsMap, expectedResponseParts, evalStruct);
                    if (Tools.notEmpty(vError)){
                        serviceResult.error = vError;
                        serviceResult.failureReason = " : VALIDATION ERROR; ";
                        hasError = true;
                    }
                    if (hasError == false){
                        hasError = ! serviceResult.gotExpectedResult();
                    }
                    boolean doingAuto = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.auto);
                    String serviceResultRow = serviceResult.dump(dump.dumpServiceResult, hasError)+"; time:"+(System.currentTimeMillis()-startTime);
                    String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": "";
                    report.addTestResult(serviceResult);
                    if (   (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed)
                        || (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.full)         ){
                        System.out.println("\r\n#---------------------#");
                    }
                    System.out.println(timeString()+" "+leader+serviceResultRow+"\r\n");
                    if (dump.payloads || (doingAuto&&hasError) ) {
                        if (Tools.notBlank(serviceResult.requestPayload)){
                            System.out.println("\r\n========== request payload ===============");
                            System.out.println(serviceResult.requestPayload);
                            System.out.println("==========================================\r\n");
                        }
                    }
                    if (dump.payloads || (doingAuto&&hasError)) {
                        if (Tools.notBlank(serviceResult.result)){
                            System.out.println("\r\n========== response payload ==============");
                            System.out.println(serviceResult.result);
                            System.out.println("==========================================\r\n");
                        }
                    }
                } catch (Throwable t) {
                    String msg = "ERROR: XmlReplay experienced an error in a test node: "+testNode+" Throwable: "+t;
                    System.out.println(msg);
                    System.out.println(Tools.getStackTrace(t));
                    ServiceResult serviceResult = new ServiceResult();
                    serviceResult.error = msg;
                    serviceResult.failureReason = " : SYSTEM ERROR; ";
                    results.add(serviceResult);
                }
            }
            if (Tools.isTrue(autoDeletePOSTS) && param_autoDeletePOSTS){
                autoDelete(serviceResultsMap, "default", 0);
            }
        }
        //=== Now spit out the HTML report file ===
        File m = new File(controlFileName);
        String localName = m.getName();//don't instantiate, just use File to extract file name without directory.
        String reportName = localName+'-'+testGroupID+".html";
        File resultFile = report.saveReport(xmlReplayBaseDir, reportsDir, reportName);
        if (resultFile!=null) {
            String toc = report.getTOC(reportName);
            reportsList.add(toc);
        }
        //================================
        return results;
    }
public static List<ServiceResult> runXmlReplayFile(String xmlReplayBaseDir,
                                          String controlFileName,
                                          String testGroupID,
                                          String oneTestID,
                                          Map<String, ServiceResult> serviceResultsMap,
                                          boolean param_autoDeletePOSTS,
                                          Dump dump,
                                          String protoHostPortParam,
                                          AuthsMap defaultAuths,
                                          List<String> reportsList,
                                          String reportsDir)
                                          throws Exception {
       
       
       
        List<ServiceResult> results = new ArrayList<ServiceResult>();
        XmlReplayReport report = new XmlReplayReport(reportsDir);
        String controlFile = Tools.glue(xmlReplayBaseDir, "/", controlFileName);
        org.dom4j.Document document;
        document = getDocument(controlFile);
        if (document==null){
            throw new FileNotFoundException("XmlReplay control file ("+controlFileName+") not found in basedir: "+xmlReplayBaseDir+" Exiting test.");
        }
        String protoHostPort;
        if (Tools.isEmpty(protoHostPortParam)){
            protoHostPort = document.selectSingleNode("/xmlReplay/protoHostPort").getText().trim();
            System.out.println("DEPRECATED: Using protoHostPort ('"+protoHostPort+"') from xmlReplay file ('"+controlFile+"'), not master.");
        } else {
            protoHostPort = protoHostPortParam;
        }
        if (Tools.isEmpty(protoHostPort)){
            throw new Exception("XmlReplay control file must have a protoHostPort element");
        }
        String authsMapINFO;
        AuthsMap authsMap = readAuths(document);
        if (authsMap.map.size()==0){
            authsMap = defaultAuths;
            authsMapINFO = "Using defaultAuths from master file: "+defaultAuths;
        } else {
            authsMapINFO = "Using AuthsMap from control file: "+authsMap;
        }
        report.addTestGroup(testGroupID, controlFileName);  
        String xmlReplayHeader = "========================================================================"
                          +"\r\nXmlReplay running:"
                          +"\r\n   controlFile: "+ (new File(controlFile).getCanonicalPath())
                          +"\r\n   protoHostPort: "+protoHostPort
                          +"\r\n   testGroup: "+testGroupID
                          + (Tools.notEmpty(oneTestID) ? "\r\n   oneTestID: "+oneTestID : "")
                          +"\r\n   AuthsMap: "+authsMapINFO
                          +"\r\n   param_autoDeletePOSTS: "+param_autoDeletePOSTS
                          +"\r\n   Dump info: "+dump
                          +"\r\n========================================================================"
                          +"\r\n";
        report.addRunInfo(xmlReplayHeader);
        System.out.println(xmlReplayHeader);
        String autoDeletePOSTS = "";
        List<Node> testgroupNodes;
        if (Tools.notEmpty(testGroupID)){
            testgroupNodes = document.selectNodes("//testGroup[@ID='"+testGroupID+"']");
        } else {
            testgroupNodes = document.selectNodes("//testGroup");
        }
        JexlEngine jexl = new JexlEngine();  
        XmlReplayEval evalStruct = new XmlReplayEval();
        evalStruct.serviceResultsMap = serviceResultsMap;
        evalStruct.jexl = jexl;
        for (Node testgroup : testgroupNodes) {
            XmlReplayEval.MapContextWKeys jc = new XmlReplayEval.MapContextWKeys()
            evalStruct.jc = jc;
            autoDeletePOSTS = testgroup.valueOf("@autoDeletePOSTS");
            List<Node> tests;
            if (Tools.notEmpty(oneTestID)){
                tests = testgroup.selectNodes("test[@ID='"+oneTestID+"']");
            } else {
                tests = testgroup.selectNodes("test");
            }
            String authForTest = "";
            int testElementIndex = -1;
            for (Node testNode : tests) {
                long startTime = System.currentTimeMillis();
                try {
                    testElementIndex++;
                    String testID = testNode.valueOf("@ID");
                   
                   
                    boolean autoDelete = param_autoDeletePOSTS;
                    String autoDeleteValue = testNode.valueOf("@autoDeletePOSTS");
                    if (autoDeleteValue != null && !autoDeleteValue.trim().isEmpty()) {
                    	autoDelete = Boolean.valueOf(autoDeleteValue).booleanValue();
                    }
                    String testIDLabel = Tools.notEmpty(testID) ? (testGroupID+'.'+testID) : (testGroupID+'.'+testElementIndex);
                    String method = testNode.valueOf("method");
                    String contentType = testNode.valueOf("contentType");
                    String uri = testNode.valueOf("uri");
                    String fullURL = Tools.glue(protoHostPort, "/", uri);
                    if (contentType == null || contentType.equals("")) {
                        contentType = XmlReplayTransport.APPLICATION_XML;
                    }
                    String currentAuthForTest = null;
                    String authIDForTest = testNode.valueOf("@auth");
                    if (Tools.notEmpty(authIDForTest)){
                        currentAuthForTest = authsMap.map.get(authIDForTest);
                    }
                    else {
                        String tokenAuthExpression = testNode.valueOf("@tokenauth");
                        if (Tools.notEmpty(tokenAuthExpression)){
                            currentAuthForTest = "Bearer " + evalStruct.eval(tokenAuthExpression, serviceResultsMap, null, jexl, jc);
                        }
                    }
                    if (Tools.notEmpty(currentAuthForTest)){
                        authForTest = currentAuthForTest;
                    }
                    if (Tools.isEmpty(authForTest)){
                        authForTest = defaultAuths.getDefaultAuth();
                    }
                    if (uri.indexOf("$")>-1){
                        uri = evalStruct.eval(uri, serviceResultsMap, null, jexl, jc);
                    }
                    fullURL = fixupFullURL(fullURL, protoHostPort, uri);
                    List<Integer> expectedCodes = new ArrayList<Integer>();
                    String expectedCodesStr = testNode.valueOf("expectedCodes");
                    if (Tools.notEmpty(expectedCodesStr)){
                         String[] codesArray = expectedCodesStr.split(",");
                         for (String code : codesArray){
                             expectedCodes.add(new Integer(code.trim()));
                         }
                    }
                    Node responseNode = testNode.selectSingleNode("response");
                    PartsStruct expectedResponseParts = null;
                    if (responseNode!=null){
                        expectedResponseParts = PartsStruct.readParts(responseNode, testID, xmlReplayBaseDir);
                       
                    }
                    ServiceResult serviceResult;
                    boolean isPOST = method.equalsIgnoreCase("POST");
                    boolean isPUT =  method.equalsIgnoreCase("PUT");
                    if ( isPOST || isPUT ) {
                        PartsStruct parts = PartsStruct.readParts(testNode, testID, xmlReplayBaseDir);
                        if (Tools.notEmpty(parts.overrideTestID)) {
                            testID = parts.overrideTestID;
                        }
                        if (isPOST){
                            String csid = CSIDfromTestID(testNode, serviceResultsMap);
                            if (Tools.notEmpty(csid)) uri = Tools.glue(uri, "/", csid+"/items/");
                        } else if (isPUT) {
                            uri = fromTestID(uri, testNode, serviceResultsMap);
                        }
                       
                       
                        Map<String,String> vars = null;
                        if (parts.varsList.size()>0){
                            vars = parts.varsList.get(0);
                        }
                        serviceResult = XmlReplayTransport.doPOST_PUTFromXML(parts.responseFilename, vars, protoHostPort, uri, method, contentType, evalStruct, authForTest, testIDLabel);
                        serviceResult.autoDelete = autoDelete;
                        if (vars!=null) {
                            serviceResult.addVars(vars);
                        }
                        results.add(serviceResult);
                       
                            serviceResultsMap.put(testID, serviceResult);     
                       
                        fullURL = fixupFullURL(fullURL, protoHostPort, uri);
                    } else if (method.equalsIgnoreCase("DELETE")){
                        String fromTestID = testNode.valueOf("fromTestID");
                        ServiceResult pr = serviceResultsMap.get(fromTestID);
                        if (pr!=null){
                            serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID);
                            serviceResult.fromTestID = fromTestID;
                            if (expectedCodes.size()>0){
                                serviceResult.expectedCodes = expectedCodes;
                            }
                            results.add(serviceResult);
                            if (serviceResult.codeInSuccessRange(serviceResult.responseCode)){ 
                                serviceResultsMap.remove(fromTestID);
                            }
                        } else {
                            if (Tools.notEmpty(fromTestID)){
                                serviceResult = new ServiceResult();
                                serviceResult.responseCode = 0;
                                serviceResult.error = "ID not found in element fromTestID: "+fromTestID;
                                System.err.println("****\r\nServiceResult: "+serviceResult.error+". SKIPPING TEST. Full URL: "+fullURL);
                            } else {
                                serviceResult = XmlReplayTransport.doDELETE(fullURL, authForTest, testID, fromTestID);
                            }
                            serviceResult.fromTestID = fromTestID;
                            results.add(serviceResult);
                        }
                    } else if (method.equalsIgnoreCase("GET")){
                        fullURL = fromTestID(fullURL, testNode, serviceResultsMap);
                        serviceResult = XmlReplayTransport.doGET(fullURL, authForTest, testIDLabel);
                        results.add(serviceResult);
                        serviceResultsMap.put(testID, serviceResult);
                    } else if (method.equalsIgnoreCase("LIST")){
                        fullURL = fixupFullURL(fullURL, protoHostPort, uri);
                        String listQueryParams = "";
                        serviceResult = XmlReplayTransport.doLIST(fullURL, listQueryParams, authForTest, testIDLabel);
                        results.add(serviceResult);
                        serviceResultsMap.put(testID, serviceResult);
                    } else {
                        throw new Exception("HTTP method not supported by XmlReplay: "+method);
                    }
                    serviceResult.testID = testID;
                    serviceResult.fullURL = fullURL;
                    serviceResult.auth = authForTest;
                    serviceResult.method = method;
                    if (expectedCodes.size()>0){
                        serviceResult.expectedCodes = expectedCodes;
                    }
                    if (Tools.isEmpty(serviceResult.testID)) serviceResult.testID = testIDLabel;
                    if (Tools.isEmpty(serviceResult.testGroupID)) serviceResult.testGroupID = testGroupID;
                    Node expectedLevel = testNode.selectSingleNode("response/expected");
                    if (expectedLevel!=null){
                        String level = expectedLevel.valueOf("@level");
                        serviceResult.payloadStrictness = level;
                    }
                   
                   
                   
                    boolean hasError = false;
                    String vError = validateResponse(serviceResult, serviceResultsMap, expectedResponseParts, evalStruct);
                    if (Tools.notEmpty(vError)){
                        serviceResult.error = vError;
                        serviceResult.failureReason = " : VALIDATION ERROR; ";
                        hasError = true;
                    }
                    if (hasError == false){
                        hasError = ! serviceResult.gotExpectedResult();
                    }
                    boolean doingAuto = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.auto);
                    String serviceResultRow = serviceResult.dump(dump.dumpServiceResult, hasError)+"; time:"+(System.currentTimeMillis()-startTime);
                    String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": "";
                    report.addTestResult(serviceResult);
                    if (   (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed)
                        || (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.full)         ){
                        System.out.println("\r\n#---------------------#");
                    }
                    System.out.println(timeString()+" "+leader+serviceResultRow+"\r\n");
                    if (dump.payloads || (doingAuto&&hasError) ) {
                        if (Tools.notBlank(serviceResult.requestPayload)){
                            System.out.println("\r\n========== request payload ===============");
                            System.out.println(serviceResult.requestPayload);
                            System.out.println("==========================================\r\n");
                        }
                    }
                    if (dump.payloads || (doingAuto&&hasError)) {
                        if (Tools.notBlank(serviceResult.result)){
                            System.out.println("\r\n========== response payload ==============");
                            System.out.println(serviceResult.result);
                            System.out.println("==========================================\r\n");
                        }
                    }
                } catch (Throwable t) {
                    String msg = "ERROR: XmlReplay experienced an error in a test node: "+testNode+" Throwable: "+t;
                    System.out.println(msg);
                    System.out.println(Tools.getStackTrace(t));
                    ServiceResult serviceResult = new ServiceResult();
                    serviceResult.error = msg;
                    serviceResult.failureReason = " : SYSTEM ERROR; ";
                    results.add(serviceResult);
                }
            }
            if (Tools.isTrue(autoDeletePOSTS) && param_autoDeletePOSTS){
                autoDelete(serviceResultsMap, "default", 0);
            }
        }
       
        File m = new File(controlFileName);
        String localName = m.getName()
        String reportName = localName+'-'+testGroupID+".html";
        File resultFile = report.saveReport(xmlReplayBaseDir, reportsDir, reportName);
        if (resultFile!=null) {
            String toc = report.getTOC(reportName);
            reportsList.add(toc);
        }
       
        return results;
    }
public static list<serviceresult> runxmlreplayfile(string xmlreplaybasedir, string controlfilename, string testgroupid, string onetestid, map<string, serviceresult> serviceresultsmap, boolean param_autodeleteposts, dump dump, string protohostportparam, authsmap defaultauths, list<string> reportslist, string reportsdir) throws exception { list<serviceresult> results = new arraylist<serviceresult>(); xmlreplayreport report = new xmlreplayreport(reportsdir); string controlfile = tools.glue(xmlreplaybasedir, "/", controlfilename); org.dom4j.document document; document = getdocument(controlfile); if (document==null){ throw new filenotfoundexception("xmlreplay control file ("+controlfilename+") not found in basedir: "+xmlreplaybasedir+" exiting test."); } string protohostport; if (tools.isempty(protohostportparam)){ protohostport = document.selectsinglenode("/xmlreplay/protohostport").gettext().trim(); system.out.println("deprecated: using protohostport ('"+protohostport+"') from xmlreplay file ('"+controlfile+"'), not master."); } else { protohostport = protohostportparam; } if (tools.isempty(protohostport)){ throw new exception("xmlreplay control file must have a protohostport element"); } string authsmapinfo; authsmap authsmap = readauths(document); if (authsmap.map.size()==0){ authsmap = defaultauths; authsmapinfo = "using defaultauths from master file: "+defaultauths; } else { authsmapinfo = "using authsmap from control file: "+authsmap; } report.addtestgroup(testgroupid, controlfilename); string xmlreplayheader = "========================================================================" +"\r\nxmlreplay running:" +"\r\n controlfile: "+ (new file(controlfile).getcanonicalpath()) +"\r\n protohostport: "+protohostport +"\r\n testgroup: "+testgroupid + (tools.notempty(onetestid) ? "\r\n onetestid: "+onetestid : "") +"\r\n authsmap: "+authsmapinfo +"\r\n param_autodeleteposts: "+param_autodeleteposts +"\r\n dump info: "+dump +"\r\n========================================================================" +"\r\n"; report.addruninfo(xmlreplayheader); system.out.println(xmlreplayheader); string autodeleteposts = ""; list<node> testgroupnodes; if (tools.notempty(testgroupid)){ testgroupnodes = document.selectnodes("//testgroup[@id='"+testgroupid+"']"); } else { testgroupnodes = document.selectnodes("//testgroup"); } jexlengine jexl = new jexlengine(); xmlreplayeval evalstruct = new xmlreplayeval(); evalstruct.serviceresultsmap = serviceresultsmap; evalstruct.jexl = jexl; for (node testgroup : testgroupnodes) { xmlreplayeval.mapcontextwkeys jc = new xmlreplayeval.mapcontextwkeys() evalstruct.jc = jc; autodeleteposts = testgroup.valueof("@autodeleteposts"); list<node> tests; if (tools.notempty(onetestid)){ tests = testgroup.selectnodes("test[@id='"+onetestid+"']"); } else { tests = testgroup.selectnodes("test"); } string authfortest = ""; int testelementindex = -1; for (node testnode : tests) { long starttime = system.currenttimemillis(); try { testelementindex++; string testid = testnode.valueof("@id"); boolean autodelete = param_autodeleteposts; string autodeletevalue = testnode.valueof("@autodeleteposts"); if (autodeletevalue != null && !autodeletevalue.trim().isempty()) { autodelete = boolean.valueof(autodeletevalue).booleanvalue(); } string testidlabel = tools.notempty(testid) ? (testgroupid+'.'+testid) : (testgroupid+'.'+testelementindex); string method = testnode.valueof("method"); string contenttype = testnode.valueof("contenttype"); string uri = testnode.valueof("uri"); string fullurl = tools.glue(protohostport, "/", uri); if (contenttype == null || contenttype.equals("")) { contenttype = xmlreplaytransport.application_xml; } string currentauthfortest = null; string authidfortest = testnode.valueof("@auth"); if (tools.notempty(authidfortest)){ currentauthfortest = authsmap.map.get(authidfortest); } else { string tokenauthexpression = testnode.valueof("@tokenauth"); if (tools.notempty(tokenauthexpression)){ currentauthfortest = "bearer " + evalstruct.eval(tokenauthexpression, serviceresultsmap, null, jexl, jc); } } if (tools.notempty(currentauthfortest)){ authfortest = currentauthfortest; } if (tools.isempty(authfortest)){ authfortest = defaultauths.getdefaultauth(); } if (uri.indexof("$")>-1){ uri = evalstruct.eval(uri, serviceresultsmap, null, jexl, jc); } fullurl = fixupfullurl(fullurl, protohostport, uri); list<integer> expectedcodes = new arraylist<integer>(); string expectedcodesstr = testnode.valueof("expectedcodes"); if (tools.notempty(expectedcodesstr)){ string[] codesarray = expectedcodesstr.split(","); for (string code : codesarray){ expectedcodes.add(new integer(code.trim())); } } node responsenode = testnode.selectsinglenode("response"); partsstruct expectedresponseparts = null; if (responsenode!=null){ expectedresponseparts = partsstruct.readparts(responsenode, testid, xmlreplaybasedir); } serviceresult serviceresult; boolean ispost = method.equalsignorecase("post"); boolean isput = method.equalsignorecase("put"); if ( ispost || isput ) { partsstruct parts = partsstruct.readparts(testnode, testid, xmlreplaybasedir); if (tools.notempty(parts.overridetestid)) { testid = parts.overridetestid; } if (ispost){ string csid = csidfromtestid(testnode, serviceresultsmap); if (tools.notempty(csid)) uri = tools.glue(uri, "/", csid+"/items/"); } else if (isput) { uri = fromtestid(uri, testnode, serviceresultsmap); } map<string,string> vars = null; if (parts.varslist.size()>0){ vars = parts.varslist.get(0); } serviceresult = xmlreplaytransport.dopost_putfromxml(parts.responsefilename, vars, protohostport, uri, method, contenttype, evalstruct, authfortest, testidlabel); serviceresult.autodelete = autodelete; if (vars!=null) { serviceresult.addvars(vars); } results.add(serviceresult); serviceresultsmap.put(testid, serviceresult); fullurl = fixupfullurl(fullurl, protohostport, uri); } else if (method.equalsignorecase("delete")){ string fromtestid = testnode.valueof("fromtestid"); serviceresult pr = serviceresultsmap.get(fromtestid); if (pr!=null){ serviceresult = xmlreplaytransport.dodelete(pr.deleteurl, authfortest, testidlabel, fromtestid); serviceresult.fromtestid = fromtestid; if (expectedcodes.size()>0){ serviceresult.expectedcodes = expectedcodes; } results.add(serviceresult); if (serviceresult.codeinsuccessrange(serviceresult.responsecode)){ serviceresultsmap.remove(fromtestid); } } else { if (tools.notempty(fromtestid)){ serviceresult = new serviceresult(); serviceresult.responsecode = 0; serviceresult.error = "id not found in element fromtestid: "+fromtestid; system.err.println("****\r\nserviceresult: "+serviceresult.error+". skipping test. full url: "+fullurl); } else { serviceresult = xmlreplaytransport.dodelete(fullurl, authfortest, testid, fromtestid); } serviceresult.fromtestid = fromtestid; results.add(serviceresult); } } else if (method.equalsignorecase("get")){ fullurl = fromtestid(fullurl, testnode, serviceresultsmap); serviceresult = xmlreplaytransport.doget(fullurl, authfortest, testidlabel); results.add(serviceresult); serviceresultsmap.put(testid, serviceresult); } else if (method.equalsignorecase("list")){ fullurl = fixupfullurl(fullurl, protohostport, uri); string listqueryparams = ""; serviceresult = xmlreplaytransport.dolist(fullurl, listqueryparams, authfortest, testidlabel); results.add(serviceresult); serviceresultsmap.put(testid, serviceresult); } else { throw new exception("http method not supported by xmlreplay: "+method); } serviceresult.testid = testid; serviceresult.fullurl = fullurl; serviceresult.auth = authfortest; serviceresult.method = method; if (expectedcodes.size()>0){ serviceresult.expectedcodes = expectedcodes; } if (tools.isempty(serviceresult.testid)) serviceresult.testid = testidlabel; if (tools.isempty(serviceresult.testgroupid)) serviceresult.testgroupid = testgroupid; node expectedlevel = testnode.selectsinglenode("response/expected"); if (expectedlevel!=null){ string level = expectedlevel.valueof("@level"); serviceresult.payloadstrictness = level; } boolean haserror = false; string verror = validateresponse(serviceresult, serviceresultsmap, expectedresponseparts, evalstruct); if (tools.notempty(verror)){ serviceresult.error = verror; serviceresult.failurereason = " : validation error; "; haserror = true; } if (haserror == false){ haserror = ! serviceresult.gotexpectedresult(); } boolean doingauto = (dump.dumpserviceresult == serviceresult.dump_options.auto); string serviceresultrow = serviceresult.dump(dump.dumpserviceresult, haserror)+"; time:"+(system.currenttimemillis()-starttime); string leader = (dump.dumpserviceresult == serviceresult.dump_options.detailed) ? "xmlreplay:"+testidlabel+": ": ""; report.addtestresult(serviceresult); if ( (dump.dumpserviceresult == serviceresult.dump_options.detailed) || (dump.dumpserviceresult == serviceresult.dump_options.full) ){ system.out.println("\r\n#---------------------#"); } system.out.println(timestring()+" "+leader+serviceresultrow+"\r\n"); if (dump.payloads || (doingauto&&haserror) ) { if (tools.notblank(serviceresult.requestpayload)){ system.out.println("\r\n========== request payload ==============="); system.out.println(serviceresult.requestpayload); system.out.println("==========================================\r\n"); } } if (dump.payloads || (doingauto&&haserror)) { if (tools.notblank(serviceresult.result)){ system.out.println("\r\n========== response payload =============="); system.out.println(serviceresult.result); system.out.println("==========================================\r\n"); } } } catch (throwable t) { string msg = "error: xmlreplay experienced an error in a test node: "+testnode+" throwable: "+t; system.out.println(msg); system.out.println(tools.getstacktrace(t)); serviceresult serviceresult = new serviceresult(); serviceresult.error = msg; serviceresult.failurereason = " : system error; "; results.add(serviceresult); } } if (tools.istrue(autodeleteposts) && param_autodeleteposts){ autodelete(serviceresultsmap, "default", 0); } } file m = new file(controlfilename); string localname = m.getname() string reportname = localname+'-'+testgroupid+".html"; file resultfile = report.savereport(xmlreplaybasedir, reportsdir, reportname); if (resultfile!=null) { string toc = report.gettoc(reportname); reportslist.add(toc); } return results; }