public class FileTestDataProvider implements TestDataProvider {
	
	private File provFile = null;

	private Logger logger = Logger.getLogger(FileTestDataProvider.class.getSimpleName());
	
	public List<Map<String, String>> provide(GatfTestDataProvider provider, AcceptanceTestContext context) {
		
		List<Map<String, String>> result = new ArrayList<Map<String,String>>();
		
		if(provider.getArgs()==null || provider.getArgs().length==0) {
			throw new AssertionError("No arguments passed to the FileProvider");
		}
		
		if(provider.getArgs().length<2) {
			throw new AssertionError("FileProvider needs 2 arguments, namely filePath and fileType");
		}
		
		Assert.assertNotNull("filePath cannot be empty", provider.getArgs()[0]);
		Assert.assertNotNull("fileType cannot be empty", provider.getArgs()[1]);
		Assert.assertNotNull("variableNames cannot be empty", provider.getProviderProperties());
		
		String filePath = provider.getArgs()[0].trim();
		String fileType = provider.getArgs()[1].trim();
		String variableNames = provider.getProviderProperties().trim();
		
		Assert.assertFalse("filePath cannot be empty", filePath.isEmpty());
		Assert.assertFalse("fileType cannot be empty", fileType.isEmpty());
		
		List<String> variableNamesArr = new ArrayList<String>();
		for (String varName : variableNames.split(",")) {
			if(!varName.trim().isEmpty()) {
				variableNamesArr.add(varName);
			}
		}
		Assert.assertTrue("need to define at-least a single variable name", 
				!variableNames.isEmpty() && variableNames.split(",").length>0 && variableNamesArr.size()>0);
		
		StringBuilder build = new StringBuilder();
		build.append("Provider configuration [\n");
		build.append(String.format("filePath is %s\n", filePath));
		build.append(String.format("fileType is %s\n", fileType));
		build.append(String.format("variableNames is %s]", variableNames));
		logger.info(build.toString());
		
		try {
			provFile = context.getResourceFile(filePath);
			Assert.assertTrue(String.format("Unable to find %s", filePath), provFile!=null && provFile.exists());
		} catch (Exception e) {
			throw new AssertionError(String.format("Unable to find %s", filePath));
		}
		
		if(fileType.equalsIgnoreCase("csv") || fileType.equalsIgnoreCase("xls") || fileType.equalsIgnoreCase("xlsx")) {
			handleCsvFamilyFile(provider.getArgs(), fileType, variableNamesArr, result);
		} else if(fileType.equalsIgnoreCase("xml")) {
			handleXMLFile(variableNamesArr, result);
		} else if(fileType.equalsIgnoreCase("json")) {
			handleJSONFile(variableNamesArr, result);
		} else {
			throw new AssertionError(String.format("Invalid fileType %s, only csv, xml and json allowed", fileType));
		}
		
		return result;
	}
	
	public String getHash() {
		if(provFile!=null && provFile.exists()) {
			try {
				return DigestUtils.sha512Hex(new FileInputStream(provFile));
			} catch (Exception e) {
			}
		}
		return null;
	}
	
