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