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