	private void handleCsvFamilyFile(String[] args, String fileType, List<String> variableNamesArr, 
			List<Map<String, String>> result)
	{
		List<String[]> list = new ArrayList<String[]>();
		try {
			if(fileType.equalsIgnoreCase("csv")) {
				char splitStr = ',';
				if(args.length>2 && args[2].trim().matches("separator=(.*)")) {
					splitStr = args[2].trim().charAt(10);
				}
				list = readCsvFamilyFile(fileType, provFile, splitStr, -1);
			} else if(fileType.equalsIgnoreCase("xls") || fileType.equalsIgnoreCase("xlsx")) {
				int sheet = 0;
				if(args.length>2 && args[2].trim().matches("sheet=([0-9]+)")) {
					sheet = Integer.parseInt(args[2].trim().substring(6));
				}
				list = readCsvFamilyFile(fileType, provFile, ',', sheet);
			}
			
			int counter = 1;
			for (String[] parts : list) {
				if(parts.length!=variableNamesArr.size()) {
					throw new AssertionError("The number of columns in the file record do not match the " +
							"variable Names provided at position " + counter++);
				}
				Map<String, String> row = new HashMap<String, String>();
				for (int i = 0; i < variableNamesArr.size(); i++) {
					row.put(variableNamesArr.get(i), parts[i]);
				}
				result.add(row);
			}
		} catch(AssertionError e) {
			throw e;
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}

	/*private void handleCSVFile(File file, List<String> variableNamesArr, List<Map<String, String>> result,
			 char splitStr) {
		CSVReader reader = null;
		try {
			reader = new CSVReader(new FileReader(file), splitStr);
			List<String[]> list = reader.readAll();
			for (String[] parts : list) {
				if(parts.length!=variableNamesArr.size()) {
					reader.close();
					throw new AssertionError("The number of columns in the csv line do not match the variable Names provided");
				}
				Map<String, String> row = new HashMap<String, String>();
				for (int i = 0; i < variableNamesArr.size(); i++) {
					row.put(variableNamesArr.get(i), parts[i]);
				}
				result.add(row);
			}
			reader.close();
		} catch (Exception e) {
			throw new AssertionError(e);
		} finally {
			if(reader!=null)
			{
				try {
					reader.close();
				} catch (IOException e) {
				}
			}
		}
	}*/
	
	private void handleXMLFile(List<String> variableNamesArr, List<Map<String, String>> result) {
		try
		{
			String content = FileUtils.readFileToString(provFile, "UTF-8");
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document xmlDocument = db.parse(new ByteArrayInputStream(content.getBytes()));

			if(xmlDocument!=null)
			{
				List<NodeList> nodes = new ArrayList<NodeList>(variableNamesArr.size());
				for (int i = 0; i < variableNamesArr.size(); i++) {
					
					String expression = variableNamesArr.get(i).replaceAll("\\.", "\\/");
					if(expression.charAt(0)!='/')
						expression = "/" + expression;
					XPath xPath =  XPathFactory.newInstance().newXPath();
					NodeList xmlNodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
					Assert.assertTrue("Expected Node " + variableNamesArr.get(i) + " is null", 
							xmlNodeList!=null && xmlNodeList.getLength()>0);
					
					nodes.add(xmlNodeList);
					String xmlValue = xmlNodeList.item(0).getNodeValue();
					Assert.assertNotNull("Expected Node " + variableNamesArr.get(i) + " is null", xmlValue);
					
				}
				
				int nodeLength = nodes.get(0).getLength();
				for (NodeList nodeList : nodes) {
					if(nodeList.getLength()!=nodeLength) {
						throw new AssertionError("Variable set sizes don't match");
					}
				}
				
				for (int j = 0; j < nodeLength; j++) {
					Map<String, String> row = new HashMap<String, String>();
					for (int i = 0; i < variableNamesArr.size(); i++) {
						row.put(variableNamesArr.get(i), nodes.get(i).item(j).getNodeValue());
					}
					result.add(row);
				}
			}
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
	
	private void handleJSONFile(List<String> variableNamesArr, List<Map<String, String>> result) {
		try
		{
			String content = FileUtils.readFileToString(provFile, "UTF-8");
			if(content!=null && !content.isEmpty()) {
				List<List<String>> varValues = new ArrayList<List<String>>();
				for (int i = 0; i < variableNamesArr.size(); i++) {
					List<String> values = JsonPath.read(content, variableNamesArr.get(i));
					Assert.assertNotNull(String.format("No values found for %s", variableNamesArr.get(i)), values);
					varValues.add(values);
				}
				
				int nodeLength = varValues.get(0).size();
				for (List<String> nodeList : varValues) {
					if(nodeList.size()!=nodeLength) {
						throw new AssertionError("Variable set sizes don't match");
					}
				}
				
				for (int j = 0; j < nodeLength; j++) {
					Map<String, String> row = new HashMap<String, String>();
					for (int i = 0; i < variableNamesArr.size(); i++) {
						row.put(variableNamesArr.get(i), varValues.get(i).get(j));
					}
					result.add(row);
				}
			}
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
	
	public static List<String[]> readCsvFamilyFile(String fileType, File file, char splitStr, int sheet)
	{
		Workbook hssfWorkbook = null;
		CSVReader reader = null;
		List<String[]> list = new ArrayList<String[]>();
		try {
			if(fileType.equalsIgnoreCase("csv")) {
				reader = new CSVReader(new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")), splitStr);
				list = reader.readAll();
			} else if(fileType.equalsIgnoreCase("xls") || fileType.equalsIgnoreCase("xlsx")) {
				if(fileType.equalsIgnoreCase("xls")) {
					hssfWorkbook = new HSSFWorkbook(new FileInputStream(file));
				} else {
					hssfWorkbook = new XSSFWorkbook(new FileInputStream(file));
				}
				if(sheet >= hssfWorkbook.getNumberOfSheets()) {
					throw new AssertionError("Invalid sheet number specified for file " + file.getName());
				}
				Sheet hssfSheet = hssfWorkbook.getSheetAt(sheet);
				Iterator<Row> rows = hssfSheet.rowIterator();
				while (rows.hasNext()) {
					Row hssfRow = rows.next();
					String[] parts = new String[hssfRow.getLastCellNum()];
					for(int cn=0; cn<hssfRow.getLastCellNum(); cn++) {
						Cell hssfCell = hssfRow.getCell(cn, MissingCellPolicy.CREATE_NULL_AS_BLANK);
						DataFormatter fmt = new DataFormatter();
						parts[cn] = fmt.formatCellValue(hssfCell);
					}
					list.add(parts);
				}
			}
		} catch (Exception e) {
			throw new AssertionError(e);
		} finally {
			if(hssfWorkbook!=null) {
				try {
					hssfWorkbook.close();
				} catch (IOException e) {
				}
			}
			if(reader!=null)
			{
				try {
					reader.close();
				} catch (IOException e) {
				}
			}
		}
		return list;
	}
}
 @XStreamAlias("gatf-testdata-config")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataConfig implements Serializable {

	private static final long serialVersionUID = 1L;
	
	@XStreamConverter(value=MapKeyValueCustomXstreamConverter.class)
	private Map<String, String> globalVariables;
	
	private List<String> compareEnvBaseUrls;
	
	private List<GatfTestDataSource> dataSourceList;

	private List<GatfTestDataProvider> providerTestDataList;
	
	private List<GatfTestDataSourceHook> dataSourceHooks;
	
	private List<GatfTestDataSource> dataSourceListForProfiling;

	public Map<String, String> getGlobalVariables() {
		return globalVariables;
	}

	public void setGlobalVariables(Map<String, String> globalVariables) {
		this.globalVariables = globalVariables;
	}

	public List<GatfTestDataProvider> getProviderTestDataList() {
		return providerTestDataList;
	}

	public void setProviderTestDataList(
			List<GatfTestDataProvider> providerTestDataList) {
		this.providerTestDataList = providerTestDataList;
	}

	public List<String> getCompareEnvBaseUrls() {
		return compareEnvBaseUrls;
	}

	public void setCompareEnvBaseUrls(List<String> compareEnvBaseUrls) {
		this.compareEnvBaseUrls = compareEnvBaseUrls;
	}

	public List<GatfTestDataSource> getDataSourceList() {
		return dataSourceList;
	}

	public void setDataSourceList(List<GatfTestDataSource> dataSourceList) {
		this.dataSourceList = dataSourceList;
	}

	public List<GatfTestDataSourceHook> getDataSourceHooks() {
		return dataSourceHooks;
	}

	public void setDataSourceHooks(List<GatfTestDataSourceHook> dataSourceHooks) {
		this.dataSourceHooks = dataSourceHooks;
	}

	public List<GatfTestDataSource> getDataSourceListForProfiling() {
		return dataSourceListForProfiling;
	}

	public void setDataSourceListForProfiling(List<GatfTestDataSource> dataSourceListForProfiling) {
		this.dataSourceListForProfiling = dataSourceListForProfiling;
	}
}
 @XStreamAlias("gatf-testdata-provider")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataProvider implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String dataSourceName;
	
	private String providerName;
	
	private String providerClass;
	
	private String sourceProperties;
	
	private String providerProperties;
	
	private String queryStr;
	
	private Boolean enabled;
	
	protected String[] args;
	
	private Boolean live;

	public String getProviderName() {
		return providerName;
	}

	public void setProviderName(String providerName) {
		this.providerName = providerName;
	}

	public Boolean isEnabled() {
		return enabled!=null && enabled;
	}

	public void setEnabled(Boolean enabled) {
		this.enabled = enabled;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public String getProviderProperties() {
		return providerProperties;
	}

	public void setProviderProperties(String providerProperties) {
		this.providerProperties = providerProperties;
	}

	public String getSourceProperties() {
		return sourceProperties;
	}

	public void setSourceProperties(String sourceProperties) {
		this.sourceProperties = sourceProperties;
	}

	public String getQueryStr() {
		return queryStr;
	}

	public void setQueryStr(String queryStr) {
		this.queryStr = queryStr;
	}

	public String[] getArgs() {
		return args;
	}

	public void setArgs(String[] args) {
		this.args = args;
	}

	public String getProviderClass() {
		return providerClass;
	}

	public void setProviderClass(String providerClass) {
		this.providerClass = providerClass;
	}

	public Boolean isLive() {
		return live!=null && live;
	}

	public void setLive(Boolean live) {
		this.live = live;
	}

	public GatfTestDataProvider(){}

	public GatfTestDataProvider(GatfTestDataProvider other) {
		super();
		this.dataSourceName = other.dataSourceName;
		this.providerName = other.providerName;
		this.providerClass = other.providerClass;
		this.sourceProperties = other.sourceProperties;
		this.providerProperties = other.providerProperties;
		this.queryStr = other.queryStr;
		this.enabled = other.enabled;
		if(other.args!=null) {
		    this.args = (String[])Arrays.asList(other.args).toArray(new String[other.args.length]);
		}
		this.live = other.live;
	}
}
 @XStreamAlias("gatf-testdata-source")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataSource implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String dataSourceName;
	
	private String dataSourceClass;
	
	private int poolSize = 1;
	
	private long newResourceCheckoutTimeMs = 1000;
	
	private String[] args;

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public String[] getArgs() {
		return args;
	}

	public void setArgs(String[] args) {
		this.args = args;
	}

	public String getDataSourceClass() {
		return dataSourceClass;
	}

	public void setDataSourceClass(String dataSourceClass) {
		this.dataSourceClass = dataSourceClass;
	}

	public int getPoolSize() {
		return poolSize;
	}

	public void setPoolSize(int poolSize) {
		this.poolSize = poolSize;
	}
	
	public long getNewResourceCheckoutTimeMs() {
		return newResourceCheckoutTimeMs;
	}

	public void setNewResourceCheckoutTimeMs(long newResourceCheckoutTimeMs) {
		this.newResourceCheckoutTimeMs = newResourceCheckoutTimeMs;
	}
}
 @XStreamAlias("gatf-testdata-source-hook")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class GatfTestDataSourceHook implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private String hookName;
	
	private String hookClass;
	
	private String dataSourceName;
	
	private String[] queryStrs;
	
	private boolean executeOnStart;
	
	private boolean executeOnShutdown;
	
	public String getHookName() {
		return hookName;
	}

	public void setHookName(String hookName) {
		this.hookName = hookName;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public String[] getQueryStrs() {
		return queryStrs;
	}

	public void setQueryStrs(String[] queryStrs) {
		this.queryStrs = queryStrs;
	}

	public String getHookClass() {
		return hookClass;
	}

	public void setHookClass(String hookClass) {
		this.hookClass = hookClass;
	}

	public boolean isExecuteOnStart() {
		return executeOnStart;
	}

	public void setExecuteOnStart(boolean executeOnStart) {
		this.executeOnStart = executeOnStart;
	}

	public boolean isExecuteOnShutdown() {
		return executeOnShutdown;
	}

	public void setExecuteOnShutdown(boolean executeOnShutdown) {
		this.executeOnShutdown = executeOnShutdown;
	}
}
 public class InlineValueTestDataProvider implements TestDataProvider {

	public List<Map<String, String>> provide(GatfTestDataProvider provider, AcceptanceTestContext context) {
		
		List<Map<String, String>> result = new ArrayList<Map<String,String>>();
		
		if(provider.getArgs()==null || provider.getArgs().length==0) {
			throw new AssertionError("Insufficient arguments passed, need variable values");
		}
		
		Assert.assertNotNull("variableNames cannot be empty", provider.getProviderProperties());
		String variableNames = provider.getProviderProperties().trim();
		
		List<String> variableNamesArr = new ArrayList<String>();
		for (String varName : variableNames.split(",")) {
			if(!varName.trim().isEmpty()) {
				variableNamesArr.add(varName);
			}
		}
		Assert.assertTrue("need to define at-least a single variable name", 
				!variableNames.isEmpty() && variableNames.split(",").length>0 && variableNamesArr.size()>0);
		
		boolean quoted = false;
		int start = 0;
		String splitStr = ",";
		if(provider.getArgs()[0].trim().equalsIgnoreCase("quoted=true")) {
			quoted = true;
			start = 1;
			splitStr = "\"[\t ]*,[\t ]*\"";
		}
		
		for (int j = start; j < provider.getArgs().length; j++) {
			Assert.assertNotNull("variable value cannot be empty", provider.getArgs()[j]);
			String[] values = null;
			if(quoted) {
				values = provider.getArgs()[j].trim().substring(1, provider.getArgs()[j].length()-1).split(splitStr);
			} else {
				values = provider.getArgs()[j].trim().split(splitStr);
			}
			
			Assert.assertNotNull(String.format("need %d values for variables defined", variableNamesArr.size()), 
					provider.getArgs()[j]);
			
			Map<String, String> row = new HashMap<String, String>();
			for (int i = 0; i < variableNamesArr.size(); i++) {
				row.put(variableNamesArr.get(i), values[i]);
			}
			result.add(row);
		}
		return result;
	}
}
 public class MongoDBTestDataSource extends TestDataSource {

	private Logger logger = Logger.getLogger(MongoDBTestDataSource.class.getSimpleName());
	
	List<ServerAddress> addresses = new ArrayList<ServerAddress>();
	
	public void init() {
		if(args==null || args.length==0) {
			throw new AssertionError("No arguments passed to the MongoDBTestDataProvider");
		}
		
		if(args.length<3) {
			throw new AssertionError("The arguments, namely mongodb-host, mongodb-port, mongodb-database are " +
					" mandatory for MongoDBTestDataProvider");
		}
		
		Assert.assertNotNull("mongodb-host cannot be empty", args[0]);
		Assert.assertNotNull("mongodb-port cannot be empty", args[1]);
		Assert.assertNotNull("mongodb-database cannot be empty", args[2]);
		
		String host = args[0].trim();
		String port = args[1].trim();
		String dbName = args[2].trim();
		
		Assert.assertFalse("mongodb-host cannot be empty", host.isEmpty());
		Assert.assertFalse("mongodb-port cannot be empty", port.isEmpty());
		Assert.assertFalse("mongodb-database cannot be empty", dbName.isEmpty());
		
		String username = null, password = "";
		if(args.length>3) {
			Assert.assertNotNull("mongodb-user cannot be empty", args[3]);
			Assert.assertFalse("mongodb-user cannot be empty", args[3].isEmpty());
			
			username = args[3].trim();
			if(args.length>4 && args[4]!=null)
				password = args[4].trim();
		}
		
		StringBuilder build = new StringBuilder();
		build.append("MongoDBTestDataSource configuration [\n");
		build.append(String.format("mongodb-host is %s\n", host));
		build.append(String.format("mongodb-port is %s\n", port));
		build.append(String.format("mongodb-database is %s\n", dbName));
		if(username!=null) {
			build.append(String.format("mongodb-user is %s\n", username));
			build.append(String.format("mongodb-password is %s\n", password));
		}
		logger.info(build.toString());
		
		try {
			String[] hosts = host.split(",");
			String[] ports = port.split(",");
			
			if(hosts.length>ports.length) {
				Assert.assertEquals(String.format("Port missing for host %s", hosts[ports.length-1]), hosts.length, ports.length);
			} else {
				Assert.assertEquals(String.format("Host missing for port %s", ports[hosts.length-1]), hosts.length, ports.length);
			}
			
			for (String portVal : ports) {
				try {
					Integer.valueOf(portVal);
				} catch (Exception e) {
					throw new AssertionError(String.format("Port value invalid - %s", portVal));
				}
			}
			
			for (int i=0;i<hosts.length;i++) {
				ServerAddress address = new ServerAddress(hosts[i], Integer.valueOf(ports[i]));
				addresses.add(address);
			}
			
			for (int i = 0; i < poolSize; i++) {
				MongoClient mongoClient = null;
				//Now try connecting to the Database
				try {
					mongoClient = new MongoClient(addresses);
				} catch (Exception e) {
					throw new AssertionError(String.format("Connection to MongoDB failed with the error %s", 
							ExceptionUtils.getStackTrace(e)));
				}
				
				DB db = null;
				try {
					db = mongoClient.getDB(dbName);
					if(username!=null && password!=null) {
						Assert.assertTrue(String.format("Authentication to the Mongo database %s failed with %s/%s", dbName, 
								username, password), db.authenticate(username, password.toCharArray()));
					}
				} catch (Exception e) {
					throw new AssertionError(String.format("Error during initialization of MongoDB connection %s", 
							ExceptionUtils.getStackTrace(e)));
				}
				
				addToPool(mongoClient, false);
			}
		} catch (Exception e) {
			throw new AssertionError(String.format("Error during initialization of MongoDB connection %s", 
					ExceptionUtils.getStackTrace(e)));
		} finally {
		}

	}
	
	public void destroy() {
		for (Resource res : pool) {
			MongoClient mongoClient = (MongoClient)res.object;
			mongoClient.close();
		}
		logger.info("Releasing connections....");
	}
	
	public List<Map<String, String>> provide(GatfTestDataProvider provider, AcceptanceTestContext context) {
		
		List<Map<String, String>> result = new ArrayList<Map<String,String>>();
		
		Assert.assertNotNull("provider cannot be null", provider);
		Assert.assertTrue("provider cannot be null", provider.getArgs()!=null && provider.getArgs().length>0);
		Assert.assertNotNull("mongodb-collection cannot be empty", provider.getArgs()[0]);
		Assert.assertNotNull("queryString cannot be empty", provider.getQueryStr());
		Assert.assertNotNull("variableNames cannot be empty", provider.getSourceProperties());
		Assert.assertNotNull("propertyNames cannot be empty", provider.getProviderProperties());
		
		String dbName = args[2].trim();
		String collName = provider.getArgs()[0].trim();
		String queryString = provider.getQueryStr().trim();
		String variableNames = provider.getProviderProperties();
		String propertyNames = provider.getSourceProperties();
		
		Assert.assertNotNull("mongodb-collection cannot be empty", collName.isEmpty());
		Assert.assertFalse("queryString cannot be empty", queryString.isEmpty());
		
		List<String> variableNamesArr = new ArrayList<String>();
		for (String varName : variableNames.split(",")) {
			if(!varName.trim().isEmpty()) {
				variableNamesArr.add(varName);
			}
		}
		Assert.assertTrue("need to define at-least a single variable name", 
				!variableNames.isEmpty() && variableNames.split(",").length>0 && variableNamesArr.size()>0);
		
		List<String> propertyNamesArr = new ArrayList<String>();
		for (String varName : propertyNames.split(",")) {
			if(!varName.trim().isEmpty()) {
				propertyNamesArr.add(varName);
			}
		}
		Assert.assertTrue("need to define at-least a single property name", 
				!propertyNames.isEmpty() && propertyNames.split(",").length>0 && propertyNamesArr.size()>0);
		
		Assert.assertTrue("property name and variable name sizes don't match", 
				propertyNamesArr.size()==variableNamesArr.size());
		
		StringBuilder build = new StringBuilder();
		build.append("Provider configuration [\n");
		build.append(String.format("dataSource name is %s\n", getDataSourceName()));
		build.append(String.format("mongodb-collection is %s\n", collName));
		build.append(String.format("queryString is %s\n", queryString));
		build.append(String.format("propertyNames is %s\n", propertyNames));
		build.append(String.format("variableNames is %s]", variableNames));
		logger.info(build.toString());
		
		Resource res = null;
		try {
			
			res = getResource();
			MongoClient mongoClient = (MongoClient)res.object;
			
			DB db = null;
			try {
				db = mongoClient.getDB(dbName);
				
				DBCollection coll = db.getCollection(collName);
				Assert.assertNotNull(String.format("Mongodb collection %s not found", collName), coll);
				
				DBObject queryObject = null;
				try {
					queryObject = (DBObject) JSON.parse(queryString);
				} catch (Exception e) {
					Assert.assertNotNull("queryString passed is invalid");
				}
				
				DBCursor cursor = null;
				try {
					cursor = coll.find(queryObject);
					while(cursor.hasNext()) {
						DBObject object = cursor.next();
						Map<String, String> row = new HashMap<String, String>();
						for (int i = 0; i < variableNamesArr.size(); i++) {
							//Assert.assertTrue(String.format("Could not find %s field in the result document returned",
							//		propertyNamesArr.get(i)), object.containsField(propertyNamesArr.get(i)));
							if(object.containsField(propertyNamesArr.get(i)))
							{
								row.put(variableNamesArr.get(i), object.get(propertyNamesArr.get(i)).toString());
							}
						}
						result.add(row);
					}
				} catch (Exception e) {
					throw new AssertionError(e);
				} finally {
					if(cursor!=null)
						cursor.close();
				}
			} catch (Exception e) {
				throw new AssertionError(String.format("Fetching Test Data failed while executing query %s with the error %s", 
						queryString, ExceptionUtils.getStackTrace(e)));
			}
		} catch (Exception e) {
			throw new AssertionError(String.format("Fetching Test Data failed while executing query %s with the error %s", 
					queryString, ExceptionUtils.getStackTrace(e)));
		} finally {
			if(res!=null)
				releaseToPool(res);
		}
		return result;
	}

	private static final Pattern DB_REMOVE_REGEX = Pattern.compile("db\\.([^\\.]*)\\.remove\\((.*)\\)", Pattern.DOTALL);
	private static final Pattern DB_SAVE_REGEX = Pattern.compile("db\\.([^\\.]*)\\.save\\((.*)\\)", Pattern.DOTALL);
	private static final Pattern DB_INSERT_REGEX = Pattern.compile("db\\.([^\\.]*)\\.insert\\((.*)\\)", Pattern.DOTALL);
	//private static final Pattern DB_UPDATE_REGEX = Pattern.compile("db\\.([^\\.]*)\\.update\\((.*)\\)", Pattern.DOTALL);
	
	private static Pattern[] ALLOWED_REGEXES = new Pattern[]{DB_REMOVE_REGEX, DB_SAVE_REGEX, DB_INSERT_REGEX};
	
	public boolean execute(String queryStr) {
		
		boolean result = false;
		
		Assert.assertNotNull("queryString cannot be empty", queryStr);
		
		queryStr = queryStr.trim();
		
		int queryType = 0;
		
		String collectionName = null;
		String paramData = null;
		
		boolean found = false;
		for (Pattern pattern : ALLOWED_REGEXES) {
			Matcher match = pattern.matcher(queryStr);
			if(match.matches()) {
				found = true;
				collectionName = match.group(1);
				paramData = match.group(2);
				
				if(DB_REMOVE_REGEX.equals(pattern)) {
					queryType = 1;
				} else if(DB_SAVE_REGEX.equals(pattern)) {
					queryType = 2;
				} else if(DB_INSERT_REGEX.equals(pattern)) {
					queryType = 3;
				}
				
				break;
			}
		}
		
		Assert.assertTrue("Only remove/insert/save queries allowed", found);
		Assert.assertTrue("collectionName not found in query", StringUtils.isNotBlank(collectionName));
		
		String dbName = args[2].trim();
		String queryString = queryStr.trim();
		
		Assert.assertFalse("queryString cannot be empty", queryString.isEmpty());

		StringBuilder build = new StringBuilder();
		build.append("DataSourceHook configuration [\n");
		build.append(String.format("dataSource name is %s\n", getDataSourceName()));
		build.append(String.format("queryString is %s]", queryString));
		logger.info(build.toString());
		
		Resource res = null;
		try {
			
			res = getResource();
			MongoClient mongoClient = (MongoClient)res.object;
			
			DB db = null;
			try {
				db = mongoClient.getDB(dbName);

				try {
					DBCollection coll = db.getCollection(collectionName);
					Assert.assertNotNull("collection not found", coll);
					
					DBObject dbObj = new BasicDBObject();
					if(StringUtils.isNotBlank(paramData))
					{
						dbObj = (DBObject)JSON.parse(paramData);
					}
					
					if(queryType==1) {
						WriteResult response = coll.remove(dbObj);
						result = response!=null && response.getError()==null;
					} else if(queryType==2) {
						WriteResult response = coll.save(dbObj);
						result = response!=null && response.getError()==null;
					} else if(queryType==3) {
						WriteResult response = coll.insert(dbObj);
						result = response!=null && response.getError()==null;
					}
				} catch (Exception e) {
					throw new AssertionError(e);
				}
			} catch (Exception e) {
				throw new AssertionError(String.format("Error occurred while executing query %s with the error %s", 
						queryString, ExceptionUtils.getStackTrace(e)));
			}
		} catch (Exception e) {
			throw new AssertionError(String.format("Error occurred while executing query %s with the error %s", 
					queryString, ExceptionUtils.getStackTrace(e)));
		} finally {
			if(res!=null)releaseToPool(res);
		}
		return result;
	}
	
	private Resource getResource() {
		Resource res = checkoutFromPool();
		MongoClient mongoClient = null;
		if(res==null)
		{
			try {
				Thread.sleep(newResourceCheckoutTimeMs/2);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			
			res = checkoutFromPool();
			if(res!=null)return res;
			
			try {
				Thread.sleep(newResourceCheckoutTimeMs/2);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			
			res = checkoutFromPool();
			if(res!=null)return res;
			
			try {
				mongoClient = new MongoClient(addresses);
			} catch (Exception e) {
				throw new AssertionError(String.format("Connection to MongoDB failed with the error %s", 
						ExceptionUtils.getStackTrace(e)));
			}
			res = addToPool(mongoClient, true);
		}
		return res;
	}
}
 public class RandomValueTestDataProvider implements TestDataProvider {

	public List<Map<String, String>> provide(GatfTestDataProvider provider, AcceptanceTestContext context) {
		
		List<Map<String, String>> result = new ArrayList<Map<String,String>>();
		
		if(provider.getArgs()==null || provider.getArgs().length==0) {
			throw new AssertionError("No arguments passed to the RandomValueTestCaseDataProvider");
		}
		
		if(provider.getArgs().length!=2) {
			throw new AssertionError("Insufficient arguments passed, need all parameters namely," +
					" variableTypes and count");
		}
		
		Assert.assertNotNull("variableNames cannot be empty", provider.getProviderProperties());
		Assert.assertNotNull("variableTypes cannot be empty", provider.getArgs()[0]);
		
		String variableNames = provider.getProviderProperties().trim();
		String variableTypes = provider.getArgs()[0].trim();
		
		Assert.assertFalse("count not defined", provider.getArgs()[1]==null || provider.getArgs()[1].trim().isEmpty());
		
		int varCount = 0;
		try {
			varCount = Integer.valueOf(provider.getArgs()[1].trim());
		} catch (Exception e) {
			throw new AssertionError("count is not valid, should be a number");
		}
		
		List<String> variableNamesArr = new ArrayList<String>();
		for (String varName : variableNames.split(",")) {
			if(!varName.trim().isEmpty()) {
				variableNamesArr.add(varName);
			}
		}
		Assert.assertTrue("need to define at-least a single variable name", 
				!variableNames.isEmpty() && variableNames.split(",").length>0 && variableNamesArr.size()>0);
		
		List<String> variableTypesArr = new ArrayList<String>();
		for (String varType : variableTypes.split(",")) {
			if(!varType.trim().isEmpty()) {
			    Object val = GatfFunctionHandler.handleFunction(varType);
				if(val==null)
				{
					throw new AssertionError("variable type needs to be one of alpha, alphanum, number, boolean," +
							" -number, +number, float, boolean and date(format)");
				}
				variableTypesArr.add(varType);
			}
		}
		Assert.assertTrue("the variable names don't match the variables types defined", 
				variableNamesArr.size()==variableTypesArr.size());
		
		for (int j = 0; j < varCount; j++) {
			Map<String, String> row = new HashMap<String, String>();
			for (int i = 0; i < variableNamesArr.size(); i++) {
				String varName = variableNamesArr.get(i);
				String varType = variableTypesArr.get(i);
				
				String value = GatfFunctionHandler.handleFunction(varType);
				row.put(varName, value);
			}
			result.add(row);
		}
		
		return result;
	}
}
 public class SQLDatabaseTestDataSource extends TestDataSource {

	private Logger logger = Logger.getLogger(SQLDatabaseTestDataSource.class.getSimpleName());
	
	private String jdbcUrl, dbUserName, dbPassword;
	
	public void init() {
		Thread currentThread = Thread.currentThread();
		ClassLoader oldClassLoader = currentThread.getContextClassLoader();
		try {
			currentThread.setContextClassLoader(getProjectClassLoader());
			if(args==null || args.length==0) {
				throw new AssertionError("No arguments passed to the DatabaseProvider");
			}
			
			if(args.length<4) {
				throw new AssertionError("All 4 arguments, namely jdbcUrl, jdbcDriver, dbUserName and" +
						"dbPassword are mandatory for DatabaseProvider");
			}
			
			Assert.assertNotNull("jdbcUrl cannot be empty", args[0]);
			Assert.assertNotNull("jdbcDriver cannot be empty", args[1]);
			Assert.assertNotNull("dbUserName cannot be empty", args[2]);
			Assert.assertNotNull("dbPassword cannot be empty", args[3]);
			
			jdbcUrl = args[0].trim();
			String jdbcDriver = args[1].trim();
			dbUserName = args[2].trim();
			dbPassword = args[3].trim();
			
			Assert.assertFalse("jdbcUrl cannot be empty", jdbcUrl.isEmpty());
			Assert.assertFalse("jdbcDriver cannot be empty", jdbcDriver.isEmpty());
			Assert.assertFalse("dbUserName cannot be empty", dbUserName.isEmpty());
			
			StringBuilder build = new StringBuilder();
			build.append("DatabaseTestDataSource configuration [\n");
			build.append(String.format("jdbcUrl is %s\n", jdbcUrl));
			build.append(String.format("jdbcDriver is %s\n", jdbcDriver));
			build.append(String.format("dbUserName is %s\n", dbUserName));
			build.append(String.format("dbPassword is %s]", dbPassword));
			logger.info(build.toString());
			
			//Load the driver first
			try {
				Driver driver = (Driver)getProjectClassLoader().loadClass(jdbcDriver).getDeclaredConstructor().newInstance();
				CustomDriverManager.registerDriver(driver);
			} catch (Exception e) {
				throw new AssertionError(String.format("Driver class %s not found", jdbcDriver));
			}
			
			for (int i = 0; i < poolSize; i++) {
				//Now try connecting to the Database
				Connection conn = null;
				try {
					conn = CustomDriverManager.getConnection(jdbcUrl, dbUserName, dbPassword);
				} catch (Exception e) {
					throw new AssertionError(String.format("Connection to the Database using the JDBC URL %s failed with the error %s", 
							jdbcUrl, ExceptionUtils.getStackTrace(e)));
				}
				
				addToPool(conn, false);
			}
			
		} catch (Exception t) {
			t.printStackTrace();
		} finally {
			currentThread.setContextClassLoader(oldClassLoader);
		}
	}
	
	public void destroy() {
		for (Resource res : pool) {
			Connection conn = (Connection)res.object;
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		logger.info("Releasing connections....");
	}
	
	public List<Map<String, String>> provide(GatfTestDataProvider provider, AcceptanceTestContext context) {
		
		List<Map<String, String>> result = new ArrayList<Map<String,String>>();
		
		Resource res = null;
		try {
			
			Assert.assertNotNull("queryString cannot be empty", provider.getQueryStr());
			Assert.assertNotNull("variableNames cannot be empty", provider.getProviderProperties());
			
			String queryStr = provider.getQueryStr();
			String providerProperties = provider.getProviderProperties();
			
			Assert.assertFalse("queryString cannot be empty", queryStr.isEmpty());
			Assert.assertTrue("only select queries allowed", queryStr.toLowerCase().indexOf("select ")==0);
			
			List<String> variableNamesArr = new ArrayList<String>();
			for (String varName : providerProperties.split(",")) {
				if(!varName.trim().isEmpty()) {
					variableNamesArr.add(varName);
				}
			}
			Assert.assertTrue("need to define at-least a single variable name", 
					!providerProperties.isEmpty() && providerProperties.split(",").length>0 && variableNamesArr.size()>0);
			
			StringBuilder build = new StringBuilder();
			build.append("Provider configuration [\n");
			build.append(String.format("dataSource name is %s\n", getDataSourceName()));
			build.append(String.format("queryString is %s\n", queryStr));
			build.append(String.format("variableNames is %s]", providerProperties));
			logger.info(build.toString());
			
			Statement statement = null;
			ResultSet resultSet = null;
			
			try {
				
				res = getResource();
				Connection conn = (Connection)res.object;
				
				statement = conn.createStatement();
				resultSet = statement.executeQuery(queryStr);
				if(resultSet.getMetaData().getColumnCount()!=variableNamesArr.size())
					throw new AssertionError("The number of columns from the query does not match the variable Names provided");
				for (int i = 0; i < variableNamesArr.size(); i++) {
					Assert.assertTrue(String.format("Invalid variable/sql type, only strings,boolean,numbers allowed for %s", variableNamesArr.get(i)), 
							isValidDataType(resultSet.getMetaData().getColumnType(i+1)));
				}
				while (resultSet.next()) {
					Map<String, String> row = new HashMap<String, String>();
					for (int i = 0; i < variableNamesArr.size(); i++) {
						row.put(variableNamesArr.get(i), resultSet.getString(i+1));
					}
					result.add(row);
				}
			} catch (Exception e) {
				throw new AssertionError(String.format("Fetching Test Data failed while executing query %s with the error %s", 
						queryStr, ExceptionUtils.getStackTrace(e)));
			} finally {
				try {
					if(resultSet!=null)resultSet.close();
				} catch (SQLException e) {
				}
				try {
					if(statement!=null)statement.close();
				} catch (SQLException e) {
				}
			}
		} catch (Exception t) {
			t.printStackTrace();
		} finally {
			if(res!=null)releaseToPool(res);
		}
		return result;
	}
	
	private boolean isValidDataType(int type) {
		return type == java.sql.Types.BIGINT || type == java.sql.Types.BIT || type == java.sql.Types.BOOLEAN || 
				type == java.sql.Types.CHAR || type == java.sql.Types.DATE || type == java.sql.Types.DECIMAL || 
				type == java.sql.Types.DOUBLE || type == java.sql.Types.FLOAT || type == java.sql.Types.INTEGER ||
				type == java.sql.Types.LONGNVARCHAR || type == java.sql.Types.LONGVARCHAR || type == java.sql.Types.NCHAR ||
				type == java.sql.Types.NULL || type == java.sql.Types.NUMERIC || type == java.sql.Types.NVARCHAR ||
				type == java.sql.Types.REAL || type == java.sql.Types.SMALLINT || type == java.sql.Types.TIME ||
				type == java.sql.Types.TINYINT || type == java.sql.Types.TIMESTAMP || type == java.sql.Types.VARCHAR;
	}

	public boolean execute(String queryStr) {
		boolean response = false;
		
		Resource res = null;
		try {
			
			Assert.assertNotNull("queryString cannot be empty", queryStr);
			Assert.assertFalse("queryString cannot be empty", queryStr.isEmpty());
			
			StringBuilder build = new StringBuilder();
			build.append("DataSourceHook configuration [\n");
			build.append(String.format("dataSource name is %s\n", getDataSourceName()));
			build.append(String.format("queryString is %s]", queryStr));
			logger.info(build.toString());
			
			
			Statement statement = null;
			try {
				res = getResource();
				Connection conn = (Connection)res.object;
				
				statement = conn.createStatement();
				if(!queryStr.toLowerCase().startsWith("select ")) {
					response = statement.executeUpdate(queryStr)>0;
				}
			} catch (Exception e) {
				throw new AssertionError(String.format("Error while executing query %s with the error %s", 
						queryStr, ExceptionUtils.getStackTrace(e)));
			} finally {
				try {
					if(statement!=null)statement.close();
				} catch (SQLException e) {
				}
			}
		} catch (Exception t) {
			t.printStackTrace();
		} finally {
			if(res!=null)releaseToPool(res);
		}
		return response;
	}
	
	private Resource getResource() {
		Resource res = checkoutFromPool();
		Connection conn = null;
		if(res==null)
		{
			try {
				Thread.sleep(newResourceCheckoutTimeMs/2);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			
			res = checkoutFromPool();
			if(res!=null)return res;
			
			try {
				Thread.sleep(newResourceCheckoutTimeMs/2);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			
			res = checkoutFromPool();
			if(res!=null)return res;
			
			//Now try connecting to the Database
			try {
				conn = CustomDriverManager.getConnection(jdbcUrl, dbUserName, dbPassword);
			} catch (Exception e) {
				throw new AssertionError(String.format("Connection to the Database using the JDBC URL %s failed with the error %s", 
						jdbcUrl, ExceptionUtils.getStackTrace(e)));
			}
			res = addToPool(conn, true);
		}
		return res;
	}
}
 public interface TestDataHook {

	boolean execute(String queryStr);
}
 public interface TestDataProvider {

	List<Map<String, String>> provide(GatfTestDataProvider provider, AcceptanceTestContext context);
}
 public abstract class TestDataSource implements TestDataProvider, TestDataHook {

	protected String className;
	
	protected String[] args;
	
	protected String dataSourceName;
	
	protected int poolSize = 1;
	
	protected long newResourceCheckoutTimeMs = 1000;
	
	private AcceptanceTestContext context;
	
	protected final List<Resource> pool = new ArrayList<Resource>();
	
	private final List<Boolean> poolBusyStatus = new ArrayList<Boolean>();
	
	public void setContext(AcceptanceTestContext context) {
		this.context = context;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public String[] getArgs() {
		return args;
	}

	public void setArgs(String[] args) {
		this.args = args;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public ClassLoader getProjectClassLoader() {
		return context.getProjectClassLoader();
	}

	protected File getResourceFile(String filePath) {
		return context.getResourceFile(filePath);
	}

	public int getPoolSize() {
		return poolSize;
	}

	public void setPoolSize(int poolSize) {
		this.poolSize = poolSize;
	}
	
	public long getNewResourceCheckoutTimeMs() {
		return newResourceCheckoutTimeMs;
	}

	public void setNewResourceCheckoutTimeMs(long newResourceCheckoutTimeMs) {
		this.newResourceCheckoutTimeMs = newResourceCheckoutTimeMs;
	}

	public void init() {
	}
	
	public void destroy() {
	}
	
	public Resource addToPool(Object object, boolean initialStatus) {
		synchronized (pool) {
			Resource res = new Resource();
			res.index = pool.size();
			res.object = object;
			pool.add(res);
			poolBusyStatus.add(initialStatus);
			return res;
		}
	}
	
	public void releaseToPool(Resource res) {
		synchronized (pool) {
			poolBusyStatus.set(res.index, false);
		}
	}
	
	public Resource checkoutFromPool() {
		synchronized (pool) {
			for (int i=0;i<poolBusyStatus.size();i++) {
				if(!poolBusyStatus.get(i)) {
					poolBusyStatus.set(i, true);
					return pool.get(i);
				}
			}
		}
		return null;
	}
	
	protected static class Resource {
		int index;
		Object object;
	}
}
 public class DistributedAcceptanceContext implements Serializable {

	private static final long serialVersionUID = 1L;

	public static enum Command
	{
		CONFIG_SHARE_REQ,
		CONFIG_SHARE_RES,
		TESTS_SHARE_REQ,
		TESTS_SHARE_RES,
		LOAD_TESTS_RES,
		SELENIUM_REQ,
		SELENIUM_RES,
		INVALID
	}
	
	private String node = null;
	
	private GatfExecutorConfig config = null;
	
	private Map<String, String> httpHeaders = null;
	
	private Map<String, String> soapEndpoints = null;
	
	private Map<String, String> soapMessages = null;
	
	private Map<String, String> soapActions = new HashMap<String, String>();
	
	List<Object[]> selTestdata = new ArrayList<Object[]>();
    
    private int index;
    
    private static Pool<Kryo> pool = new Pool<Kryo>(true, true) {
		@Override
		protected Kryo create() {
			Kryo k = new Kryo();
			k.setRegistrationRequired(false); 
            k.setClassLoader(DistributedGatfListener.class.getClassLoader());
            return k;
		}
	};
	
	public String getNode() {
		return node;
	}

	public void setNode(String node) {
		this.node = node;
	}

	public GatfExecutorConfig getConfig() {
		return config;
	}

	public void setConfig(GatfExecutorConfig config) {
		this.config = config;
	}

	public Map<String, String> getHttpHeaders() {
		return httpHeaders;
	}

	public void setHttpHeaders(Map<String, String> httpHeaders) {
		this.httpHeaders = httpHeaders;
	}

	public Map<String, String> getSoapEndpoints() {
		return soapEndpoints;
	}

	public void setSoapEndpoints(Map<String, String> soapEndpoints) {
		this.soapEndpoints = soapEndpoints;
	}

	public Map<String, String> getSoapMessages() {
		return soapMessages;
	}

	public void setSoapMessages(Map<String, String> soapMessages) {
		this.soapMessages = soapMessages;
	}

	public Map<String, String> getSoapActions() {
		return soapActions;
	}

	public void setSoapActions(Map<String, String> soapActions) {
		this.soapActions = soapActions;
	}
	
	public File getResourceFile(String filename) {
		File basePath = new File(config.getTestCasesBasePath());
	    File testPath = new File(basePath, config.getTestCaseDir());
	    File resource = new File(testPath, filename);
        if(!resource.exists()) {
            resource = new File(basePath, filename);
        }
		return resource;
    }

    public List<Object[]> getSelTestdata() {
        return selTestdata;
    }

    public void setSelTestdata(List<Object[]> selTestdata) {
        this.selTestdata = selTestdata;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
    
	public static void ser(Output output, Object o) {
        Kryo kryo = pool.obtain();
        try {
            kryo.writeClassAndObject(output, o);
            output.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.free(kryo);
        }
    }
    
    @SuppressWarnings("unchecked")
	public static <T> T unser(Input input) {
        Kryo kryo = pool.obtain();
        try {
            Object r = kryo.readClassAndObject(input);
            return (T)r;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pool.free(kryo);
        }
        return null;
    }
}
 public class PerformanceTestCaseExecutor implements TestCaseExecutor {

	public List<TestCaseReport> execute(TestCase testCase, TestCaseExecutorUtil testCaseExecutorUtil) {
	
		WorkflowContextHandler workflowContextHandler = testCaseExecutorUtil.getContext().getWorkflowContextHandler();
		
		SingleTestCaseExecutor singleTestCaseExecutor = new SingleTestCaseExecutor();
		List<TestCaseReport> reports = singleTestCaseExecutor.execute(testCase, testCaseExecutorUtil);
		
		TestCaseReport testCaseReport = reports==null || reports.isEmpty() ? null : reports.get(0);
		reports.add(new TestCaseReport(testCaseReport));
		
		testCaseReport.setNumberOfRuns(1);
		testCaseReport.getExecutionTimes().add(testCaseReport.getExecutionTime());
		
		if(testCase.isStopOnFirstFailureForPerfTest() && testCase.isFailed())
		{
			return reports;
		}
		
		List<CompletableFuture<TestCaseReport>> futures = new ArrayList<CompletableFuture<TestCaseReport>>();
		for (int i = 0; i < testCase.getNumberOfExecutions(); i++) {
			
				testCaseReport.setNumberOfRuns(testCaseReport.getNumberOfRuns()+1);
				TestCase testCaseCopy = new TestCase(testCase);
				TestCaseReport testCaseReportCopy = new TestCaseReport(testCaseReport);
				reports.add(testCaseReportCopy);
			
			try {
				workflowContextHandler.handleContextVariables(testCaseCopy, new HashMap<String, String>(), 
						testCaseExecutorUtil.getContext());
			} catch (Throwable e) {
				testCaseReportCopy.setExecutionTime(0L);
				testCaseReportCopy.setStatus(TestStatus.Failed.status);
				testCaseReport.setFailureReason(TestFailureReason.Exception.status);
				testCaseReportCopy.setErrorText(ExceptionUtils.getStackTrace(e));
				testCaseReportCopy.setError(e.getMessage());
				if(e.getMessage()==null && testCaseReportCopy.getErrorText()!=null && testCaseReportCopy.getErrorText().indexOf("\n")!=-1) {
					testCaseReportCopy.setError(testCaseReportCopy.getErrorText().substring(0, testCaseReportCopy.getErrorText().indexOf("\n")));
				}
				testCaseReport.setExecutionTime(testCaseReport.getExecutionTime() + testCaseReportCopy.getExecutionTime());
				testCaseReport.getExecutionTimes().add(testCaseReportCopy.getExecutionTime());
				testCaseReport.getErrors().put(i+2+"", testCaseReportCopy.getError());

				e.printStackTrace();
				continue;
			}
			
			CompletableFuture<TestCaseReport> CompletableFuture = testCaseExecutorUtil.executeTestCase(testCaseCopy, testCaseReportCopy);
			futures.add(CompletableFuture);
		}
		
		for(int i = 0; i < testCase.getNumberOfExecutions(); i++) {
            CompletableFuture<TestCaseReport> CompletableFuture = futures.get(i);
            while(!CompletableFuture.isDone()) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                }
            }
		}
		
		for(int i = 0; i < testCase.getNumberOfExecutions(); i++) {
			CompletableFuture<TestCaseReport> CompletableFuture = futures.get(i);
			try {
				TestCaseReport tc = CompletableFuture.get();
				testCaseReport.setExecutionTime(testCaseReport.getExecutionTime() + tc.getExecutionTime());
				testCaseReport.getExecutionTimes().add(tc.getExecutionTime());
				if(tc.getError()!=null)
				{
					testCaseReport.getErrors().put(i+2+"", tc.getError());
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		testCaseReport.setAverageExecutionTime(testCaseReport.getExecutionTime()/testCaseReport.getNumberOfRuns());
		return reports;
	}
} public class ScenarioTestCaseExecutor implements TestCaseExecutor {

	private Logger logger = Logger.getLogger(ScenarioTestCaseExecutor.class.getSimpleName());
	
	public List<TestCaseReport> execute(TestCase testCase, TestCaseExecutorUtil testCaseExecutorUtil) {

		List<TestCaseReport> lst = new ArrayList<TestCaseReport>();
		
		WorkflowContextHandler workflowContextHandler = testCaseExecutorUtil.getContext().getWorkflowContextHandler();
		
		List<CompletableFuture<TestCaseReport>> futures = new ArrayList<CompletableFuture<TestCaseReport>>();
		for (Map<String, String> scenarioMap : testCase.getRepeatScenarios()) {
			
			logger.info("Running with Scenario map = " + scenarioMap);
			
			TestCase testCaseCopy = new TestCase(testCase);
			TestCaseReport testCaseReport = new TestCaseReport();
			testCaseReport.setTestCase(testCaseCopy);
			testCaseReport.setNumberOfRuns(1);
			testCaseCopy.setCurrentScenarioVariables(scenarioMap);

			try {
				workflowContextHandler.handleContextVariables(testCaseCopy, scenarioMap, 
						testCaseExecutorUtil.getContext());
			} catch (Exception e) {
				testCaseReport.setExecutionTime(0L);
				testCaseReport.setStatus(TestStatus.Failed.status);
				testCaseReport.setFailureReason(TestFailureReason.Exception.status);
				testCaseReport.setError(e.getMessage());
				testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
				if(e.getMessage()==null && testCaseReport.getErrorText()!=null && testCaseReport.getErrorText().indexOf("\n")!=-1) {
					testCaseReport.setError(testCaseReport.getErrorText().substring(0, testCaseReport.getErrorText().indexOf("\n")));
				}
				lst.add(testCaseReport);

				e.printStackTrace();
				continue;
			}
				
			CompletableFuture<TestCaseReport> CompletableFuture = testCaseExecutorUtil.executeTestCase(testCaseCopy, testCaseReport);
			
			if(!testCaseReport.getTestCase().isRepeatScenariosConcurrentExecution())
			{
				try {
	                while(!CompletableFuture.isDone()) {
	                    Thread.sleep(1);
	                }
					testCaseReport = CompletableFuture.get();
					ResponseValidator.validateLogicalConditions(testCaseReport.getTestCase(), 
							testCaseExecutorUtil.getContext(), scenarioMap);
					testCaseReport.getTestCase().setCurrentScenarioVariables(null);
				} catch (Exception e) {
					testCaseReport.setStatus(TestStatus.Failed.status);
					testCaseReport.setFailureReason(TestFailureReason.Exception.status);
					testCaseReport.setError(e.getMessage());
					testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
					e.printStackTrace();
				} catch (AssertionError e) {
					testCaseReport.setStatus(TestStatus.Failed.status);
					testCaseReport.setFailureReason(TestFailureReason.NodeValidationFailed.status);
					testCaseReport.setError(e.getMessage());
					testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
					e.printStackTrace();
				}
				lst.add(testCaseReport);
			}
			else
			{
				futures.add(CompletableFuture);
			}
		}
		
		for (CompletableFuture<TestCaseReport> CompletableFuture : futures) {
		    while(!CompletableFuture.isDone()) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                }
            }
        }
		
		for (CompletableFuture<TestCaseReport> CompletableFuture : futures) {
			TestCaseReport testCaseReport = null;
			try {
				testCaseReport = CompletableFuture.get();
				ResponseValidator.validateLogicalConditions(testCaseReport.getTestCase(), 
						testCaseExecutorUtil.getContext(), testCaseReport.getTestCase().getCurrentScenarioVariables());
				testCaseReport.getTestCase().setCurrentScenarioVariables(null);
			} catch (Exception e) {
				testCaseReport.setStatus(TestStatus.Failed.status);
				testCaseReport.setFailureReason(TestFailureReason.Exception.status);
				testCaseReport.setError(e.getMessage());
				testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
				e.printStackTrace();
			} catch (AssertionError e) {
				testCaseReport.setStatus(TestStatus.Failed.status);
				testCaseReport.setFailureReason(TestFailureReason.NodeValidationFailed.status);
				testCaseReport.setError(e.getMessage());
				testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
				e.printStackTrace();
			}
			lst.add(testCaseReport);
		}
		
		return lst;
	}

}
 public class SingleTestCaseExecutor implements TestCaseExecutor {

	public List<TestCaseReport> execute(TestCase testCase, TestCaseExecutorUtil testCaseExecutorUtil) {

		WorkflowContextHandler workflowContextHandler = testCaseExecutorUtil.getContext().getWorkflowContextHandler();
		
		TestCaseReport testCaseReport = new TestCaseReport();
		testCaseReport.setTestCase(testCase);
		testCaseReport.setNumberOfRuns(1);
		
		try {
			workflowContextHandler.handleContextVariables(testCase, new HashMap<String, String>(), testCaseExecutorUtil.getContext());
		} catch (Exception e) {
			testCaseReport.setExecutionTime(0L);
			testCaseReport.setStatus(TestStatus.Failed.status);
			testCaseReport.setFailureReason(TestFailureReason.Exception.status);
			testCaseReport.setError(e.getMessage());
			testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
			if(e.getMessage()==null && testCaseReport.getErrorText()!=null && testCaseReport.getErrorText().indexOf("\n")!=-1) {
				testCaseReport.setError(testCaseReport.getErrorText().substring(0, testCaseReport.getErrorText().indexOf("\n")));
			}

			e.printStackTrace();
			
			List<TestCaseReport> lst = new ArrayList<TestCaseReport>();
			lst.add(testCaseReport);
			return lst;
		}
		
		CompletableFuture<TestCaseReport> report = testCaseExecutorUtil.executeTestCase(testCase, testCaseReport);
		try {
		    while(!report.isDone()) {
		        Thread.sleep(1);
		    }
			testCaseReport = report.get();
		} catch (Exception e) {
			testCaseReport.setStatus(TestStatus.Failed.status);
			testCaseReport.setError(e.getMessage());
			testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
			e.printStackTrace();
		}
		
		List<TestCaseReport> lst = new ArrayList<TestCaseReport>();
		lst.add(testCaseReport);
		return lst;
	}
	
	public List<TestCaseReport> executeDirectTestCase(TestCase testCase, TestCaseExecutorUtil testCaseExecutorUtil) {

		TestCaseReport testCaseReport = new TestCaseReport();
		testCaseReport.setTestCase(testCase);
		testCaseReport.setNumberOfRuns(1);
		
		try {
			CompletableFuture<TestCaseReport> report = testCaseExecutorUtil.executeTestCase(testCase, testCaseReport);
			while(!report.isDone()) {
				Thread.sleep(1);
			}
			testCaseReport = report.get();
		} catch (Exception e) {
			testCaseReport.setStatus(TestStatus.Failed.status);
			testCaseReport.setError(e.getMessage());
			testCaseReport.setErrorText(ExceptionUtils.getStackTrace(e));
			e.printStackTrace();
		}
		
		List<TestCaseReport> lst = new ArrayList<TestCaseReport>();
		lst.add(testCaseReport);
		return lst;
	}
}
 public abstract class TestCaseFinder {

	public enum TestCaseFileType
	{
		XML(".xml"),
		JSON(".json"),
		CSV(".csv"),
		XLS(".xls"),
		XLSX(".xlsx");
		
		public String ext;
		
		private TestCaseFileType(String ext)
		{
			this.ext = ext;
		}
	}
	
	protected abstract TestCaseFileType getFileType();
	public abstract List<TestCase> resolveTestCases(File testCaseFile) throws Exception;
	
	private List<String> targetFileNames;
	
	public static final FileFilter DIR_FILTER = new FileFilter() {
		public boolean accept(File file) {
			return file.isDirectory();
		}
	};
	
	public static final FileFilter FILE_FILTER = new FileFilter() {
		public boolean accept(File file) {
			return !file.isDirectory();
		}
	};
	
	public static final FileFilter NOZIP_FILE_FILTER = new FileFilter() {
		public boolean accept(File file) {
			return !file.isDirectory() && !file.getName().endsWith(".zip");
		}
	};
	
	public static void getFiles(File dir, FilenameFilter filter, List<File> fileLst)
	{
		if (dir.isDirectory()) {
			File[] files = dir.listFiles(filter);
			for (File file : files) {
				fileLst.add(file);
				getFiles(file, filter, fileLst);
			}
			
			files = dir.listFiles(DIR_FILTER);
			for (File file : files) {
				getFiles(file, filter, fileLst);
			}
		}
	}
	
	public List<String> getTargetFileNames() {
		if(targetFileNames==null) {
			targetFileNames = new ArrayList<String>();
		}
		return targetFileNames;
	}
	public void setTargetFileNames(List<String> targetFileNames) {
		this.targetFileNames = targetFileNames;
	}
	public List<TestCase> findTestCases(File dir, AcceptanceTestContext context, boolean considerConfig,
			Set<String> relativeFileNames)
	{
		if(context==null)
			considerConfig = false;
		
		String[] ignoreFiles = considerConfig?context.getGatfExecutorConfig().getIgnoreFiles():null;
		String[] orderedFiles = considerConfig?context.getGatfExecutorConfig().getOrderedFiles():null;
		boolean isOrderByFileName = considerConfig?context.getGatfExecutorConfig().isOrderByFileName():false;
		
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File folder, String name) {
				if(targetFileNames!=null && targetFileNames.size()>0)
				{
					for (String tfileName : targetFileNames) {
						return name.equalsIgnoreCase(tfileName) && name.toLowerCase().endsWith(getFileType().ext);
					}
					return false;
				}
				else
				{
					return name.toLowerCase().endsWith(getFileType().ext);
				}
			}
		};
		
		List<TestCase> testcases = new ArrayList<TestCase>();
		if (dir.isDirectory()) {
			List<File> files = new ArrayList<File>();
			getFiles(dir, filter, files);
			
			if(considerConfig)
			{
				files = filterFiles(ignoreFiles, files, dir);
				files = orderFiles(orderedFiles, isOrderByFileName, files, dir);
			}
			
			for (File file : files) {
				String relativeFileName = getRelativePath(file, dir);
				try {
					List<TestCase> testcasesTemp = resolveTestCases(file);
					if(testcasesTemp != null)
					{
						for (TestCase testCase : testcasesTemp) {
							testCase.setSourcefileName(relativeFileName);
							if(testCase.getSimulationNumber()==null)
							{
								testCase.setSimulationNumber(0);
							}
							if(considerConfig)
							{
							    if(StringUtils.isBlank(testCase.getBaseUrl())) {
							        testCase.setBaseUrl(context.getGatfExecutorConfig().getBaseUrl());
							    }
							}
							testCase.setExternalApi(false);
							testCase.setServerApiAuth(false);
							testCase.setServerApiTarget(false);
						}
						
						testcases.addAll(testcasesTemp);
						if(relativeFileNames!=null)relativeFileNames.add(relativeFileName);
					}
				} catch (Exception e) {
					System.out.println("Ignoring file due to invalid testcase format ... " + relativeFileName);
					//e.printStackTrace();
				}
			}
		}
		return testcases;
	}
	
	// returns null if file isn't relative to folder
	public static String getRelativePath(File file, File folder) {
	    String filePath = file.getAbsolutePath();
	    String folderPath = folder.getAbsolutePath();
	    if (filePath.startsWith(folderPath)) {
	        return filePath.substring(folderPath.length() + 1);
	    } else {
	        return null;
	    }
	}
	
	public static List<File> filterFiles(String[] ignoreFiles, List<File> testFiles, File dir)
	{
		if(ignoreFiles!=null)
		{
			List<File> files = new ArrayList<File>();
			for (String fileN : ignoreFiles) {
				fileN = fileN.trim();
				if(fileN.isEmpty()) {
					continue;
				}
				if(fileN.equals("*") || fileN.equals("*.*")) {
					return files;
				}
			}
			
			for (File file : testFiles) {
				boolean isIgnore = false;
				
				String completeFileName = getRelativePath(file, dir);
				String fileName = file.getName();
				
				for (String fileN : ignoreFiles) {
					fileN = fileN.trim();
					
					boolean isCompleteNameMatch = fileN.indexOf("\\")!=-1;
					
					if(fileN.isEmpty()) {
						continue;
					}
					
					if(fileN.startsWith("*.")) {
						String ext = fileN.substring(2);
						if((isCompleteNameMatch && completeFileName.endsWith(ext))
								|| (!isCompleteNameMatch && fileName.endsWith(ext))) {
							isIgnore = true;
							break;
						}
					} else if(fileN.endsWith("*")) {
						fileN = fileN.substring(0, fileN.lastIndexOf("*"));
						if((isCompleteNameMatch && completeFileName.startsWith(fileN))
								|| (!isCompleteNameMatch && fileName.startsWith(fileN))) {
							isIgnore = true; 
							break;
						}
					} else if(fileN.startsWith("*")) {
						fileN = fileN.substring(1);
						if((isCompleteNameMatch && completeFileName.endsWith(fileN))
								|| (!isCompleteNameMatch && fileName.endsWith(fileN))) {
							isIgnore = true;
							break;
						}
					} else if((isCompleteNameMatch && completeFileName.equals(fileN))
							|| (!isCompleteNameMatch && fileName.equals(fileN))) {
						isIgnore = true;
						break;
					}
				}
				if(isIgnore)
					continue;
				
				files.add(file);
			}
			return files;
		}
		return testFiles;
	}
	
	public static List<File> orderFiles(String[] orderedFiles, boolean isOrderByFileName, List<File> files, File dir)
	{
		if(orderedFiles!=null)
		{
			List<File> allFiles = new ArrayList<File>();
			for (String fileN : orderedFiles) {
				for (File file : files) {
					String completeFileName = getRelativePath(file, dir);
					if(completeFileName.equals(fileN)) {
						allFiles.add(file);
						break;
					}
				}
			}
			for (File file : files) {
				if(!allFiles.contains(file)) {
					allFiles.add(file);
				}
			}
			return allFiles;
		}
		else
		{
			if(isOrderByFileName) {
				AlphanumComparator<File> comparator = new AlphanumComparator<File>();
				Collections.sort(files, comparator);
			}
			return files;
		}
	}
	
	public static List<File> filterValidTestCaseFiles(List<File> testFiles)
	{
		List<File> allFiles = new ArrayList<File>();
		for (File file : testFiles) {
		    if(file.getName().toLowerCase().endsWith(".sel")) {
		        allFiles.add(file);
		        continue;
		    }
			try {
				new XMLTestCaseFinder().resolveTestCases(file);
				allFiles.add(file);
			} catch (Exception e) {
			}
		}
		return allFiles;
	}
}
 public class XMLTestCaseFinder extends TestCaseFinder {

	protected TestCaseFileType getFileType() {
		return TestCaseFileType.XML;
	}

	@SuppressWarnings("unchecked")
	public List<TestCase> resolveTestCases(File testCaseFile) throws Exception {
		XStream xstream = new XStream(new DomDriver("UTF-8"));
       
        xstream.allowTypes(new Class[]{TestCase.class});
		xstream.processAnnotations(new Class[]{TestCase.class});
		xstream.alias("TestCases", List.class);
		List<TestCase> xmlTestCases = (List<TestCase>)xstream.fromXML(testCaseFile);
		return xmlTestCases;
	}
}
 @JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class TestCaseReport implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	private String actualUrl;
	
	private String url;
	
	private String method;
	
	private String workflowName;

	private TestCase testCase;
	
	private String status;
	
	private String failureReason;
	
	private Integer numberOfRuns;
	
	private Long executionTime = 0L;
	
	private List<Long> executionTimes = new ArrayList<Long>();
	
	private Long averageExecutionTime;
	
	private String requestContent;
	
	private String requestHeaders;
	
	private String responseHeaders;
	
	private String requestContentType;
	
	private String responseContentType;
	
	private String responseContent;
	
	private Integer responseStatusCode;
	
	private String error;
	
	private String errorText;
	
	private Map<String, String> errors = new HashMap<String, String>();
	
	private String testIdentifier;
	
	private List<String> aexpectedNodes = new ArrayList<String>();
	
	private String serverLogs;

	@XStreamOmitField
	@JsonIgnore
	private Map<String, List<String>> resHeaders;
	
	private List<Map<String, Object>> perfResult = new ArrayList<Map<String, Object>>();
	
	public String getWorkflowName() {
		return workflowName;
	}

	public void setWorkflowName(String workflowName) {
		this.workflowName = workflowName;
	}

	public TestCase getTestCase() {
		return testCase;
	}

	public void setTestCase(TestCase testCase) {
		this.testCase = testCase;
		if(testCase!=null) {
			if(testCase.getIdentifier().equals(testCase.getSourcefileName()))
				setTestIdentifier(testCase.getIdentifier()+"\n"+testCase.getName());
			else
				setTestIdentifier(testCase.getIdentifier()+"\n"+testCase.getSourcefileName()+"\n"+testCase.getName());
			if(testCase.getAurl()!=null)
			{
				setActualUrl(testCase.getAurl());
			}
			else
			{
				setActualUrl(testCase.getUrl());
			}
			setRequestContent(testCase.getAcontent());
			setUrl(testCase.getUrl());
			setMethod(testCase.getMethod());
			setAexpectedNodes(testCase.getAexpectedNodes());
		}
	}
	
	public void setTestCaseExternalApi(TestCase testCase) {
		this.testCase = testCase;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public Integer getNumberOfRuns() {
		return numberOfRuns;
	}

	public void setNumberOfRuns(Integer numberOfRuns) {
		this.numberOfRuns = numberOfRuns;
	}

	public Long getExecutionTime() {
		return executionTime;
	}

	public void setExecutionTime(Long executionTime) {
		this.executionTime = executionTime;
	}

	public List<Long> getExecutionTimes() {
		return executionTimes;
	}

	public void setExecutionTimes(List<Long> executionTimes) {
		this.executionTimes = executionTimes;
	}

	public String getRequestContent() {
		return requestContent;
	}

	public void setRequestContent(String requestContent) {
		this.requestContent = requestContent;
	}

	public String getResponseHeaders() {
		return responseHeaders;
	}

	public void setResponseHeaders(String responseHeaders) {
		this.responseHeaders = responseHeaders;
	}

	public String getResponseContent() {
		return responseContent;
	}

	public void setResponseContent(String responseContent) {
		this.responseContent = responseContent;
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public String getActualUrl() {
		return actualUrl;
	}

	public void setActualUrl(String actualUrl) {
		this.actualUrl = actualUrl;
	}

	public String getErrorText() {
		return errorText;
	}

	public void setErrorText(String errorText) {
		this.errorText = errorText;
	}

	public String getTestIdentifier() {
		return testIdentifier;
	}

	public void setTestIdentifier(String testIdentifier) {
		this.testIdentifier = testIdentifier;
	}

	public Long getAverageExecutionTime() {
		return averageExecutionTime;
	}

	public void setAverageExecutionTime(Long averageExecutionTime) {
		this.averageExecutionTime = averageExecutionTime;
	}

	public Map<String, String> getErrors() {
		return errors;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public void setErrors(Map<String, String> errors) {
		this.errors = errors;
	}

	public String getRequestHeaders() {
		return requestHeaders;
	}

	public void setRequestHeaders(String requestHeaders) {
		this.requestHeaders = requestHeaders;
	}

	public String getRequestContentType() {
		return requestContentType;
	}

	public void setRequestContentType(String requestContentType) {
		this.requestContentType = requestContentType;
	}

	public String getResponseContentType() {
		return responseContentType;
	}

	public void setResponseContentType(String responseContentType) {
		this.responseContentType = responseContentType;
	}

	public Integer getResponseStatusCode() {
		return responseStatusCode;
	}

	public void setResponseStatusCode(Integer responseStatusCode) {
		this.responseStatusCode = responseStatusCode;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public String getFailureReason() {
		return failureReason;
	}

	public void setFailureReason(String failureReason) {
		this.failureReason = failureReason;
	}

	public List<String> getAexpectedNodes() {
		return aexpectedNodes;
	}

	public void setAexpectedNodes(List<String> aexpectedNodes) {
		this.aexpectedNodes = aexpectedNodes;
	}

	public Map<String, List<String>> getResHeaders() {
		return resHeaders;
	}

	public void setResHeaders(Map<String, List<String>> resHeaders) {
		this.resHeaders = resHeaders;
	}

	public String getServerLogs() {
		return serverLogs;
	}

	public void setServerLogs(String serverLogs) {
		this.serverLogs = serverLogs;
	}

	public List<Map<String, Object>> getPerfResult() {
		return perfResult;
	}

	public void setPerfResult(List<Map<String, Object>> perfResult) {
		this.perfResult = perfResult;
	}

	public TestCaseReport(){}
	
	public TestCaseReport(TestCaseReport other) {
		super();
		this.actualUrl = other.actualUrl;
		this.workflowName = other.workflowName;
		this.testCase = other.testCase;
		this.status = other.status;
		this.numberOfRuns = other.numberOfRuns;
		this.executionTime = other.executionTime;
		if(other.executionTimes!=null)
		{
			this.executionTimes = new ArrayList<Long>(other.executionTimes);
		}
		this.requestContent = other.requestContent;
		this.responseHeaders = other.responseHeaders;
		this.responseContent = other.responseContent;
		this.error = other.error;
		this.errorText = other.errorText;
		this.testIdentifier = other.testIdentifier;
		if(other.errors!=null)
		{
			this.errors = other .errors;
		}
		this.responseStatusCode = other.responseStatusCode;
		this.method = other.method;
		this.failureReason = other.failureReason;
		this.perfResult = other.perfResult;
	}

	@Override
	public String toString() {
		final int maxLen = 10;
		StringBuilder builder = new StringBuilder();
		builder.append("TestCaseReport = [\n");
		builder.append("actualUrl=");
		builder.append(actualUrl);
		builder.append("\nurl=");
		builder.append(url);
		builder.append("\nmethod=");
		builder.append(method);
		builder.append("\nworkflowName=");
		builder.append(workflowName);
		builder.append("\nstatus=");
		builder.append(status);
		builder.append("\nfailureReason=");
		builder.append(failureReason);
		builder.append("\nnumberOfRuns=");
		builder.append(numberOfRuns);
		builder.append("\nexecutionTime=");
		builder.append(executionTime);
		builder.append("\nexecutionTimes=");
		builder.append(executionTimes != null ? toString(executionTimes, maxLen)
				: null);
		builder.append("\naverageExecutionTime=");
		builder.append(averageExecutionTime);
		builder.append("\nrequestContent=");
		builder.append(requestContent);
		builder.append("\nrequestHeaders=");
		builder.append(requestHeaders);
		builder.append("\nresponseHeaders=");
		builder.append(responseHeaders);
		builder.append("\nrequestContentType=");
		builder.append(requestContentType);
		builder.append("\nresponseContentType=");
		builder.append(responseContentType);
		builder.append("\nresponseContent=");
		builder.append(responseContent);
		builder.append("\nresponseStatusCode=");
		builder.append(responseStatusCode);
		builder.append("\nerror=");
		builder.append(error);
		builder.append("\nerrorText=");
		builder.append(errorText);
		builder.append("\nerrors=");
		builder.append(errors != null ? toString(errors.entrySet(), maxLen)
				: null);
		builder.append("\ntestIdentifier=");
		builder.append(testIdentifier);
		builder.append("]\n");
		return builder.toString();
	}

	private String toString(Collection<?> collection, int maxLen) {
		StringBuilder builder = new StringBuilder();
		builder.append("[");
		int i = 0;
		for (Iterator<?> iterator = collection.iterator(); iterator.hasNext()
				&& i < maxLen; i++) {
			if (i > 0)
				builder.append("\n");
			builder.append(iterator.next());
		}
		builder.append("]");
		return builder.toString();
	}
	
	public static enum TestStatus {
		Success("Success"),
		Skipped("Skipped"),
		Failed("Failed");
		
		public String status;
		private TestStatus(String status) {
			this.status = status;
		}
	}
	
	public static enum TestFailureReason {
		InvalidStatusCode("InvalidStatusCode"),
		Exception("Exception"),
		NodeValidationFailed("NodeValidationFailed"),
		InvalidContentType("InvalidContentType"),
		ExecuteConditionFailed("ExecConditionFailed");
		
		public String status;
		private TestFailureReason(String status) {
			this.status = status;
		}
	}
	
	private String sanitizeString(String inp)
	{
		return inp.replace("\"", "\\\"").replace("\n", "\\\n");
	}
	
	public String toStatisticsCsv()
	{
		StringBuilder builder = new StringBuilder();
		builder.append("\"");
		if(testCase.getSourcefileName().equals(testCase.getIdentifier()))
		{
			builder.append(sanitizeString(testCase.getSourcefileName()));
		}
		else
		{
			builder.append(sanitizeString(testCase.getSourcefileName()));
			builder.append("(");
			builder.append(testCase.getIdentifier());
			builder.append(")");
		}
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(sanitizeString(testCase.getName()));
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(method);
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(actualUrl);
		builder.append("\"");
		builder.append(",");
		builder.append("\"");
		builder.append(sanitizeString(testCase.getDescription()));
		builder.append("\"");
		builder.append(",");
		builder.append(executionTime);
		builder.append(",");
		builder.append(responseStatusCode);
		builder.append(",");
		builder.append("\"");
		builder.append(status);
		builder.append("\"\n");
		return builder.toString();
	}
}

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import javax.ws.rs.core.HttpHeaders;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.routines.UrlValidator;
import org.glassfish.grizzly.http.server.Response;
import org.junit.Assert;
import org.reficio.ws.builder.SoapBuilder;
import org.reficio.ws.builder.SoapOperation;
import org.reficio.ws.builder.core.Wsdl;
import org.w3c.dom.Document;
import com.gatf.executor.dataprovider.FileTestDataProvider;
import com.gatf.executor.dataprovider.GatfTestDataConfig;
import com.gatf.executor.dataprovider.GatfTestDataProvider;
import com.gatf.executor.dataprovider.GatfTestDataSource;
import com.gatf.executor.dataprovider.GatfTestDataSourceHook;
import com.gatf.executor.dataprovider.InlineValueTestDataProvider;
import com.gatf.executor.dataprovider.MongoDBTestDataSource;
import com.gatf.executor.dataprovider.RandomValueTestDataProvider;
import com.gatf.executor.dataprovider.SQLDatabaseTestDataSource;
import com.gatf.executor.dataprovider.TestDataHook;
import com.gatf.executor.dataprovider.TestDataProvider;
import com.gatf.executor.dataprovider.TestDataSource;
import com.gatf.executor.distributed.DistributedAcceptanceContext;
import com.gatf.executor.executor.PerformanceTestCaseExecutor;
import com.gatf.executor.executor.ScenarioTestCaseExecutor;
import com.gatf.executor.executor.SingleTestCaseExecutor;
import com.gatf.executor.finder.TestCaseFinder;
import com.gatf.executor.finder.XMLTestCaseFinder;
import com.gatf.executor.report.TestCaseReport;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

				}
			}
		}
	}
	
	private void handleProviders(List<GatfTestDataProvider> providerTestDataList)
	{
		for (GatfTestDataProvider provider : providerTestDataList) {
			Assert.assertNotNull("Provider name is not defined", provider.getProviderName());
			
			Assert.assertNotNull("Provider properties is not defined", provider.getProviderProperties());
			
			Assert.assertNull("Duplicate Provider name found", providerTestDataMap.get(provider.getProviderName()));
			
			TestDataSource dataSource = null;
			if(provider.getDataSourceName()!=null && provider.getProviderClass()==null)
			{	
				Assert.assertNotNull("Provider DataSource name is not defined", provider.getDataSourceName());
				dataSource = dataSourceMap.get(provider.getDataSourceName());
				
				Assert.assertNotNull("No DataSource found", dataSource);
				
				if(dataSource instanceof MongoDBTestDataSource)
				{
					Assert.assertNotNull("Provider query string is not defined", provider.getQueryStr());
					Assert.assertNotNull("Provider source properties not defined", provider.getSourceProperties());
				}
				
				if(dataSource instanceof SQLDatabaseTestDataSource)
				{
					Assert.assertNotNull("Provider query string is not defined", provider.getQueryStr());
				}
			}
			else if(provider.getDataSourceName()!=null && provider.getProviderClass()!=null)
			{
				Assert.assertNotNull("Specify either providerClass or dataSourceName", null);
			}
			else if(provider.getDataSourceName()==null && provider.getProviderClass()==null)
			{
				Assert.assertNotNull("Specify any one of providerClass or dataSourceName", null);
			}
			
			
			if(provider.isEnabled()==null) {
				provider.setEnabled(true);
			}
			
			if(!provider.isEnabled()) {
				logger.info("Provider " + provider.getProviderName() + " is Disabled...");
				continue;
			}
			
			if(provider.isLive()) {
				liveProviders.put(provider.getProviderName(), provider);
				logger.info("Provider " + provider.getProviderName() + " is a Live one...");
				continue;
			}
			
			List<Map<String, String>> testData = getProviderData(provider, null);
			if(gatfExecutorConfig.isSeleniumExecutor() && gatfExecutorConfig.getConcurrentUserSimulationNum()>1) {
			    for (int i = 0; i < gatfExecutorConfig.getConcurrentUserSimulationNum(); i++)
                {
			        if(FileTestDataProvider.class.getCanonicalName().equals(provider.getProviderClass().trim())) {
			            if(i==0) {
			                providerTestDataMap.put(provider.getProviderName()+(i+1), testData);
			                continue;
			            }
			            GatfTestDataProvider tp = new GatfTestDataProvider(provider);
	                    tp.setProviderName(provider.getProviderName()+(i+1));
	                    tp.getArgs()[0] = tp.getArgs()[0] + i;
	                    try {
	                        logger.info("Concurrent simulation scenario #"+(i+1)+" fetching provider with filePath "+tp.getArgs()[0]);
	                        List<Map<String, String>> testDataT = getProviderData(tp, null);
	                        if(testDataT==null) {
	                            testDataT = testData;
	                        }
	                        providerTestDataMap.put(tp.getProviderName(), testDataT);
	                    } catch (Throwable e) {
	                        logger.severe("Cannot find data provider for the concurrent simulation scenario #"+(i+1)+" with name " + tp.getProviderName());
	                        providerTestDataMap.put(tp.getProviderName(), testData);
	                    }
			        } else {
			            logger.severe("Concurrent simulation scenarios need file data providers");
			        }
                }
			} else {
			    providerTestDataMap.put(provider.getProviderName(), testData);
			}
		}
	}
	
	public List<Map<String, String>> getLiveProviderData(String provName, TestCase testCase)
	{
		GatfTestDataProvider provider = liveProviders.get(provName);
		return getProviderData(provider, testCase);
	}
	
	public List<Map<String, String>> getAnyProviderData(String provName, TestCase testCase)
	{
		if(liveProviders.containsKey(provName))
		{
			GatfTestDataProvider provider = liveProviders.get(provName);
			return getProviderData(provider, testCase);
		}
		else
		{
			return providerTestDataMap.get(provName);
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private List<Map<String, String>> getProviderData(GatfTestDataProvider provider, TestCase testCase) {
		
		TestDataSource dataSource = dataSourceMap.get(provider.getDataSourceName());
		
		TestDataProvider testDataProvider = null;
		List<Map<String, String>> testData = null;
		if(provider.getProviderClass()!=null) {
			if(FileTestDataProvider.class.getCanonicalName().equals(provider.getProviderClass().trim())) {
				testDataProvider = new FileTestDataProvider();

			} else if(InlineValueTestDataProvider.class.getCanonicalName().equals(provider.getProviderClass().trim())) {
