From fcd53c22f2457e0515ed5307475aa3fa20f3c080 Mon Sep 17 00:00:00 2001 From: Laramie Crocker Date: Tue, 9 Nov 2010 20:51:09 +0000 Subject: [PATCH] NOJIRA: merging from branch sprint --- .../xmlreplay/ServiceResult.java | 124 ++++ .../IntegrationTests/xmlreplay/Tools.java | 126 ++++ .../IntegrationTests/xmlreplay/XmlReplay.java | 668 ++++++++++++++++++ .../xmlreplay/XmlReplayEval.java | 114 +++ .../xmlreplay/XmlReplayTest.java | 179 +++++ .../xmlreplay/XmlReplayTransport.java | 237 +++++++ 6 files changed, 1448 insertions(+) create mode 100755 services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java create mode 100755 services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/Tools.java create mode 100755 services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java create mode 100755 services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayEval.java create mode 100755 services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTest.java create mode 100755 services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTransport.java diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java new file mode 100755 index 000000000..04f8f55c5 --- /dev/null +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/ServiceResult.java @@ -0,0 +1,124 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Copyright (c) 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.collectionspace.services.IntegrationTests.xmlreplay; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: laramie + * $LastChangedRevision: $ + * $LastChangedDate: $ + */ +public class ServiceResult { + public String testID = ""; + public String testGroupID = ""; + public String fullURL = ""; + public String deleteURL = ""; + public String location = ""; + public String CSID = ""; + public String subresourceCSID = ""; + public String result = ""; + public int responseCode = 0; + public String responseMessage = ""; + public String method = ""; + public String error = ""; + public String fromTestID = ""; + public String auth = ""; + public List expectedCodes = new ArrayList(); + public boolean codeInSuccessRange(int code){ + if (0<=code && code<200){ + return false; + } else if (400<=code) { + return false; + } + return true; + } + public boolean gotExpectedResult(){ + for (Integer oneExpected : expectedCodes){ + if (responseCode == oneExpected){ + return true; + } + } + if (expectedCodes.size()>0 && codeInSuccessRange(responseCode)){ //none found, but result expected. + for (Integer oneExpected : expectedCodes){ + if ( ! codeInSuccessRange(oneExpected)){ + return false; + } + } + } + return codeInSuccessRange(responseCode); + } + //public static final String[] DUMP_OPTIONS = {"minimal", "detailed", "full"}; + public static enum DUMP_OPTIONS {minimal, detailed, full}; + + public String toString(){ + return detail(true); + + } + public String detail(boolean includePayloads){ + return "{ServiceResult: " + + ( Tools.notEmpty(testID) ? " testID:"+testID : "" ) + + ( Tools.notEmpty(testGroupID) ? "; testGroupID:"+testGroupID : "" ) + + ( Tools.notEmpty(fromTestID) ? "; fromTestID:"+fromTestID : "" ) + +"; "+method + +"; "+responseCode + + ( Tools.notEmpty(responseMessage) ? "; msg:"+responseMessage : "" ) + +"; URL:"+fullURL + +"; auth: "+auth + + ( Tools.notEmpty(deleteURL) ? "; deleteURL:"+deleteURL : "" ) + + ( Tools.notEmpty(location) ? "; location.CSID:"+location : "" ) + + ( Tools.notEmpty(error) ? "; ERROR:"+error : "" ) + + ( (expectedCodes.size()>0) ? "; expectedCodes:"+expectedCodes : "" ) + + "; gotExpected:"+gotExpectedResult() + + ( includePayloads && Tools.notEmpty(result) ? "; result:"+result : "" ) + +"}"; + } + public String minimal(){ + return "{" + + ( gotExpectedResult() ? "SUCCESS" : "FAILURE" ) + + + ( Tools.notEmpty(testID) ? "; "+testID : "" ) + +"; "+method + +"; "+responseCode + + (expectedCodes.size()>0 ? "; expected:"+expectedCodes : "") + + ( Tools.notEmpty(responseMessage) ? "; msg:"+responseMessage : "" ) + +"; URL:"+fullURL + +"; auth: "+auth + + ( Tools.notEmpty(error) ? "; ERROR:"+error : "" ) + +"}"; + } + public String dump(ServiceResult.DUMP_OPTIONS opt){ + switch (opt){ + case minimal: + return minimal(); + case detailed: + return detail(false); + case full: + return detail(true); + default: + return toString(); + } + } +} diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/Tools.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/Tools.java new file mode 100755 index 000000000..bfd7ebf63 --- /dev/null +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/Tools.java @@ -0,0 +1,126 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Copyright (c) 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.collectionspace.services.IntegrationTests.xmlreplay; + +import org.apache.commons.jexl2.Expression; +import org.apache.commons.jexl2.JexlContext; +import org.apache.commons.jexl2.JexlEngine; +import org.apache.commons.jexl2.MapContext; + +import java.io.File; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** General utility methods. + * @author Laramie Crocker + */ +public class Tools { + /** @return first glued to second with the separator string, at most one time - useful for appending paths. + */ + public static String glue(String first, String separator, String second){ + if (first==null) { first = ""; } + if (second==null) { second = ""; } + if (separator==null) { separator = ""; } + if (first.startsWith(separator) && second.startsWith(separator)){ + return first.substring(0, first.length()-separator.length()) + second; + } + if (first.endsWith(separator) || second.startsWith(separator)){ + return first+second; + } + return first+separator+second; + } + + /** Handles null strings as empty. */ + public static boolean isEmpty(String str){ + return !notEmpty(str); + } + + /** Handles null strings as empty. */ + public static boolean notEmpty(String str){ + if (str==null) return false; + if (str.length()==0) return false; + return true; + } + + /** Handles null strings as false. */ + public static boolean isTrue(String test){ + return notEmpty(test) && (new Boolean(test)).booleanValue(); + } + + /* Example usage of searchAndReplace: + for (Map.Entry entry : variablesMap.entrySet()){ + String key = entry.getKey(); + String replace = entry.getValue(); + String find = "\\$\\{"+key+"\\}"; //must add expression escapes + //because $ and braces are "special", and we want to find "${object.CSID}" + uri = Tools.searchAndReplace(uri, find, replace); + System.out.println("---- REPLACE.uri: "+initURI); + System.out.println("---- REPLACE.find: "+find); + System.out.println("---- REPLACE.replace: "+replace); + System.out.println("---- REPLACE.uri result: "+uri); + } + */ + public static String searchAndReplace(String source, String find, String replace){ + Pattern pattern = Pattern.compile(find); + Matcher matcher = pattern.matcher(source); + String output = matcher.replaceAll(replace); + return output; + } + + static boolean m_fileSystemIsDOS = "\\".equals(File.separator); + static boolean m_fileSystemIsMac = ":".equals(File.separator); + + public static boolean fileSystemIsDOS(){return m_fileSystemIsDOS;} + public static boolean fileSystemIsMac(){return m_fileSystemIsMac;} + + public static String fixFilename(String filename){ + if ( m_fileSystemIsDOS ) { + return filename.replace('/', '\\'); + } + if ( m_fileSystemIsMac ) { + String t = filename.replace('/', ':'); + t = t.replace('\\', ':'); + return t; + } + return filename.replace('\\','/'); + } + + public static String join(String dir, String file){ + if ( dir.length() == 0 ) { + return file; + } + dir = Tools.fixFilename(dir); + file = Tools.fixFilename(file); + if ( ! dir.endsWith(File.separator) ) { + dir += File.separator; + } + if ( file.startsWith(File.separator) ) { + file = file.substring(1); + } + return dir + file; + } + + + +} diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java new file mode 100755 index 000000000..c12b3faef --- /dev/null +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplay.java @@ -0,0 +1,668 @@ +package org.collectionspace.services.IntegrationTests.xmlreplay; + +import org.apache.commons.cli.*; + +import org.apache.commons.jexl2.JexlContext; +import org.apache.commons.jexl2.JexlEngine; +import org.apache.commons.jexl2.MapContext; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Node; +import org.dom4j.io.SAXReader; + +import java.io.*; +import java.util.*; + +/** This class is used to replay a request to the Services layer, by sending the XML payload + * in an appropriate Multipart request. + * See example usage in calling class XmlReplayTest in services/IntegrationTests, and also in main() in this class. + * @author Laramie Crocker + */ +public class XmlReplay { + + public XmlReplay(String basedir){ + this.basedir = basedir; + this.serviceResultsMap = createResultsMap(); + } + + public static final String DEFAULT_CONTROL = "xml-replay-control.xml"; + public static final String DEFAULT_MASTER_CONTROL = "xml-replay-master.xml"; + + private String basedir = "."; //set from constructor. + public String getBaseDir(){ + return basedir; + } + + private String controlFileName = DEFAULT_CONTROL; + public String getControlFileName() { + return controlFileName; + } + public void setControlFileName(String controlFileName) { + this.controlFileName = controlFileName; + } + + private String protoHostPort = ""; + public String getProtoHostPort() { + return protoHostPort; + } + public void setProtoHostPort(String protoHostPort) { + this.protoHostPort = protoHostPort; + } + + private boolean autoDeletePOSTS = true; + public boolean isAutoDeletePOSTS() { + return autoDeletePOSTS; + } + public void setAutoDeletePOSTS(boolean autoDeletePOSTS) { + this.autoDeletePOSTS = autoDeletePOSTS; + } + + private Dump dump; + public Dump getDump() { + return dump; + } + public void setDump(Dump dump) { + this.dump = dump; + } + + AuthsMap defaultAuthsMap; + public AuthsMap getDefaultAuthsMap(){ + return defaultAuthsMap; + } + public void setDefaultAuthsMap(AuthsMap authsMap){ + defaultAuthsMap = authsMap; + } + + private Map serviceResultsMap; + public Map getServiceResultsMap(){ + return serviceResultsMap; + } + public static Map createResultsMap(){ + return new HashMap(); + } + + + public String toString(){ + return "XmlReplay{"+this.basedir+", "+this.controlFileName+", "+this.defaultAuthsMap+", "+this.dump+'}'; + } + + // ============== METHODS =========================================================== + + public Document openMasterConfigFile(String masterFilename) throws FileNotFoundException { + Document document = getDocument(Tools.glue(basedir, "/", masterFilename)); //will check full path first, then checks relative to PWD. + if (document == null){ + throw new FileNotFoundException("XmlReplay master control file ("+masterFilename+") not found in basedir: "+basedir+". Exiting test."); + } + return document; + } + + /** specify the master config file, relative to getBaseDir(), but ignore any tests or testGroups in the master. + * @return a Document object, which you don't need to use: all options will be stored in XmlReplay instance. + */ + public Document readOptionsFromMasterConfigFile(String masterFilename) throws FileNotFoundException { + Document document = openMasterConfigFile(masterFilename); + protoHostPort = document.selectSingleNode("/xmlReplayMaster/protoHostPort").getText().trim(); + AuthsMap authsMap = readAuths(document); + setDefaultAuthsMap(authsMap); + Dump dump = XmlReplay.readDumpOptions(document); + setDump(dump); + return document; + } + + public List> runMaster(String masterFilename) throws Exception { + return runMaster(masterFilename, true); + } + + /** Creates new instances of XmlReplay, one for each controlFile specified in the master, + * and setting defaults from this instance, but not sharing ServiceResult objects or maps. */ + public List> runMaster(String masterFilename, boolean readOptionsFromMaster) throws Exception { + List> list = new ArrayList>(); + Document document; + if (readOptionsFromMaster){ + document = readOptionsFromMasterConfigFile(masterFilename); + } else { + document = openMasterConfigFile(masterFilename); + } + String controlFile, testGroup, test; + List runNodes; + runNodes = document.selectNodes("/xmlReplayMaster/run"); + for (Node runNode : runNodes) { + controlFile = runNode.valueOf("@controlFile"); + testGroup = runNode.valueOf("@testGroup"); + test = runNode.valueOf("@test"); //may be empty + + //Create a new instance and clone only config values, not any results maps. + XmlReplay replay = new XmlReplay(basedir); + replay.setControlFileName(controlFile); + replay.setProtoHostPort(protoHostPort); + replay.setAutoDeletePOSTS(isAutoDeletePOSTS()); + replay.setDump(dump); + replay.setDefaultAuthsMap(getDefaultAuthsMap()); + + //Now run *that* instance. + List results = replay.runTests(testGroup, test); + list.add(results); + } + return list; + } + + /** Use this if you wish to named tests within a testGroup, otherwise call runTestGroup(). */ + public List runTests(String testGroupID, String testID) throws Exception { + List result = runXmlReplayFile(this.basedir, + this.controlFileName, + testGroupID, + testID, + this.serviceResultsMap, + this.autoDeletePOSTS, + dump, + this.protoHostPort, + this.defaultAuthsMap); + return result; + } + + /** Use this if you wish to specify just ONE test to run within a testGroup, otherwise call runTestGroup(). */ + public ServiceResult runTest(String testGroupID, String testID) throws Exception { + List result = runXmlReplayFile(this.basedir, + this.controlFileName, + testGroupID, + testID, + this.serviceResultsMap, + this.autoDeletePOSTS, + dump, + this.protoHostPort, + this.defaultAuthsMap); + if (result.size()>1){ + 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."); + } + return result.get(0); + } + + /** Use this if you wish to run all tests within a testGroup.*/ + public List runTestGroup(String testGroupID) throws Exception { + //NOTE: calling runTest with empty testID runs all tests in a test group, but don't expose this fact. + // Expose this method (runTestGroup) instead. + return runTests(testGroupID, ""); + } + + public List autoDelete(String logName){ + return autoDelete(this.serviceResultsMap, logName); + } + + /** Use this method to clean up resources created on the server that returned CSIDs, if you have + * specified autoDeletePOSTS==false, which means you are managing the cleanup yourself. + * @param serviceResultsMap a Map of ServiceResult objects, which will contain ServiceResult.deleteURL. + * @return a List of debug info about which URLs could not be deleted. + */ + public static List autoDelete(Map serviceResultsMap, String logName){ + List results = new ArrayList(); + for (ServiceResult pr : serviceResultsMap.values()){ + try { + ServiceResult deleteResult = XmlReplayTransport.doDELETE(pr.deleteURL, pr.auth, pr.testID, "[autodelete:"+logName+"]"); + results.add(deleteResult); + } catch (Throwable t){ + String s = (pr!=null) ? "ERROR while cleaning up ServiceResult map: "+pr+" for "+pr.deleteURL+" :: "+t + : "ERROR while cleaning up ServiceResult map (null ServiceResult): "+t; + System.err.println(s); + ServiceResult errorResult = new ServiceResult(); + errorResult.fullURL = pr.fullURL; + errorResult.testGroupID = pr.testGroupID; + errorResult.fromTestID = pr.fromTestID; + errorResult.error = s; + results.add(errorResult); + } + } + return results; + } + + public static class AuthsMap { + Map map; + String defaultID=""; + public String getDefaultAuth(){ + return map.get(defaultID); + } + public String toString(){ + return "AuthsMap: {default='"+defaultID+"'; "+map.keySet()+'}'; + } + } + + public static AuthsMap readAuths(Document document){ + Map map = new HashMap(); + List authNodes = document.selectNodes("//auths/auth"); + for (Node auth : authNodes) { + map.put(auth.valueOf("@ID"), auth.getStringValue()); + } + AuthsMap authsMap = new AuthsMap(); + Node auths = document.selectSingleNode("//auths"); + String defaultID = ""; + if (auths != null){ + defaultID = auths.valueOf("@default"); + } + authsMap.map = map; + authsMap.defaultID = defaultID; + return authsMap; + } + + public static class Dump { + public boolean payloads = false; + //public static final ServiceResult.DUMP_OPTIONS dumpServiceResultOptions = ServiceResult.DUMP_OPTIONS; + public ServiceResult.DUMP_OPTIONS dumpServiceResult = ServiceResult.DUMP_OPTIONS.minimal; + public String toString(){ + return "payloads: "+payloads+" dumpServiceResult: "+dumpServiceResult; + } + } + + public static Dump getDumpConfig(){ + return new Dump(); + } + + public static Dump readDumpOptions(Document document){ + Dump dump = getDumpConfig(); + Node dumpNode = document.selectSingleNode("//dump"); + if (dumpNode != null){ + dump.payloads = Tools.isTrue(dumpNode.valueOf("@payloads")); + String dumpServiceResultStr = dumpNode.valueOf("@dumpServiceResult"); + if (Tools.notEmpty(dumpServiceResultStr)){ + dump.dumpServiceResult = ServiceResult.DUMP_OPTIONS.valueOf(dumpServiceResultStr); + } + } + return dump; + } + + private static class PartsStruct { + public List partsList = new ArrayList(); + public List filesList = new ArrayList(); + boolean bDoingSinglePartPayload = false; + String singlePartPayloadFilename = ""; + String overrideTestID = ""; + public static PartsStruct readParts(Node testNode, final String testID, String xmlReplayBaseDir){ + PartsStruct result = new PartsStruct(); + result.singlePartPayloadFilename = testNode.valueOf("filename"); + String singlePartPayloadFilename = testNode.valueOf("filename"); + if (Tools.notEmpty(singlePartPayloadFilename)){ + result.bDoingSinglePartPayload = true; + result.singlePartPayloadFilename = xmlReplayBaseDir + '/' + singlePartPayloadFilename; + } else { + result.bDoingSinglePartPayload = false; + List parts = testNode.selectNodes("parts/part"); + if (parts == null || parts.size()==0){ //path is just /testGroup/test/part/ + String commonPartName = testNode.valueOf("part/label"); + String testfile = testNode.valueOf("part/filename"); + String fullTestFilename = xmlReplayBaseDir + '/' + testfile; + if ( Tools.isEmpty(testID) ){ + result.overrideTestID = testfile; //It is legal to have a missing ID attribute, and rely on a unique filename. + } + result.partsList.add(commonPartName); + result.filesList.add(fullTestFilename); + } else { // path is /testGroup/test/parts/part/ + for (Node part : parts){ + String commonPartName = part.valueOf("label"); + String filename = part.valueOf("filename"); + String fullTestFilename = xmlReplayBaseDir + '/' + filename; + if ( Tools.isEmpty(testID) ){ //if testID is empty, we'll use the *first* filename as ID. + result.overrideTestID = filename; //It is legal to have a missing ID attribute, and rely on a unique filename. + } + result.partsList.add(commonPartName); + result.filesList.add(fullTestFilename); + } + } + } + return result; + } + } + + private static String fixupFullURL(String fullURL, String protoHostPort, String uri){ + if ( ! uri.startsWith(protoHostPort)){ + fullURL = Tools.glue(protoHostPort, "/", uri); + } else { + fullURL = uri; + } + return fullURL; + } + + private static String fromTestID(String fullURL, Node testNode, Map serviceResultsMap){ + String fromTestID = testNode.valueOf("fromTestID"); + if (Tools.notEmpty(fromTestID)){ + ServiceResult getPR = serviceResultsMap.get(fromTestID); + if (getPR != null){ + fullURL = Tools.glue(fullURL, "/", getPR.location); + } + } + return fullURL; + } + + private static String CSIDfromTestID(Node testNode, Map serviceResultsMap){ + String result = ""; + String fromTestID = testNode.valueOf("fromTestID"); + if (Tools.notEmpty(fromTestID)){ + ServiceResult getPR = serviceResultsMap.get(fromTestID); + if (getPR != null){ + result = getPR.location; + } + } + return result; + } + + + public static org.dom4j.Document getDocument(String xmlFileName) { + Document document = null; + SAXReader reader = new SAXReader(); + try { + document = reader.read(xmlFileName); + } catch (DocumentException e) { + //e.printStackTrace(); + } + return document; + } + + + //================= runXmlReplayFile ====================================================== + + public static List runXmlReplayFile(String xmlReplayBaseDir, + String controlFileName, + String testGroupID, + String oneTestID, + Map serviceResultsMap, + boolean param_autoDeletePOSTS, + Dump dump, + String protoHostPortParam, + AuthsMap defaultAuths) + throws Exception { + //Internally, we maintain two collections of ServiceResult: + // the first is the return value of this method. + // the second is the serviceResultsMap, which is used for keeping track of CSIDs created by POSTs, for later reference by DELETE, etc. + List results = new ArrayList(); + + String controlFile = Tools.glue(xmlReplayBaseDir, "/", controlFileName); + Document document; + document = getDocument(controlFile); //will check full path first, then checks relative to PWD. + if (document==null){ + throw new FileNotFoundException("XmlReplay control file ("+controlFileName+") not found in basedir: "+xmlReplayBaseDir+" Exiting test."); + } + String protoHostPort; + if (Tools.isEmpty(protoHostPortParam)){ + protoHostPort = document.selectSingleNode("/xmlReplay/protoHostPort").getText().trim(); + System.out.println("DEPRECATED: Using protoHostPort ('"+protoHostPort+"') from xmlReplay file ('"+controlFile+"'), not master."); + } else { + protoHostPort = protoHostPortParam; + } + if (Tools.isEmpty(protoHostPort)){ + throw new Exception("XmlReplay control file must have a protoHostPort element"); + } + + String authsMapINFO; + AuthsMap authsMap = readAuths(document); + if (authsMap.map.size()==0){ + authsMap = defaultAuths; + authsMapINFO = "Using defaultAuths from master file: "+defaultAuths; + } else { + authsMapINFO = "Using AuthsMap from control file: "+authsMap; + } + System.out.println("XmlReplay running:" + +"\r\n controlFile: "+ (new File(controlFile).getCanonicalPath()) + +"\r\n protoHostPort: "+protoHostPort + +"\r\n testGroup: "+testGroupID + + (Tools.notEmpty(oneTestID) ? "\r\n oneTestID: "+oneTestID : "") + +"\r\n AuthsMap: "+authsMapINFO + +"\r\n param_autoDeletePOSTS: "+param_autoDeletePOSTS + +"\r\n Dump info: "+dump + +"\r\n"); + + String autoDeletePOSTS = ""; + List testgroupNodes; + if (Tools.notEmpty(testGroupID)){ + testgroupNodes = document.selectNodes("//testGroup[@ID='"+testGroupID+"']"); + } else { + testgroupNodes = document.selectNodes("//testGroup"); + } + + JexlEngine jexl = new JexlEngine(); // Used for expression language expansion from uri field. + XmlReplayEval evalStruct = new XmlReplayEval(); + evalStruct.serviceResultsMap = serviceResultsMap; + evalStruct.jexl = jexl; + + for (Node testgroup : testgroupNodes) { + JexlContext jc = new MapContext(); //Get a new JexlContext for each test group. + evalStruct.jc = jc; + + autoDeletePOSTS = testgroup.valueOf("@autoDeletePOSTS"); + List tests; + if (Tools.notEmpty(oneTestID)){ + tests = testgroup.selectNodes("test[@ID='"+oneTestID+"']"); + } else { + tests = testgroup.selectNodes("test"); + } + String authForTest = ""; + int testElementIndex = -1; + + for (Node testNode : tests) { + testElementIndex++; + String testID = testNode.valueOf("@ID"); + String testIDLabel = Tools.notEmpty(testID) ? (testGroupID+'.'+testID) : (testGroupID+'.'+testElementIndex); + String method = testNode.valueOf("method"); + String uri = testNode.valueOf("uri"); + String fullURL = Tools.glue(protoHostPort, "/", uri); + String initURI = uri; + + String authIDForTest = testNode.valueOf("@auth"); + String currentAuthForTest = authsMap.map.get(authIDForTest); + if (Tools.notEmpty(currentAuthForTest)){ + authForTest = currentAuthForTest; //else just run with current from last loop; + } + if (Tools.isEmpty(authForTest)){ + authForTest = defaultAuths.getDefaultAuth(); + } + + if (uri.indexOf("$")>-1){ + uri = evalStruct.eval(uri, serviceResultsMap, jexl, jc); + } + fullURL = fixupFullURL(fullURL, protoHostPort, uri); + + List expectedCodes = new ArrayList(); + String expectedCodesStr = testNode.valueOf("expectedCodes"); + if (Tools.notEmpty(expectedCodesStr)){ + String[] codesArray = expectedCodesStr.split(","); + for (String code : codesArray){ + expectedCodes.add(new Integer(code)); + } + } + + ServiceResult serviceResult; + boolean isPOST = method.equalsIgnoreCase("POST"); + boolean isPUT = method.equalsIgnoreCase("PUT"); + if ( isPOST || isPUT ) { + PartsStruct parts = PartsStruct.readParts(testNode, testID, xmlReplayBaseDir); + if (Tools.notEmpty(parts.overrideTestID)) { + testID = parts.overrideTestID; + } + if (isPOST){ + String csid = CSIDfromTestID(testNode, serviceResultsMap); + if (Tools.notEmpty(csid)) uri = Tools.glue(uri, "/", csid+"/items/"); + } else if (isPUT) { + uri = fromTestID(uri, testNode, serviceResultsMap); + } + if (parts.bDoingSinglePartPayload){ + serviceResult = XmlReplayTransport.doPOST_PUTFromXML(parts.singlePartPayloadFilename, protoHostPort, uri, "POST", XmlReplayTransport.APPLICATION_XML, evalStruct, authForTest, testIDLabel); + } else { + serviceResult = XmlReplayTransport.doPOST_PUTFromXML_Multipart(parts.filesList, parts.partsList, protoHostPort, uri, "POST", evalStruct, authForTest, testIDLabel); + } + results.add(serviceResult); + if (isPOST){ + serviceResultsMap.put(testID, serviceResult); //PUTs do not return a Location, so don't add PUTs to serviceResultsMap. + } + fullURL = fixupFullURL(fullURL, protoHostPort, uri); + } else if (method.equalsIgnoreCase("DELETE")){ + String fromTestID = testNode.valueOf("fromTestID"); + ServiceResult pr = serviceResultsMap.get(fromTestID); + if (pr!=null){ + serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID); + serviceResult.fromTestID = fromTestID; + results.add(serviceResult); + if (serviceResult.gotExpectedResult()){ + serviceResultsMap.remove(fromTestID); + } + } else { + if (Tools.notEmpty(fromTestID)){ + serviceResult = new ServiceResult(); + serviceResult.responseCode = 0; + serviceResult.error = "ID not found in element fromTestID: "+fromTestID; + System.err.println("****\r\nServiceResult: "+serviceResult.error+". SKIPPING TEST. Full URL: "+fullURL); + } else { + serviceResult = XmlReplayTransport.doDELETE(fullURL, authForTest, testID, fromTestID); + } + serviceResult.fromTestID = fromTestID; + results.add(serviceResult); + } + } else if (method.equalsIgnoreCase("GET")){ + fullURL = fromTestID(fullURL, testNode, serviceResultsMap); + serviceResult = XmlReplayTransport.doGET(fullURL, authForTest, testIDLabel); + results.add(serviceResult); + } else if (method.equalsIgnoreCase("LIST")){ + fullURL = fixupFullURL(fullURL, protoHostPort, uri); + String listQueryParams = ""; //TODO: empty for now, later may pick up from XML control file. + serviceResult = XmlReplayTransport.doLIST(fullURL, listQueryParams, authForTest, testIDLabel); + results.add(serviceResult); + } else { + throw new Exception("HTTP method not supported by XmlReplay: "+method); + } + + serviceResult.testID = testID; + serviceResult.fullURL = fullURL; + serviceResult.auth = authForTest; + serviceResult.method = method; + if (expectedCodes.size()>0){ + serviceResult.expectedCodes = expectedCodes; + } + if (Tools.isEmpty(serviceResult.testID)) serviceResult.testID = testIDLabel; + if (Tools.isEmpty(serviceResult.testGroupID)) serviceResult.testGroupID = testGroupID; + + String serviceResultRow = serviceResult.dump(dump.dumpServiceResult); + String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": ""; + System.out.println(leader+serviceResultRow+"\r\n"); + if (dump.payloads) System.out.println(serviceResult.result); + } + if (Tools.isTrue(autoDeletePOSTS)&¶m_autoDeletePOSTS){ + autoDelete(serviceResultsMap, "default"); + } + } + return results; + } + + + //======================== MAIN =================================================================== + + private static Options createOptions() { + Options options = new Options(); + options.addOption("xmlReplayBaseDir", true, "default/basedir"); + return options; + } + + public static String usage(){ + String result = "org.collectionspace.services.IntegrationTests.xmlreplay.XmlReplay {args}\r\n" + +" -xmlReplayBaseDir \r\n" + +" You may also override these with system args, e.g.: \r\n" + +" -DxmlReplayBaseDir=/path/to/dir \r\n" + +" These may also be passed in via the POM.\r\n" + +" You can also set these system args, e.g.: \r\n" + +" -DtestGroupID= \r\n" + +" -DtestID=" + +" -DautoDeletePOSTS= \r\n" + +" (note: -DautoDeletePOSTS won't force deletion if set to false in control file."; + return result; + } + + private static String opt(CommandLine line, String option){ + String result; + String fromProps = System.getProperty(option); + if (Tools.notEmpty(fromProps)){ + return fromProps; + } + result = line.getOptionValue(option); + if (result == null){ + result = ""; + } + return result; + } + + public static void main(String[]args) throws Exception { + Options options = createOptions(); + //System.out.println("System CLASSPATH: "+prop.getProperty("java.class.path", null)); + CommandLineParser parser = new GnuParser(); + try { + // parse the command line arguments + CommandLine line = parser.parse(options, args); + + String xmlReplayBaseDir = opt(line, "xmlReplayBaseDir"); + String testGroupID = opt(line, "testGroupID"); + String testID = opt(line, "testID"); + String autoDeletePOSTS = opt(line, "autoDeletePOSTS"); + String dumpResults = opt(line, "dumpResults"); + String controlFilename = opt(line, "controlFilename"); + String xmlReplayMaster = opt(line, "xmlReplayMaster"); + + xmlReplayBaseDir = Tools.fixFilename(xmlReplayBaseDir); + controlFilename = Tools.fixFilename(controlFilename); + + boolean bAutoDeletePOSTS = true; + if (Tools.notEmpty(autoDeletePOSTS)) { + bAutoDeletePOSTS = Tools.isTrue(autoDeletePOSTS); + } + boolean bDumpResults = false; + if (Tools.notEmpty(dumpResults)) { + bDumpResults = Tools.isTrue(autoDeletePOSTS); + } + if (Tools.isEmpty(xmlReplayBaseDir)){ + System.err.println("ERROR: xmlReplayBaseDir was not specified."); + return; + } + File f = new File(Tools.glue(xmlReplayBaseDir, "/", controlFilename)); + if (Tools.isEmpty(xmlReplayMaster) && !f.exists()){ + System.err.println("Control file not found: "+f.getCanonicalPath()); + return; + } + File fMaster = new File(Tools.glue(xmlReplayBaseDir, "/", xmlReplayMaster)); + if (Tools.notEmpty(xmlReplayMaster) && !fMaster.exists()){ + System.err.println("Master file not found: "+fMaster.getCanonicalPath()); + return; + } + + String xmlReplayBaseDirResolved = (new File(xmlReplayBaseDir)).getCanonicalPath(); + System.out.println("XmlReplay ::" + + "\r\n xmlReplayBaseDir: "+xmlReplayBaseDir + + "\r\n xmlReplayBaseDir(resolved): "+xmlReplayBaseDirResolved + + "\r\n controlFilename: "+controlFilename + + "\r\n xmlReplayMaster: "+xmlReplayMaster + + "\r\n testGroupID: "+testGroupID + + "\r\n testID: "+testID + + "\r\n autoDeletePOSTS: "+bAutoDeletePOSTS + + (Tools.notEmpty(xmlReplayMaster) + ? ("\r\n will use master file: "+fMaster.getCanonicalPath()) + : ("\r\n will use control file: "+f.getCanonicalPath()) ) + ); + + if (Tools.notEmpty(xmlReplayMaster)){ + if (Tools.notEmpty(controlFilename)){ + System.out.println("WARN: controlFilename: "+controlFilename+" will not be used because master was specified. Running master: "+xmlReplayMaster); + } + XmlReplay replay = new XmlReplay(xmlReplayBaseDirResolved); + replay.readOptionsFromMasterConfigFile(xmlReplayMaster); + replay.setAutoDeletePOSTS(bAutoDeletePOSTS); + Dump dumpFromMaster = replay.getDump(); + dumpFromMaster.payloads = Tools.isTrue(dumpResults); + replay.setDump(dumpFromMaster); + replay.runMaster(xmlReplayMaster, false); //false, because we already just read the options, and override a few. + } else { + Dump dump = getDumpConfig(); + dump.payloads = Tools.isTrue(dumpResults); + runXmlReplayFile(xmlReplayBaseDirResolved, controlFilename, testGroupID, testID, createResultsMap(), bAutoDeletePOSTS, dump, "", null); + } + } catch (ParseException exp) { + // oops, something went wrong + System.err.println("Cmd-line parsing failed. Reason: " + exp.getMessage()); + System.err.println(usage()); + } catch (Exception e) { + System.out.println("Error : " + e.getMessage()); + e.printStackTrace(); + } + } + +} diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayEval.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayEval.java new file mode 100755 index 000000000..5723f5b27 --- /dev/null +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayEval.java @@ -0,0 +1,114 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Copyright (c) 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.collectionspace.services.IntegrationTests.xmlreplay; + +import org.apache.commons.jexl2.Expression; +import org.apache.commons.jexl2.JexlContext; +import org.apache.commons.jexl2.JexlEngine; + +import java.util.Map; + +/** + * User: laramie + * $LastChangedRevision: $ + * $LastChangedDate: $ + */ +public class XmlReplayEval { + public Map serviceResultsMap; + public JexlEngine jexl; + public JexlContext jc; + + /** + * You may pass in a Jexl 2 expression, e.g. ${foo.bar} and it will be eval'd for you. + * We are looking at some URI like so: ${newOrgAuthority.CSID} + * The idea here is that the XML control file may bind to this namespace, and + * this module may find those values and any future extensions, specifically + * when someone says "I want to bind to ${CSID} and ${SUBRESOURCE.CSID} + * The code here is easy to extend, but the test cases build up, so you don't + * want to break all the config files by not being backward compatible. Binding + * to context variables like this makes it easy. + * EXAMPLE USAGE:
+ * String uri = "/cspace-services/orgauthorities/${OrgAuth1.CSID}/items/${Org1.CSID}";
+ * uri = eval(uri, serviceResultsMap, jexl, jc);
+ * RESULT: "/cspace-services/orgauthorities/43a2739c-4f40-49c8-a6d5/items/" + */ + public static String eval(String inputJexlExpression, Map serviceResultsMap, JexlEngine jexl, JexlContext jc) { + //System.out.println("\r\n---- REPLACE.init-uri: "+inputJexlExpression); + String result; + try { + for (ServiceResult postResult : serviceResultsMap.values()) { + jc.set(postResult.testID, postResult); + //System.out.println("eval :: "+postResult.testID+"==>"+postResult); + } + result = parse(inputJexlExpression, jexl, jc); + } catch (Throwable t) { + System.err.println("ERROR: " + t); + result = "ERROR"; + } + //System.out.println("---- REPLACE.uri: "+result+"\r\n"); + return result; + } + + private static String parse(String in, JexlEngine jexl, JexlContext jc) { + StringBuffer result = new StringBuffer(); + String s = in; + String var = ""; + int start, end, len; + len = in.length(); + start = 0; + int cursor = 0; + String front = ""; + while (start < len) { + end = in.indexOf("}", start); + start = in.indexOf("${", start); + if (start < 0) { + String tail = in.substring(cursor); + result.append(tail); + break; + } + if (end < 0) { + return "ERROR: unbalanced ${} braces"; + } + front = in.substring(cursor, start); + result.append(front); + cursor = end + 1; //bump past close brace + var = in.substring(start + 2, end); //+2 bump past open brace ${ and then "end" is indexed just before the close brace } + //s = s.substring(end+1); //bump past close brace + start = cursor; + + Expression expr = jexl.createExpression(var); + Object resultObj = expr.evaluate(jc); + String resultStr; + if (null == resultObj){ + resultStr = "ERROR"; + System.out.println("Jexl context: "+jc.toString()); + } else { + resultStr = resultObj.toString(); + + } + result.append(resultStr); + } + return result.toString(); + } + +} diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTest.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTest.java new file mode 100755 index 000000000..f05ab135b --- /dev/null +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTest.java @@ -0,0 +1,179 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Copyright (c) 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.collectionspace.services.IntegrationTests.xmlreplay; + +import org.testng.Assert; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** Subclass this test to programmatically control XmlReplay from a surefire test. See example in IntegrationTests :: XmlReplaySelfTest + * User: laramie + * $LastChangedRevision: $ + * $LastChangedDate: $ + */ +public class XmlReplayTest { + + public static final String XMLREPLAY_REL_DIR_TO_MODULE = "/src/test/resources/test-data/xmlreplay"; + + /** To use this method, you should have a test repository of xml files in the path + * defined by XMLREPLAY_REL_DIR_TO_MODULE, relative to your pom.xml file, but normally + * you would use the central repository of tests, which live in services/IntegrationTests, + * and which you can use by calling createXmlReplay() which calls createXmlReplayUsingIntegrationTestsModule() for you. + */ + public static XmlReplay createXmlReplayForModule() throws Exception { + String pwd = (new File(".")).getCanonicalPath(); + System.out.println("createXmlReplayForModule.pwd: "+pwd); + XmlReplay replay = new XmlReplay(pwd+XMLREPLAY_REL_DIR_TO_MODULE); + System.out.println("XmlReplay: "+replay); + return replay; + } + + /** Use this method if your test xml files are stored in the central repository, + * which is "services/IntegrationTests" + XMLREPLAY_REL_DIR_TO_MODULE + */ + public static XmlReplay createXmlReplay() throws Exception { + return createXmlReplayUsingIntegrationTestsModule("../.."); + } + + /** + * @param relToServicesRoot is a Unix-like path from the calling module to the services root, + * so if if you are in services/dimension/client/ + * then relToServicesRoot is "../.." which is how most of the client tests are set up, or if you + * are setting up your test repository relative to the main service, e.g. you are in + * services/dimension/, then relToServicesRoot is ".." + */ + public static XmlReplay createXmlReplayUsingIntegrationTestsModule(String relToServicesRoot) throws Exception { + String thisDir = Tools.glue(relToServicesRoot, "/", "IntegrationTests"); + String pwd = (new File(thisDir)).getCanonicalPath(); + System.out.println("createXmlReplayUsingIntegrationTestsModule.pwd: "+pwd); + XmlReplay replay = new XmlReplay(pwd+XMLREPLAY_REL_DIR_TO_MODULE); + System.out.println("XmlReplay: "+replay); + return replay; + } + + public static void logTest(ServiceResult sresult, String testname){ + ResultSummary summary = resultSummary(sresult); + org.testng.Reporter.log(summary.table); + Assert.assertEquals(summary.oks, summary.total, "Expected all "+summary.total+ " XmlReplay tests to pass. See Output from test '"+testname+"'."); + } + + public static void logTest(List list, String testname){ + ResultSummary summary = resultSummary(list); + org.testng.Reporter.log(summary.table); + Assert.assertEquals(summary.oks, summary.total, "Expected all "+summary.total+ " XmlReplay tests to pass. See Output from test '"+testname+"'."); + } + + public static void logTestForGroup(List> list, String testname){ + ResultSummary summary = resultSummaryForGroup(list); + org.testng.Reporter.log(summary.table); + Assert.assertEquals(summary.oks, summary.total, "Expected all "+summary.total+ " XmlReplay tests to pass. See Output from test '"+testname+"'."); + } + + + //============== HELPERS AND FORMATTING ===================================================== + + private static final String TBLSTART = ""; + private static final String ROWSTART = ""; + private static final String ROWENDRED = ""; + private static final String TBLEND = "
"; + private static final String ROWSTARTRED = "
"; + private static final String SEP = ""; + private static final String ROWEND = "
"; + + public static class ResultSummary { + public long oks = 0; + public long total = 0; + public String table = ""; + public List groups = new ArrayList(); + } + + public static ResultSummary resultSummaryForGroup(List> list){ + ResultSummary summary = new ResultSummary(); + summary.oks = 0; + summary.total = 0; + StringBuffer buff = new StringBuffer(); + buff.append(TBLSTART); + for (List serviceResults : list){ + String groupID = ""; + if (serviceResults.size()>0){ + groupID = serviceResults.get(0).testGroupID; + summary.groups.add(groupID); + } + buff.append(ROWSTART+"XmlReplay testGroup "+groupID+ROWEND); + for (ServiceResult serviceResult : serviceResults){ + summary.total++; + if (serviceResult.gotExpectedResult()){ + summary.oks++; + buff.append(ROWSTART+serviceResult.minimal()+ROWEND); + } else { + buff.append(ROWSTARTRED+serviceResult.minimal()+ROWENDRED); + } + } + + } + buff.append(TBLEND); + summary.table = buff.toString(); + return summary; + } + + public static ResultSummary resultSummary(List serviceResults){ + ResultSummary summary = new ResultSummary(); + summary.oks = 0; + summary.total = 0; + StringBuffer buff = new StringBuffer(); + buff.append(TBLSTART); + for (ServiceResult serviceResult : serviceResults){ + summary.total++; + if (serviceResult.gotExpectedResult()){ + summary.oks++; + buff.append(ROWSTART+serviceResult.minimal()+ROWEND); + } else { + buff.append(ROWSTARTRED+serviceResult.minimal()+ROWENDRED); + } + } + buff.append(TBLEND); + summary.table = buff.toString(); + return summary; + } + + public static ResultSummary resultSummary(ServiceResult serviceResult){ + ResultSummary summary = new ResultSummary(); + summary.oks = 0; + summary.total = 1; + StringBuffer buff = new StringBuffer(); + buff.append(TBLSTART); + if (serviceResult.gotExpectedResult()){ + summary.oks = 1; + buff.append(ROWSTART+serviceResult.minimal()+ROWEND); + } else { + buff.append(ROWSTARTRED+serviceResult.minimal()+ROWENDRED); + } + buff.append(TBLEND); + summary.table = buff.toString(); + return summary; + } + +} diff --git a/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTransport.java b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTransport.java new file mode 100755 index 000000000..b86578775 --- /dev/null +++ b/services/IntegrationTests/src/main/java/org/collectionspace/services/IntegrationTests/xmlreplay/XmlReplayTransport.java @@ -0,0 +1,237 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + * + * http://www.collectionspace.org + * http://wiki.collectionspace.org + * + * Copyright (c) 2009 Regents of the University of California + * + * Licensed under the Educational Community License (ECL), Version 2.0. + * You may not use this file except in compliance with this License. + * + * You may obtain a copy of the ECL 2.0 License at + * https://source.collectionspace.org/collection-space/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.collectionspace.services.IntegrationTests.xmlreplay; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.io.FileUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.List; +import java.util.Map; + +import org.collectionspace.services.IntegrationTests.xmlreplay.ServiceResult; + +/** + * @author Laramie Crocker + */ +public class XmlReplayTransport { + + private static String BOUNDARY = "34d97c83-0d61-4958-80ab-6bf8d362290f"; + private static String DD = "--"; + private static String CRLF = "\r\n"; + + public static ServiceResult doGET(String urlString, String authForTest, String fromTestID) throws Exception { + HttpClient client = new HttpClient(); + GetMethod getMethod = new GetMethod(urlString); + getMethod.addRequestHeader("Accept", "multipart/mixed"); + getMethod.addRequestHeader("Accept", "application/xml"); + getMethod.setRequestHeader("Authorization", "Basic " + authForTest); //"dGVzdDp0ZXN0"); + getMethod.setRequestHeader("X-XmlReplay-fromTestID", fromTestID); + ServiceResult pr = new ServiceResult(); + + int statusCode1 = client.executeMethod(getMethod); + pr.responseCode = statusCode1; + pr.method = "GET"; + try { + pr.result = getMethod.getResponseBodyAsString(); + pr.responseMessage = getMethod.getStatusText(); + } catch (Throwable t){ + //System.err.println("ERROR getting content from response: "+t); + pr.error = t.toString(); + } + + + getMethod.releaseConnection(); + return pr; + } + + public static ServiceResult doDELETE(String urlString, String authForTest, String testID, String fromTestID) throws Exception { + ServiceResult pr = new ServiceResult(); + pr.method = "DELETE"; + pr.fullURL = urlString; + if (Tools.isEmpty(urlString)){ + pr.error = "url was empty. Check the result for fromTestID: "+fromTestID+". currentTest: "+testID; + return pr; + } + HttpClient client = new HttpClient(); + DeleteMethod deleteMethod = new DeleteMethod(urlString); + deleteMethod.setRequestHeader("Accept", "multipart/mixed"); + deleteMethod.addRequestHeader("Accept", "application/xml"); + deleteMethod.setRequestHeader("Authorization", "Basic " + authForTest); + deleteMethod.setRequestHeader("X-XmlReplay-fromTestID", fromTestID); + int statusCode1 = 0; + String res = ""; + try { + statusCode1 = client.executeMethod(deleteMethod); + pr.responseCode = statusCode1; + //System.out.println("statusCode: "+statusCode1+" statusLine ==>" + deleteMethod.getStatusLine()); + pr.responseMessage = deleteMethod.getStatusText(); + res = deleteMethod.getResponseBodyAsString(); + deleteMethod.releaseConnection(); + } catch (Throwable t){ + pr.error = t.toString(); + } + pr.result = res; + pr.responseCode = statusCode1; + return pr; + } + + public static ServiceResult doLIST(String urlString, String listQueryParams, String authForTest, String fromTestID) throws Exception { + //String u = Tools.glue(urlString, "/", "items/"); + if (Tools.notEmpty(listQueryParams)){ + urlString = Tools.glue(urlString, "?", listQueryParams); + } + return doGET(urlString, authForTest, fromTestID); + } + + public static final String MULTIPART_MIXED = "multipart/mixed"; + public static final String APPLICATION_XML = "application/xml"; + + /** Use this overload for multipart messages. */ + public static ServiceResult doPOST_PUTFromXML_Multipart(List filesList, + List partsList, + String protoHostPort, + String uri, + String method, + XmlReplayEval evalStruct, + String authForTest, + String fromTestID) + throws Exception { + if ( filesList==null||filesList.size()==0 + ||partsList==null||partsList.size()==0 + ||(partsList.size() != filesList.size())){ + throw new Exception("filesList and partsList must not be empty and must have the same number of items each."); + } + String content = DD + BOUNDARY; + + for (int i=0; i> headers = conn.getHeaderFields(); + List locations = headers.get("Location"); + if (locations != null){ + String locationZero = locations.get(0); + if (locationZero != null){ + String[] segments = locationZero.split("/"); + location = segments[segments.length - 1]; + deleteURL = Tools.glue(urlString, "/", location); + } + } + result.location = location; + result.deleteURL = deleteURL; + result.CSID = location; + result.method = method; + return result; + } + +} -- 2.47.3