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