]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
33f996b64c4aebe8348047dcd71a2551aa0ad091
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.IntegrationTests.xmlreplay;\r
2 \r
3 import org.apache.commons.cli.*;\r
4 \r
5 import org.apache.commons.jexl2.JexlContext;\r
6 import org.apache.commons.jexl2.JexlEngine;\r
7 import org.apache.commons.jexl2.MapContext;\r
8 import org.dom4j.Document;\r
9 import org.dom4j.DocumentException;\r
10 import org.dom4j.Node;\r
11 import org.dom4j.io.SAXReader;\r
12 \r
13 import java.io.*;\r
14 import java.util.*;\r
15 \r
16 /**  This class is used to replay a request to the Services layer, by sending the XML payload\r
17  *   in an appropriate Multipart request.\r
18  *   See example usage in calling class XmlReplayTest in services/IntegrationTests, and also in main() in this class.\r
19  *   @author Laramie Crocker\r
20  */\r
21 public class XmlReplay {\r
22 \r
23     public XmlReplay(String basedir){\r
24         this.basedir = basedir;\r
25         this.serviceResultsMap = createResultsMap();\r
26     }\r
27 \r
28     public static final String DEFAULT_CONTROL = "xml-replay-control.xml";\r
29     public static final String DEFAULT_MASTER_CONTROL = "xml-replay-master.xml";\r
30 \r
31     private String basedir = ".";  //set from constructor.\r
32     public String getBaseDir(){\r
33         return basedir;\r
34     }\r
35     \r
36     private String controlFileName = DEFAULT_CONTROL;\r
37     public String getControlFileName() {\r
38         return controlFileName;\r
39     }\r
40     public void setControlFileName(String controlFileName) {\r
41         this.controlFileName = controlFileName;\r
42     }\r
43 \r
44     private String protoHostPort = "";\r
45     public String getProtoHostPort() {\r
46         return protoHostPort;\r
47     }\r
48     public void setProtoHostPort(String protoHostPort) {\r
49         this.protoHostPort = protoHostPort;\r
50     }\r
51 \r
52     private boolean autoDeletePOSTS = true;\r
53     public boolean isAutoDeletePOSTS() {\r
54         return autoDeletePOSTS;\r
55     }\r
56     public void setAutoDeletePOSTS(boolean autoDeletePOSTS) {\r
57         this.autoDeletePOSTS = autoDeletePOSTS;\r
58     }\r
59 \r
60     private Dump dump;\r
61     public Dump getDump() {\r
62         return dump;\r
63     }\r
64     public void setDump(Dump dump) {\r
65         this.dump = dump;\r
66     }\r
67 \r
68     AuthsMap defaultAuthsMap;\r
69     public AuthsMap getDefaultAuthsMap(){\r
70         return defaultAuthsMap;\r
71     }\r
72     public void setDefaultAuthsMap(AuthsMap authsMap){\r
73         defaultAuthsMap = authsMap;\r
74     }\r
75 \r
76     private Map<String, ServiceResult> serviceResultsMap;\r
77     public Map<String, ServiceResult> getServiceResultsMap(){\r
78         return serviceResultsMap;\r
79     }\r
80     public static Map<String, ServiceResult> createResultsMap(){\r
81         return new HashMap<String, ServiceResult>();\r
82     }\r
83 \r
84 \r
85     public String toString(){\r
86         return "XmlReplay{"+this.basedir+", "+this.controlFileName+", "+this.defaultAuthsMap+", "+this.dump+'}';\r
87     }\r
88 \r
89     // ============== METHODS ===========================================================\r
90 \r
91     public Document openMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
92         Document document = getDocument(Tools.glue(basedir, "/", masterFilename)); //will check full path first, then checks relative to PWD.\r
93         if (document == null){\r
94             throw new FileNotFoundException("XmlReplay master control file ("+masterFilename+") not found in basedir: "+basedir+". Exiting test.");\r
95         }\r
96         return document;\r
97     }\r
98 \r
99     /** specify the master config file, relative to getBaseDir(), but ignore any tests or testGroups in the master.\r
100      *  @return a Document object, which you don't need to use: all options will be stored in XmlReplay instance.\r
101      */\r
102     public Document readOptionsFromMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
103         Document document = openMasterConfigFile(masterFilename);\r
104         protoHostPort = document.selectSingleNode("/xmlReplayMaster/protoHostPort").getText().trim();\r
105         AuthsMap authsMap = readAuths(document);\r
106         setDefaultAuthsMap(authsMap);\r
107         Dump dump = XmlReplay.readDumpOptions(document);\r
108         setDump(dump);\r
109         return document;\r
110     }\r
111 \r
112     public List<List<ServiceResult>> runMaster(String masterFilename) throws Exception {\r
113         return runMaster(masterFilename, true);\r
114     }\r
115 \r
116     /** Creates new instances of XmlReplay, one for each controlFile specified in the master,\r
117      *  and setting defaults from this instance, but not sharing ServiceResult objects or maps. */\r
118     public List<List<ServiceResult>> runMaster(String masterFilename, boolean readOptionsFromMaster) throws Exception {\r
119         List<List<ServiceResult>> list = new ArrayList<List<ServiceResult>>();\r
120         Document document;\r
121         if (readOptionsFromMaster){\r
122             document = readOptionsFromMasterConfigFile(masterFilename);\r
123         } else {\r
124             document = openMasterConfigFile(masterFilename);\r
125         }\r
126         String controlFile, testGroup, test;\r
127         List<Node> runNodes;\r
128         runNodes = document.selectNodes("/xmlReplayMaster/run");\r
129         for (Node runNode : runNodes) {\r
130             controlFile = runNode.valueOf("@controlFile");\r
131             testGroup = runNode.valueOf("@testGroup");\r
132             test = runNode.valueOf("@test"); //may be empty\r
133 \r
134             //Create a new instance and clone only config values, not any results maps.\r
135             XmlReplay replay = new XmlReplay(basedir);\r
136             replay.setControlFileName(controlFile);\r
137             replay.setProtoHostPort(protoHostPort);\r
138             replay.setAutoDeletePOSTS(isAutoDeletePOSTS());\r
139             replay.setDump(dump);\r
140             replay.setDefaultAuthsMap(getDefaultAuthsMap());\r
141 \r
142             //Now run *that* instance.\r
143             List<ServiceResult> results = replay.runTests(testGroup, test);\r
144             list.add(results);\r
145         }\r
146         return list;\r
147     }\r
148 \r
149     /** Use this if you wish to named tests within a testGroup, otherwise call runTestGroup(). */\r
150     public List<ServiceResult>  runTests(String testGroupID, String testID) throws Exception {\r
151         List<ServiceResult> result = runXmlReplayFile(this.basedir,\r
152                                 this.controlFileName,\r
153                                 testGroupID,\r
154                                 testID,\r
155                                 this.serviceResultsMap,\r
156                                 this.autoDeletePOSTS,\r
157                                 dump,\r
158                                 this.protoHostPort,\r
159                                 this.defaultAuthsMap);\r
160         return result;\r
161     }\r
162 \r
163     /** Use this if you wish to specify just ONE test to run within a testGroup, otherwise call runTestGroup(). */\r
164     public ServiceResult  runTest(String testGroupID, String testID) throws Exception {\r
165         List<ServiceResult> result = runXmlReplayFile(this.basedir,\r
166                                 this.controlFileName,\r
167                                 testGroupID,\r
168                                 testID,\r
169                                 this.serviceResultsMap,\r
170                                 this.autoDeletePOSTS,\r
171                                 dump,\r
172                                 this.protoHostPort,\r
173                                 this.defaultAuthsMap);\r
174         if (result.size()>1){\r
175             throw new IndexOutOfBoundsException("Multiple ("+result.size()+") tests with ID='"+testID+"' were found within test group '"+testGroupID+"', but there should only be one test per ID attribute.");\r
176         }\r
177         return result.get(0);\r
178     }\r
179 \r
180     /** Use this if you wish to run all tests within a testGroup.*/\r
181     public List<ServiceResult> runTestGroup(String testGroupID) throws Exception {\r
182         //NOTE: calling runTest with empty testID runs all tests in a test group, but don't expose this fact.\r
183         // Expose this method (runTestGroup) instead.\r
184         return runTests(testGroupID, "");\r
185     }\r
186 \r
187     public List<ServiceResult>  autoDelete(String logName){\r
188         return autoDelete(this.serviceResultsMap, logName);\r
189     }\r
190 \r
191     /** Use this method to clean up resources created on the server that returned CSIDs, if you have\r
192      *  specified autoDeletePOSTS==false, which means you are managing the cleanup yourself.\r
193      * @param serviceResultsMap a Map of ServiceResult objects, which will contain ServiceResult.deleteURL.\r
194      * @return a List<String> of debug info about which URLs could not be deleted.\r
195      */\r
196     public static List<ServiceResult> autoDelete(Map<String, ServiceResult> serviceResultsMap, String logName){\r
197         List<ServiceResult> results = new ArrayList<ServiceResult>();\r
198         for (ServiceResult pr : serviceResultsMap.values()){\r
199             try {\r
200                 ServiceResult deleteResult = XmlReplayTransport.doDELETE(pr.deleteURL, pr.auth, pr.testID, "[autodelete:"+logName+"]");\r
201                 results.add(deleteResult);\r
202             } catch (Throwable t){\r
203                 String s = (pr!=null) ? "ERROR while cleaning up ServiceResult map: "+pr+" for "+pr.deleteURL+" :: "+t\r
204                                       : "ERROR while cleaning up ServiceResult map (null ServiceResult): "+t;\r
205                 System.err.println(s);\r
206                 ServiceResult errorResult = new ServiceResult();\r
207                 errorResult.fullURL = pr.fullURL;\r
208                 errorResult.testGroupID = pr.testGroupID;\r
209                 errorResult.fromTestID = pr.fromTestID;\r
210                 errorResult.error = s;\r
211                 results.add(errorResult);\r
212             }\r
213         }\r
214         return results;\r
215     }\r
216 \r
217     public static class AuthsMap {\r
218         Map<String,String> map;\r
219         String defaultID="";\r
220         public String getDefaultAuth(){\r
221             return map.get(defaultID);\r
222         }\r
223         public String toString(){\r
224             return "AuthsMap: {default='"+defaultID+"'; "+map.keySet()+'}';\r
225         }\r
226     }\r
227 \r
228     public static AuthsMap readAuths(Document document){\r
229     Map<String, String> map = new HashMap<String, String>();\r
230         List<Node> authNodes = document.selectNodes("//auths/auth");\r
231         for (Node auth : authNodes) {\r
232             map.put(auth.valueOf("@ID"), auth.getStringValue());\r
233         }\r
234         AuthsMap authsMap = new AuthsMap();\r
235         Node auths = document.selectSingleNode("//auths");\r
236         String defaultID = "";\r
237         if (auths != null){\r
238             defaultID = auths.valueOf("@default");\r
239         }\r
240         authsMap.map = map;\r
241         authsMap.defaultID = defaultID;\r
242         return authsMap;\r
243     }\r
244 \r
245     public static class Dump {\r
246         public boolean payloads = false;\r
247         //public static final ServiceResult.DUMP_OPTIONS dumpServiceResultOptions = ServiceResult.DUMP_OPTIONS;\r
248         public ServiceResult.DUMP_OPTIONS dumpServiceResult = ServiceResult.DUMP_OPTIONS.minimal;\r
249         public String toString(){\r
250             return "payloads: "+payloads+" dumpServiceResult: "+dumpServiceResult;\r
251         }\r
252     }\r
253 \r
254     public static Dump getDumpConfig(){\r
255         return new Dump();\r
256     }\r
257 \r
258     public static Dump readDumpOptions(Document document){\r
259         Dump dump = getDumpConfig();\r
260         Node dumpNode = document.selectSingleNode("//dump");\r
261         if (dumpNode != null){\r
262             dump.payloads = Tools.isTrue(dumpNode.valueOf("@payloads"));\r
263             String dumpServiceResultStr = dumpNode.valueOf("@dumpServiceResult");\r
264             if (Tools.notEmpty(dumpServiceResultStr)){\r
265                 dump.dumpServiceResult = ServiceResult.DUMP_OPTIONS.valueOf(dumpServiceResultStr);\r
266             }\r
267         }\r
268         return dump;\r
269     }\r
270 \r
271     private static class PartsStruct {\r
272         public List<String> partsList = new ArrayList<String>();\r
273         public List<String> filesList = new ArrayList<String>();\r
274         boolean bDoingSinglePartPayload = false;\r
275         String singlePartPayloadFilename = "";\r
276         String overrideTestID = "";\r
277         public static PartsStruct readParts(Node testNode, final String testID, String xmlReplayBaseDir){\r
278             PartsStruct result = new PartsStruct();\r
279             result.singlePartPayloadFilename = testNode.valueOf("filename");\r
280             String singlePartPayloadFilename = testNode.valueOf("filename");\r
281             if (Tools.notEmpty(singlePartPayloadFilename)){\r
282                 result.bDoingSinglePartPayload = true;\r
283                 result.singlePartPayloadFilename = xmlReplayBaseDir + '/' + singlePartPayloadFilename;\r
284             } else {\r
285                 result.bDoingSinglePartPayload = false;\r
286                 List<Node> parts = testNode.selectNodes("parts/part");\r
287                 if (parts == null || parts.size()==0){  //path is just /testGroup/test/part/\r
288                     String commonPartName = testNode.valueOf("part/label");\r
289                     String testfile = testNode.valueOf("part/filename");\r
290                     String fullTestFilename = xmlReplayBaseDir + '/' + testfile;\r
291                     if ( Tools.isEmpty(testID) ){\r
292                         result.overrideTestID = testfile; //It is legal to have a missing ID attribute, and rely on a unique filename.\r
293                     }\r
294                     result.partsList.add(commonPartName);\r
295                     result.filesList.add(fullTestFilename);\r
296                 } else { // path is /testGroup/test/parts/part/\r
297                     for (Node part : parts){\r
298                         String commonPartName = part.valueOf("label");\r
299                         String filename = part.valueOf("filename");\r
300                         String fullTestFilename = xmlReplayBaseDir + '/' + filename;\r
301                         if ( Tools.isEmpty(testID) ){  //if testID is empty, we'll use the *first*  filename as ID.\r
302                             result.overrideTestID = filename; //It is legal to have a missing ID attribute, and rely on a unique filename.\r
303                         }\r
304                         result.partsList.add(commonPartName);\r
305                         result.filesList.add(fullTestFilename);\r
306                     }\r
307                 }\r
308             }\r
309             return result;\r
310         }\r
311     }\r
312 \r
313     private static String fixupFullURL(String fullURL, String protoHostPort, String uri){\r
314         if ( ! uri.startsWith(protoHostPort)){\r
315             fullURL = Tools.glue(protoHostPort, "/", uri);\r
316         } else {\r
317             fullURL = uri;\r
318         }\r
319         return fullURL;\r
320     }\r
321 \r
322     private static String fromTestID(String fullURL, Node testNode, Map<String, ServiceResult> serviceResultsMap){\r
323         String fromTestID = testNode.valueOf("fromTestID");\r
324         if (Tools.notEmpty(fromTestID)){\r
325             ServiceResult getPR = serviceResultsMap.get(fromTestID);\r
326             if (getPR != null){\r
327                 fullURL = Tools.glue(fullURL, "/", getPR.location);\r
328             }\r
329         }\r
330         return fullURL;\r
331     }\r
332 \r
333     private static String CSIDfromTestID(Node testNode, Map<String, ServiceResult> serviceResultsMap){\r
334         String result = "";\r
335         String fromTestID = testNode.valueOf("fromTestID");\r
336         if (Tools.notEmpty(fromTestID)){\r
337             ServiceResult getPR = serviceResultsMap.get(fromTestID);\r
338             if (getPR != null){\r
339                 result = getPR.location;\r
340             }\r
341         }\r
342         return result;\r
343     }\r
344 \r
345 \r
346     public static org.dom4j.Document getDocument(String xmlFileName) {\r
347         Document document = null;\r
348         SAXReader reader = new SAXReader();\r
349         try {\r
350             document = reader.read(xmlFileName);\r
351         } catch (DocumentException e) {\r
352             System.out.println("ERROR reading document: "+e);\r
353             e.printStackTrace();\r
354         }\r
355         return document;\r
356     }\r
357 \r
358 \r
359     //================= runXmlReplayFile ======================================================\r
360 \r
361     public static List<ServiceResult> runXmlReplayFile(String xmlReplayBaseDir,\r
362                                           String controlFileName,\r
363                                           String testGroupID,\r
364                                           String oneTestID,\r
365                                           Map<String, ServiceResult> serviceResultsMap,\r
366                                           boolean param_autoDeletePOSTS,\r
367                                           Dump dump,\r
368                                           String protoHostPortParam,\r
369                                           AuthsMap defaultAuths)\r
370                                           throws Exception {\r
371         //Internally, we maintain two collections of ServiceResult:\r
372         //  the first is the return value of this method.\r
373         //  the second is the serviceResultsMap, which is used for keeping track of CSIDs created by POSTs, for later reference by DELETE, etc.\r
374         List<ServiceResult> results = new ArrayList<ServiceResult>();\r
375 \r
376         String controlFile = Tools.glue(xmlReplayBaseDir, "/", controlFileName);\r
377         Document document;\r
378         document = getDocument(controlFile); //will check full path first, then checks relative to PWD.\r
379         if (document==null){\r
380             throw new FileNotFoundException("XmlReplay control file ("+controlFileName+") not found in basedir: "+xmlReplayBaseDir+" Exiting test.");\r
381         }\r
382         String protoHostPort;\r
383         if (Tools.isEmpty(protoHostPortParam)){\r
384             protoHostPort = document.selectSingleNode("/xmlReplay/protoHostPort").getText().trim();\r
385             System.out.println("DEPRECATED: Using protoHostPort ('"+protoHostPort+"') from xmlReplay file ('"+controlFile+"'), not master.");\r
386         } else {\r
387             protoHostPort = protoHostPortParam;\r
388         }\r
389         if (Tools.isEmpty(protoHostPort)){\r
390             throw new Exception("XmlReplay control file must have a protoHostPort element");\r
391         }\r
392 \r
393         String authsMapINFO;\r
394         AuthsMap authsMap = readAuths(document);\r
395         if (authsMap.map.size()==0){\r
396             authsMap = defaultAuths;\r
397             authsMapINFO = "Using defaultAuths from master file: "+defaultAuths;\r
398         } else {\r
399             authsMapINFO = "Using AuthsMap from control file: "+authsMap;\r
400         }\r
401         System.out.println("XmlReplay running:"\r
402                           +"\r\n   controlFile: "+ (new File(controlFile).getCanonicalPath())\r
403                           +"\r\n   protoHostPort: "+protoHostPort\r
404                           +"\r\n   testGroup: "+testGroupID\r
405                           + (Tools.notEmpty(oneTestID) ? "\r\n   oneTestID: "+oneTestID : "")\r
406                           +"\r\n   AuthsMap: "+authsMapINFO\r
407                           +"\r\n   param_autoDeletePOSTS: "+param_autoDeletePOSTS\r
408                           +"\r\n   Dump info: "+dump\r
409                           +"\r\n");\r
410 \r
411         String autoDeletePOSTS = "";\r
412         List<Node> testgroupNodes;\r
413         if (Tools.notEmpty(testGroupID)){\r
414             testgroupNodes = document.selectNodes("//testGroup[@ID='"+testGroupID+"']");\r
415         } else {\r
416             testgroupNodes = document.selectNodes("//testGroup");\r
417         }\r
418 \r
419         JexlEngine jexl = new JexlEngine();   // Used for expression language expansion from uri field.\r
420         XmlReplayEval evalStruct = new XmlReplayEval();\r
421         evalStruct.serviceResultsMap = serviceResultsMap;\r
422         evalStruct.jexl = jexl;\r
423 \r
424         for (Node testgroup : testgroupNodes) {\r
425             JexlContext jc = new MapContext();  //Get a new JexlContext for each test group.\r
426             evalStruct.jc = jc;\r
427 \r
428             autoDeletePOSTS = testgroup.valueOf("@autoDeletePOSTS");\r
429             List<Node> tests;\r
430             if (Tools.notEmpty(oneTestID)){\r
431                 tests = testgroup.selectNodes("test[@ID='"+oneTestID+"']");\r
432             } else {\r
433                 tests = testgroup.selectNodes("test");\r
434             }\r
435             String authForTest = "";\r
436             int testElementIndex = -1;\r
437 \r
438             for (Node testNode : tests) {\r
439                 try {\r
440                     testElementIndex++;\r
441                     String testID = testNode.valueOf("@ID");\r
442                     String testIDLabel = Tools.notEmpty(testID) ? (testGroupID+'.'+testID) : (testGroupID+'.'+testElementIndex);\r
443                     String method = testNode.valueOf("method");\r
444                     String uri = testNode.valueOf("uri");\r
445                     String fullURL = Tools.glue(protoHostPort, "/", uri);\r
446                     String initURI = uri;\r
447 \r
448                     String authIDForTest = testNode.valueOf("@auth");\r
449                     String currentAuthForTest = authsMap.map.get(authIDForTest);\r
450                     if (Tools.notEmpty(currentAuthForTest)){\r
451                         authForTest = currentAuthForTest; //else just run with current from last loop;\r
452                     }\r
453                     if (Tools.isEmpty(authForTest)){\r
454                         authForTest = defaultAuths.getDefaultAuth();\r
455                     }\r
456 \r
457                     if (uri.indexOf("$")>-1){\r
458                         uri = evalStruct.eval(uri, serviceResultsMap, jexl, jc);\r
459                     }\r
460                     fullURL = fixupFullURL(fullURL, protoHostPort, uri);\r
461 \r
462                     List<Integer> expectedCodes = new ArrayList<Integer>();\r
463                     String expectedCodesStr = testNode.valueOf("expectedCodes");\r
464                     if (Tools.notEmpty(expectedCodesStr)){\r
465                          String[] codesArray = expectedCodesStr.split(",");\r
466                          for (String code : codesArray){\r
467                              expectedCodes.add(new Integer(code.trim()));\r
468                          }\r
469                     }\r
470 \r
471                     ServiceResult serviceResult;\r
472                     boolean isPOST = method.equalsIgnoreCase("POST");\r
473                     boolean isPUT =  method.equalsIgnoreCase("PUT");\r
474                     if ( isPOST || isPUT ) {\r
475                         PartsStruct parts = PartsStruct.readParts(testNode, testID, xmlReplayBaseDir);\r
476                         if (Tools.notEmpty(parts.overrideTestID)) {\r
477                             testID = parts.overrideTestID;\r
478                         }\r
479                         if (isPOST){\r
480                             String csid = CSIDfromTestID(testNode, serviceResultsMap);\r
481                             if (Tools.notEmpty(csid)) uri = Tools.glue(uri, "/", csid+"/items/");\r
482                         } else if (isPUT) {\r
483                             uri = fromTestID(uri, testNode, serviceResultsMap);\r
484                         }\r
485                         if (parts.bDoingSinglePartPayload){\r
486                             serviceResult = XmlReplayTransport.doPOST_PUTFromXML(parts.singlePartPayloadFilename, protoHostPort, uri, method, XmlReplayTransport.APPLICATION_XML, evalStruct, authForTest, testIDLabel);\r
487                         } else {\r
488                             serviceResult = XmlReplayTransport.doPOST_PUTFromXML_Multipart(parts.filesList, parts.partsList, protoHostPort, uri, method, evalStruct, authForTest, testIDLabel);\r
489                         }\r
490                         results.add(serviceResult);\r
491                         if (isPOST){\r
492                             serviceResultsMap.put(testID, serviceResult);      //PUTs do not return a Location, so don't add PUTs to serviceResultsMap.\r
493                         }\r
494                         fullURL = fixupFullURL(fullURL, protoHostPort, uri);\r
495                     } else if (method.equalsIgnoreCase("DELETE")){\r
496                         String fromTestID = testNode.valueOf("fromTestID");\r
497                         ServiceResult pr = serviceResultsMap.get(fromTestID);\r
498                         if (pr!=null){\r
499                             serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID);\r
500                             serviceResult.fromTestID = fromTestID;\r
501                             results.add(serviceResult);\r
502                             if (serviceResult.gotExpectedResult()){\r
503                                 serviceResultsMap.remove(fromTestID);\r
504                             }\r
505                         } else {\r
506                             if (Tools.notEmpty(fromTestID)){\r
507                                 serviceResult = new ServiceResult();\r
508                                 serviceResult.responseCode = 0;\r
509                                 serviceResult.error = "ID not found in element fromTestID: "+fromTestID;\r
510                                 System.err.println("****\r\nServiceResult: "+serviceResult.error+". SKIPPING TEST. Full URL: "+fullURL);\r
511                             } else {\r
512                                 serviceResult = XmlReplayTransport.doDELETE(fullURL, authForTest, testID, fromTestID);\r
513                             }\r
514                             serviceResult.fromTestID = fromTestID;\r
515                             results.add(serviceResult);\r
516                         }\r
517                     } else if (method.equalsIgnoreCase("GET")){\r
518                         fullURL = fromTestID(fullURL, testNode, serviceResultsMap);\r
519                         serviceResult = XmlReplayTransport.doGET(fullURL, authForTest, testIDLabel);\r
520                         results.add(serviceResult);\r
521                     } else if (method.equalsIgnoreCase("LIST")){\r
522                         fullURL = fixupFullURL(fullURL, protoHostPort, uri);\r
523                         String listQueryParams = ""; //TODO: empty for now, later may pick up from XML control file.\r
524                         serviceResult = XmlReplayTransport.doLIST(fullURL, listQueryParams, authForTest, testIDLabel);\r
525                         results.add(serviceResult);\r
526                     } else {\r
527                         throw new Exception("HTTP method not supported by XmlReplay: "+method);\r
528                     }\r
529 \r
530                     serviceResult.testID = testID;\r
531                     serviceResult.fullURL = fullURL;\r
532                     serviceResult.auth = authForTest;\r
533                     serviceResult.method = method;\r
534                     if (expectedCodes.size()>0){\r
535                         serviceResult.expectedCodes = expectedCodes;\r
536                     }\r
537                     if (Tools.isEmpty(serviceResult.testID)) serviceResult.testID = testIDLabel;\r
538                     if (Tools.isEmpty(serviceResult.testGroupID)) serviceResult.testGroupID = testGroupID;\r
539 \r
540                     String serviceResultRow = serviceResult.dump(dump.dumpServiceResult);\r
541                     String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": "";\r
542                     System.out.println(leader+serviceResultRow+"\r\n");\r
543                     if (dump.payloads) System.out.println(serviceResult.result);\r
544                 } catch (Throwable t) {\r
545                     System.out.println("ERROR: XmlReplay experienced an error in a test node: "+testNode+" Throwable: "+t);\r
546                 }\r
547             }\r
548             if (Tools.isTrue(autoDeletePOSTS)&&param_autoDeletePOSTS){\r
549                 autoDelete(serviceResultsMap, "default");\r
550             }\r
551         }\r
552         return results;\r
553     }\r
554 \r
555 \r
556     //======================== MAIN ===================================================================\r
557 \r
558     private static Options createOptions() {\r
559         Options options = new Options();\r
560         options.addOption("xmlReplayBaseDir", true, "default/basedir");\r
561         return options;\r
562     }\r
563 \r
564     public static String usage(){\r
565         String result = "org.collectionspace.services.IntegrationTests.xmlreplay.XmlReplay {args}\r\n"\r
566                         +"  -xmlReplayBaseDir <dir> \r\n"\r
567                         +" You may also override these with system args, e.g.: \r\n"\r
568                         +"   -DxmlReplayBaseDir=/path/to/dir \r\n"\r
569                         +" These may also be passed in via the POM.\r\n"\r
570                         +" You can also set these system args, e.g.: \r\n"\r
571                         +"  -DtestGroupID=<oneID> \r\n"\r
572                         +"  -DtestID=<one TestGroup ID>"\r
573                         +"  -DautoDeletePOSTS=<true|false> \r\n"\r
574                         +"    (note: -DautoDeletePOSTS won't force deletion if set to false in control file.";\r
575         return result;\r
576     }\r
577 \r
578     private static String opt(CommandLine line, String option){\r
579         String result;\r
580         String fromProps = System.getProperty(option);\r
581         if (Tools.notEmpty(fromProps)){\r
582             return fromProps;\r
583         }\r
584         result = line.getOptionValue(option);\r
585         if (result == null){\r
586             result = "";\r
587         }\r
588         return result;\r
589     }\r
590 \r
591     public static void main(String[]args) throws Exception {\r
592         Options options = createOptions();\r
593         //System.out.println("System CLASSPATH: "+prop.getProperty("java.class.path", null));\r
594         CommandLineParser parser = new GnuParser();\r
595         try {\r
596             // parse the command line arguments\r
597             CommandLine line = parser.parse(options, args);\r
598 \r
599             String xmlReplayBaseDir = opt(line, "xmlReplayBaseDir");\r
600             String testGroupID      = opt(line, "testGroupID");\r
601             String testID           = opt(line, "testID");\r
602             String autoDeletePOSTS  = opt(line, "autoDeletePOSTS");\r
603             String dumpResults      = opt(line, "dumpResults");\r
604             String controlFilename   = opt(line, "controlFilename");\r
605             String xmlReplayMaster  = opt(line, "xmlReplayMaster");\r
606 \r
607             xmlReplayBaseDir = Tools.fixFilename(xmlReplayBaseDir);\r
608             controlFilename = Tools.fixFilename(controlFilename);\r
609 \r
610             boolean bAutoDeletePOSTS = true;\r
611             if (Tools.notEmpty(autoDeletePOSTS)) {\r
612                 bAutoDeletePOSTS = Tools.isTrue(autoDeletePOSTS);\r
613             }\r
614             boolean bDumpResults = false;\r
615             if (Tools.notEmpty(dumpResults)) {\r
616                 bDumpResults = Tools.isTrue(autoDeletePOSTS);\r
617             }\r
618             if (Tools.isEmpty(xmlReplayBaseDir)){\r
619                 System.err.println("ERROR: xmlReplayBaseDir was not specified.");\r
620                 return;\r
621             }\r
622             File f = new File(Tools.glue(xmlReplayBaseDir, "/", controlFilename));\r
623             if (Tools.isEmpty(xmlReplayMaster) && !f.exists()){\r
624                 System.err.println("Control file not found: "+f.getCanonicalPath());\r
625                 return;\r
626             }\r
627             File fMaster = new File(Tools.glue(xmlReplayBaseDir, "/", xmlReplayMaster));\r
628             if (Tools.notEmpty(xmlReplayMaster)  && !fMaster.exists()){\r
629                 System.err.println("Master file not found: "+fMaster.getCanonicalPath());\r
630                 return;\r
631             }\r
632 \r
633             String xmlReplayBaseDirResolved = (new File(xmlReplayBaseDir)).getCanonicalPath();\r
634             System.out.println("XmlReplay ::"\r
635                             + "\r\n    xmlReplayBaseDir: "+xmlReplayBaseDir\r
636                             + "\r\n    xmlReplayBaseDir(resolved): "+xmlReplayBaseDirResolved\r
637                             + "\r\n    controlFilename: "+controlFilename\r
638                             + "\r\n    xmlReplayMaster: "+xmlReplayMaster\r
639                             + "\r\n    testGroupID: "+testGroupID\r
640                             + "\r\n    testID: "+testID\r
641                             + "\r\n    autoDeletePOSTS: "+bAutoDeletePOSTS\r
642                             + (Tools.notEmpty(xmlReplayMaster)\r
643                                        ? ("\r\n    will use master file: "+fMaster.getCanonicalPath())\r
644                                        : ("\r\n    will use control file: "+f.getCanonicalPath()) )\r
645                              );\r
646             \r
647             if (Tools.notEmpty(xmlReplayMaster)){\r
648                 if (Tools.notEmpty(controlFilename)){\r
649                     System.out.println("WARN: controlFilename: "+controlFilename+" will not be used because master was specified.  Running master: "+xmlReplayMaster);\r
650                 }\r
651                 XmlReplay replay = new XmlReplay(xmlReplayBaseDirResolved);\r
652                 replay.readOptionsFromMasterConfigFile(xmlReplayMaster);\r
653                 replay.setAutoDeletePOSTS(bAutoDeletePOSTS);\r
654                 Dump dumpFromMaster = replay.getDump();\r
655                 dumpFromMaster.payloads = Tools.isTrue(dumpResults);\r
656                 replay.setDump(dumpFromMaster);\r
657                 replay.runMaster(xmlReplayMaster, false); //false, because we already just read the options, and override a few.\r
658             } else {\r
659                 Dump dump = getDumpConfig();\r
660                 dump.payloads = Tools.isTrue(dumpResults);\r
661                 runXmlReplayFile(xmlReplayBaseDirResolved, controlFilename, testGroupID, testID, createResultsMap(), bAutoDeletePOSTS, dump, "", null);\r
662             }\r
663         } catch (ParseException exp) {\r
664             // oops, something went wrong\r
665             System.err.println("Cmd-line parsing failed.  Reason: " + exp.getMessage());\r
666             System.err.println(usage());\r
667         } catch (Exception e) {\r
668             System.out.println("Error : " + e.getMessage());\r
669             e.printStackTrace();\r
670         }\r
671     }\r
672 \r
673 }\r