//if (isPOST){
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;
    }
}
results.add(serviceResult);
//if (isPOST){
serviceResultsMap.put(testID, serviceResult); //PUTs do not return a Location, so don't add PUTs to 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);
//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){
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;