public class AcceptanceTestContext {

	private Logger logger = Logger.getLogger(AcceptanceTestContext.class.getSimpleName());
	
	public final static String
	    PROP_SOAP_ACTION_11 = "SOAPAction",
	    PROP_SOAP_ACTION_12 = "action=",
	    PROP_CONTENT_TYPE = "Content-Type",
	    PROP_CONTENT_LENGTH = "Content-Length",
	    PROP_AUTH = "Authorization",
	    PROP_PROXY_AUTH = "Proxy-Authorization",
	    PROP_PROXY_CONN = "Proxy-Connection",
	    PROP_KEEP_ALIVE = "Keep-Alive",
	    PROP_BASIC_AUTH = "Basic",
	    PROP_DELIMITER = "; ";
	
	public final static String
	    SOAP_1_1_NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/",
	    SOAP_1_2_NAMESPACE = "http://www.w3.org/2003/05/soap-envelope";
	
	public final static String
	    MIMETYPE_TEXT_HTML = "text/html",
	    MIMETYPE_TEXT_PLAIN = "text/plain",
	    MIMETYPE_TEXT_XML = "text/xml",
	    MIMETYPE_APPLICATION_XML = "application/soap+xml";
	
	public static final String GATF_SERVER_LOGS_API_FILE_NM = "gatf-logging-api-int.xml";
	
	private final Map<String, List<TestCase>> relatedTestCases = new HashMap<String, List<TestCase>>();
	
	private final Map<String, String> httpHeaders = new HashMap<String, String>();

	private ConcurrentHashMap<Integer, String> sessionIdentifiers = new ConcurrentHashMap<Integer, String>();
	
	private final Map<String, String> soapEndpoints = new HashMap<String, String>();
	
	private final Map<String, Document> soapMessages = new HashMap<String, Document>();
	
	private final Map<String, String> soapStrMessages = new HashMap<String, String>();
	
	private final Map<String, String> soapActions = new HashMap<String, String>();
	
	private final WorkflowContextHandler workflowContextHandler = new WorkflowContextHandler();
	
	private List<TestCase> serverLogsApiLst = new ArrayList<TestCase>();
	
	private GatfExecutorConfig gatfExecutorConfig;
	
	private ClassLoader projectClassLoader;
	
	private Map<String, Method> prePostTestCaseExecHooks = new HashMap<String, Method>();
	
	public static final UrlValidator URL_VALIDATOR = new UrlValidator(new String[]{"http","https"}, UrlValidator.ALLOW_LOCAL_URLS);
	
	public static boolean isValidUrl(String url) {
		if(!URL_VALIDATOR.isValid(url)) {
			if(url.startsWith("host.docker.internal")) {
				url = url.replace("host.docker.internal", "");
				if(url.length()>0 && url.charAt(0)==':') {
					url = url.substring(1);
				}
				if(url.indexOf("/")!=-1) {
					url = url.substring(0, url.indexOf("/"));
				}
				try {
					Integer.parseInt(url);
				} catch (Exception e) {
					return false;
				}
			}
		}
		return true;
	}
	
	public static void setCorsHeaders(Response response) {
		if(Boolean.FALSE.toString().equalsIgnoreCase(System.getenv("cors.enabled")) || Boolean.FALSE.toString().equalsIgnoreCase(System.getProperty("cors.enabled"))) {
		} else {
			response.setHeader("Access-Control-Allow-Origin", "*");
			response.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
			response.setHeader("Access-Control-Allow-Headers", "*");
		}
	}
	
	public ClassLoader getProjectClassLoader() {
		return projectClassLoader;
	}

	private final Map<String, List<Map<String, String>>> providerTestDataMap = new HashMap<String, List<Map<String,String>>>();
	private final Map<String, String> fileProviderStateMap = new HashMap<String, String>();
	
	public List<Map<String, String>> getProviderTestDataMap(String providerName) {
	    if(liveProviders.containsKey(providerName))
        {
            GatfTestDataProvider provider = liveProviders.get(providerName);
            return getProviderData(provider, null);
        }
	    else 
	    {
	        return providerTestDataMap.get(providerName);
	    }
	}
	
	public String getFileProviderHash(String name) {
	    if(!fileProviderStateMap.containsKey(name)) {
	        return fileProviderStateMap.get(name);
	    }
	    return null;
	}
	
	public void newProvider(String name) {
	    if(!providerTestDataMap.containsKey(name)) {
	        providerTestDataMap.put(name, new ArrayList<Map<String,String>>());
	    }
	}
	
	private final Map<String, TestDataSource> dataSourceMap = new HashMap<String, TestDataSource>();
	
	private final Map<String, TestDataSource> dataSourceMapForProfiling = new HashMap<String, TestDataSource>();
	
	private final Map<String, GatfTestDataSourceHook> dataSourceHooksMap = new HashMap<String, GatfTestDataSourceHook>();

	private final SingleTestCaseExecutor singleTestCaseExecutor = new SingleTestCaseExecutor();
	
	private final ScenarioTestCaseExecutor scenarioTestCaseExecutor = new ScenarioTestCaseExecutor();
	
	private final PerformanceTestCaseExecutor performanceTestCaseExecutor = new PerformanceTestCaseExecutor();
	
	private final Map<String, GatfTestDataProvider> liveProviders = new HashMap<String, GatfTestDataProvider>();

	public SingleTestCaseExecutor getSingleTestCaseExecutor() {
		return singleTestCaseExecutor;
	}

	public ScenarioTestCaseExecutor getScenarioTestCaseExecutor() {
		return scenarioTestCaseExecutor;
	}

	public PerformanceTestCaseExecutor getPerformanceTestCaseExecutor() {
		return performanceTestCaseExecutor;
	}

	public AcceptanceTestContext(){}
	
	public AcceptanceTestContext(GatfExecutorConfig gatfExecutorConfig, ClassLoader projectClassLoader)
	{
		this.gatfExecutorConfig = gatfExecutorConfig;
		this.projectClassLoader = projectClassLoader;
		getWorkflowContextHandler().init();
	}
	
	public AcceptanceTestContext(DistributedAcceptanceContext dContext, ClassLoader projectClassLoader)
	{
        this.projectClassLoader = projectClassLoader;
		this.gatfExecutorConfig = dContext.getConfig();
		this.httpHeaders.putAll(dContext.getHttpHeaders());
		this.soapEndpoints.putAll(dContext.getSoapEndpoints());
		this.soapActions.putAll(dContext.getSoapActions());
		try {
			if(dContext.getSoapMessages()!=null) {
				DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
				DocumentBuilder db = dbf.newDocumentBuilder();
				for (Map.Entry<String, String> soapMsg : dContext.getSoapMessages().entrySet()) {
					Document soapMessage = db.parse(new ByteArrayInputStream(soapMsg.getValue().getBytes()));
					this.soapMessages.put(soapMsg.getKey(), soapMessage);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			// TODO: handle exception
		}
		getWorkflowContextHandler().init();
	}
	
	public GatfExecutorConfig getGatfExecutorConfig() {
		return gatfExecutorConfig;
	}

	public void setGatfExecutorConfig(GatfExecutorConfig gatfExecutorConfig) {
		this.gatfExecutorConfig = gatfExecutorConfig;
	}

	public ConcurrentHashMap<Integer, String> getSessionIdentifiers() {
		return sessionIdentifiers;
	}

	public void setSessionIdentifiers(
			ConcurrentHashMap<Integer, String> sessionIdentifiers) {
		this.sessionIdentifiers = sessionIdentifiers;
	}

	public void setSessionIdentifier(String identifier, TestCase testCase) {
		Integer simulationNumber = testCase.getSimulationNumber();
		if(testCase.isServerApiAuth() || testCase.isServerApiTarget()) {
			simulationNumber = -1;
		} else if(testCase.isExternalApi()) {
			simulationNumber = -2;
		} else if(simulationNumber==null) {
			simulationNumber = 0;
		}
		sessionIdentifiers.put(simulationNumber, identifier);
	}
	
	public String getSessionIdentifier(TestCase testCase) {
		if(testCase.isServerApiAuth() || testCase.isServerApiTarget()) {
			return sessionIdentifiers.get(-1);
		}
		if(testCase.isExternalApi()) {
			return sessionIdentifiers.get(-2);
		}
		if(testCase.getSimulationNumber()!=null)
			return sessionIdentifiers.get(testCase.getSimulationNumber());
		
		return sessionIdentifiers.get(0);
	}
	
	public Map<String, String> getHttpHeaders() {
		return httpHeaders;
	}

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

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

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

	public WorkflowContextHandler getWorkflowContextHandler() {
		return workflowContextHandler;
	}
	
	@SuppressWarnings("rawtypes")
	public Class addTestCaseHooks(Method method) {
		Class claz = null;
		if(method!=null && Modifier.isStatic(method.getModifiers())) {
			
			Annotation preHook = method.getAnnotation(PreTestCaseExecutionHook.class);
            Annotation postHook = method.getAnnotation(PostTestCaseExecutionHook.class);
			
			if(preHook!=null)
			{
				PreTestCaseExecutionHook hook = (PreTestCaseExecutionHook)preHook;
				
				if(method.getParameterTypes().length!=1 || !method.getParameterTypes()[0].equals(TestCase.class))
				{
					logger.severe("PreTestCaseExecutionHook annotated methods should " +
							"confirm to the method signature - `public static void {methodName} (" +
							"TestCase testCase)`");
					return claz;
				}
				
				if(hook.value()!=null && hook.value().length>0)
				{
					for (String testCaseName : hook.value()) {
						if(testCaseName!=null && !testCaseName.trim().isEmpty())  {
							prePostTestCaseExecHooks.put("pre"+testCaseName, method);
						}
					}
				}
				else
				{
					prePostTestCaseExecHooks.put("preAll", method);
				}
				claz = method.getDeclaringClass();
			}
			if(postHook!=null)
			{
				PostTestCaseExecutionHook hook = (PostTestCaseExecutionHook)postHook;
				
				if(method.getParameterTypes().length!=1 || !method.getParameterTypes()[0].equals(TestCaseReport.class))
				{
					logger.severe("PostTestCaseExecutionHook annotated methods should " +
							"confirm to the method signature - `public static void {methodName} (" +
							"TestCaseReport testCaseReport)`");
					return claz;
				}
				
				if(hook.value()!=null && hook.value().length>0)
				{
					for (String testCaseName : hook.value()) {
						if(testCaseName!=null && !testCaseName.trim().isEmpty())  {
							prePostTestCaseExecHooks.put("post"+testCaseName, method);
						}
					}
				}
				else
				{
					prePostTestCaseExecHooks.put("postAll", method);
				}
				claz = method.getDeclaringClass();
			}
		}
		return claz;
	}
	
	public List<Method> getPrePostHook(TestCase testCase, boolean isPreHook) {
		List<Method> methods = new ArrayList<Method>();
		String hookKey = (isPreHook?"pre":"post") + testCase.getName();
		Method meth = prePostTestCaseExecHooks.get(hookKey);
		if(meth!=null)
			methods.add(meth);
		meth = prePostTestCaseExecHooks.get((isPreHook?"preAll":"postAll"));
		if(meth!=null)
			methods.add(meth);
		return methods;
	}
	
	public File getResourceFile(String filename) {
		File basePath = new File(gatfExecutorConfig.getTestCasesBasePath());
	    File testPath = new File(basePath, gatfExecutorConfig.getTestCaseDir());
	    File resource = new File(testPath, filename);
        if(!resource.exists()) {
            resource = new File(basePath, filename);
        }
		return resource;
	}
	
	public File getNewOutResourceFile(String filename) {
		File basePath = new File(gatfExecutorConfig.getOutFilesBasePath());
		File resource = new File(basePath, gatfExecutorConfig.getOutFilesDir());
		File file = new File(resource, filename);
		if(!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
			}
		}
		return file;
	}
	
	public File getOutDir() {
		File basePath = new File(gatfExecutorConfig.getOutFilesBasePath());
		File resource = new File(basePath, gatfExecutorConfig.getOutFilesDir());
		return resource;
	}
	
	public String getOutDirPath() {
		File basePath = new File(gatfExecutorConfig.getOutFilesBasePath());
		File resource = new File(basePath, gatfExecutorConfig.getOutFilesDir());
		return resource.getAbsolutePath();
	}
	
	public Map<String, List<TestCase>> getRelatedTestCases() {
		return relatedTestCases;
	}

	public static void removeFolder(File folder)
	{
		if(folder==null || !folder.exists())return;
		try {
			FileUtils.deleteDirectory(folder);
		} catch (IOException e) {
		}
	}
	
	public void validateAndInit(boolean flag) throws Exception
	{
		gatfExecutorConfig.validate();
		
		if(gatfExecutorConfig.getOutFilesBasePath()!=null)
		{
			File basePath = new File(gatfExecutorConfig.getOutFilesBasePath().trim());
			Assert.assertTrue("Invalid out files base path..", basePath.exists());
			gatfExecutorConfig.setOutFilesBasePath(basePath.getAbsolutePath());
		}
		else
		{
			File basePath = new File(System.getProperty("user.dir"));
			gatfExecutorConfig.setOutFilesBasePath(basePath.getAbsolutePath());
		}
		
		if(gatfExecutorConfig.getTestCasesBasePath()!=null)
		{
			File basePath = new File(gatfExecutorConfig.getTestCasesBasePath());
			Assert.assertTrue("Invalid test cases base path..", basePath.exists());
			gatfExecutorConfig.setTestCasesBasePath(basePath.getAbsolutePath());
		}
		else
		{
			File basePath = new File(System.getProperty("user.dir"));
			gatfExecutorConfig.setTestCasesBasePath(basePath.getAbsolutePath());
		}
		
		Assert.assertEquals("Testcase directory not found...", getResourceFile(gatfExecutorConfig.getTestCaseDir()).exists(), true);
		
		if(StringUtils.isNotBlank(gatfExecutorConfig.getBaseUrl()))
		{
			Assert.assertTrue("Base URL is not valid", AcceptanceTestContext.isValidUrl(gatfExecutorConfig.getBaseUrl()));
		}
		
		if(gatfExecutorConfig.getOutFilesDir()!=null && !gatfExecutorConfig.getOutFilesDir().trim().isEmpty())
		{
			try
			{
				if(gatfExecutorConfig.getOutFilesBasePath()!=null)
				{
					File basePath = new File(gatfExecutorConfig.getOutFilesBasePath());
					File resource = new File(basePath, gatfExecutorConfig.getOutFilesDir());
					if(flag)
					{
						removeFolder(resource);
						File nresource = new File(basePath, gatfExecutorConfig.getOutFilesDir());
						nresource.mkdirs();
						Assert.assertTrue("Out files directory could not be created...", nresource.exists());
					}
				}
				else
				{
					File resource = new File(System.getProperty("user.dir"));
					File file = new File(resource, gatfExecutorConfig.getOutFilesDir());
					if(flag)
					{
						removeFolder(file);
						File nresource = new File(resource, gatfExecutorConfig.getOutFilesDir());
						nresource.mkdir();
						Assert.assertTrue("Out files directory could not be created...", nresource.exists());
					}
				}
			} catch (Exception e) {
				gatfExecutorConfig.setOutFilesDir(null);
			}
			Assert.assertNotNull("Testcase out file directory not found...", gatfExecutorConfig.getOutFilesDir());
		}
		else
		{
			File resource = new File(System.getProperty("user.dir"));
			if(flag)
			{
				removeFolder(resource);
				File nresource = new File(System.getProperty("user.dir"), "out");
				nresource.mkdir();
			}
			gatfExecutorConfig.setOutFilesDir("out");
			gatfExecutorConfig.setOutFilesBasePath(System.getProperty("user.dir"));
		}
		
		if(flag)
		{
			initSoapContextAndHttpHeaders();
			
			initTestDataProviderAndGlobalVariables();
		}
		
		initServerLogsApis();
	}
	
	public void initServerLogsApis() throws Exception {
		File basePath = new File(gatfExecutorConfig.getTestCasesBasePath());
		File resource = new File(basePath, GATF_SERVER_LOGS_API_FILE_NM);
		if(resource.exists() && gatfExecutorConfig.isFetchFailureLogs()) {
			TestCaseFinder finder = new XMLTestCaseFinder();
			serverLogsApiLst.clear();
			serverLogsApiLst.addAll(finder.resolveTestCases(resource));
			for (TestCase testCase : serverLogsApiLst) {
				testCase.setSourcefileName(GATF_SERVER_LOGS_API_FILE_NM);
				if(testCase.getSimulationNumber()==null)
				{
					testCase.setSimulationNumber(0);
				}
				testCase.setExternalApi(true);
				testCase.validate(getHttpHeaders(), null);
			}
		}
	}

	private void initTestDataProviderAndGlobalVariables() throws Exception {
		GatfTestDataConfig gatfTestDataConfig = null;
		if(gatfExecutorConfig.getTestDataConfigFile()!=null) {
			File file = getResourceFile(gatfExecutorConfig.getTestDataConfigFile());
			Assert.assertNotNull("Testdata configuration file not found...", file);
			Assert.assertEquals("Testdata configuration file not found...", file.exists(), true);
			
			if(gatfExecutorConfig.getTestDataConfigFile().trim().endsWith(".xml")) {
				XStream xstream = new XStream(new DomDriver("UTF-8"));
		       
		        xstream.allowTypes(new Class[]{GatfTestDataConfig.class, GatfTestDataProvider.class});
				xstream.processAnnotations(new Class[]{GatfTestDataConfig.class, GatfTestDataProvider.class});
				xstream.alias("gatf-testdata-provider", GatfTestDataProvider.class);
				xstream.alias("args", String[].class);
				xstream.alias("arg", String.class);
				gatfTestDataConfig = (GatfTestDataConfig)xstream.fromXML(file);
			} else  {
				gatfTestDataConfig = WorkflowContextHandler.OM.readValue(file, GatfTestDataConfig.class);
			}
			gatfExecutorConfig.setGatfTestDataConfig(gatfTestDataConfig);
		} else {
			gatfTestDataConfig = gatfExecutorConfig.getGatfTestDataConfig();
		}
		
		handleTestDataSourcesAndHooks(gatfTestDataConfig);
	}

	public void handleTestDataSourcesAndHooks(GatfTestDataConfig gatfTestDataConfig) {
		if(gatfTestDataConfig!=null) {
			getWorkflowContextHandler().addGlobalVariables(gatfTestDataConfig.getGlobalVariables());
			
			if(gatfTestDataConfig.getDataSourceList()!=null)
			{
				handleDataSources(gatfTestDataConfig.getDataSourceList(), false);
			}
			
			if(gatfTestDataConfig.getDataSourceListForProfiling()!=null)
			{
				handleDataSources(gatfTestDataConfig.getDataSourceListForProfiling(), true);
			}
			
			if(gatfTestDataConfig.getDataSourceHooks()!=null)
			{
				handleHooks(gatfTestDataConfig.getDataSourceHooks());
			}
				
			if(gatfTestDataConfig.getProviderTestDataList()!=null)
			{
				handleProviders(gatfTestDataConfig.getProviderTestDataList());
			}
		}
	}

	public static Map<String, String> getHttpHeadersMap() throws Exception
	{
		Map<String, String> headers = new HashMap<String, String>();
		Field[] declaredFields = HttpHeaders.class.getDeclaredFields();
		for (Field field : declaredFields) {
			if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
					&& field.getType().equals(String.class)) {
				headers.put(field.get(null).toString().toLowerCase(), field.get(null).toString());
			}
		}
		return headers;
	}
	
	private void initSoapContextAndHttpHeaders() throws Exception
	{
		Field[] declaredFields = HttpHeaders.class.getDeclaredFields();
		for (Field field : declaredFields) {
			if (java.lang.reflect.Modifier.isStatic(field.getModifiers())
					&& field.getType().equals(String.class)) {
				httpHeaders.put(field.get(null).toString().toLowerCase(), field.get(null).toString());
			}
		}
		
		File file = null;
		if(gatfExecutorConfig.getWsdlLocFile()!=null && !gatfExecutorConfig.getWsdlLocFile().trim().isEmpty())
			file = getResourceFile(gatfExecutorConfig.getWsdlLocFile());
		
		if(file!=null)
		{
			Scanner s = new Scanner(file);
			s.useDelimiter("\n");
			List<String> list = new ArrayList<String>();
			while (s.hasNext()) {
				list.add(s.next().replace("\r", ""));
			}
			s.close();
	
			for (String wsdlLoc : list) {
				if(!wsdlLoc.trim().isEmpty())
				{
					String[] wsdlLocParts = wsdlLoc.split(",");
					logger.info("Started Parsing WSDL location - " + wsdlLocParts[1]);
					Wsdl wsdl = Wsdl.parse(wsdlLocParts[1]);
					for (QName bindingName : wsdl.getBindings()) {
						SoapBuilder builder = wsdl.getBuilder(bindingName);
						for (SoapOperation operation : builder.getOperations()) {
							String request = builder.buildInputMessage(operation);
							DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
							DocumentBuilder db = dbf.newDocumentBuilder();
							Document soapMessage = db.parse(new ByteArrayInputStream(request.getBytes()));
							
							if(gatfExecutorConfig.isDistributedLoadTests()) {
								soapStrMessages.put(wsdlLocParts[0]+operation.getOperationName(), request);
							}
							
							soapMessages.put(wsdlLocParts[0]+operation.getOperationName(), soapMessage);
							if(operation.getSoapAction()!=null) {
								soapActions.put(wsdlLocParts[0]+operation.getOperationName(), operation.getSoapAction());
							}
							logger.info("Adding message for SOAP operation - " + operation.getOperationName());
						}
						soapEndpoints.put(wsdlLocParts[0], builder.getServiceUrls().get(0));
						logger.info("Adding SOAP Service endpoint - " + builder.getServiceUrls().get(0));
					}
					logger.info("Done Parsing WSDL location - " + wsdlLocParts[1]);
				}
			}
		}
	}

	public void shutdown() {
		for (GatfTestDataSourceHook dataSourceHook : dataSourceHooksMap.values()) {
			TestDataSource dataSource = dataSourceMap.get(dataSourceHook.getDataSourceName());
			
			Assert.assertNotNull("No DataSource found", dataSource);
			
			if(dataSourceHook.isExecuteOnShutdown() && dataSourceHook.getQueryStrs()!=null) {
				for (String query : dataSourceHook.getQueryStrs()) {
					boolean flag = false;
					try {
						flag = dataSource.execute(query);
					} catch (Throwable e) {
					}
					if(!flag) {
						logger.severe("Shutdown DataSourceHook execution for " + dataSourceHook.getHookName()
								+ " failed, queryString = " + query);
					}
				}
			}
		}
		
		for (GatfTestDataSourceHook dataSourceHook : dataSourceHooksMap.values()) {
			TestDataSource dataSource = dataSourceMap.get(dataSourceHook.getDataSourceName());
			
			Assert.assertNotNull("No DataSource found", dataSource);
			
			dataSource.destroy();
		}
	}
	
	public void executeDataSourceHook(String hookName) {
		GatfTestDataSourceHook dataSourceHook = dataSourceHooksMap.get(hookName);
		Assert.assertNotNull("No DataSourceHook found", dataSourceHook);
		
		TestDataSource dataSource = dataSourceMap.get(dataSourceHook.getDataSourceName());
		Assert.assertNotNull("No DataSource found", dataSource);
		
		for (String query : dataSourceHook.getQueryStrs()) {
			boolean flag = dataSource.execute(query);
			if(!flag) {
				Assert.assertNotNull("DataSourceHook execution for " + dataSourceHook.getHookName()
						+ " failed...", null);
			}
		}
	}
	
	@SuppressWarnings("rawtypes")
	private void handleDataSources(List<GatfTestDataSource> dataSourceList, boolean forProfiling)
	{
		for (GatfTestDataSource dataSource : dataSourceList) {
			Assert.assertNotNull("DataSource name is not defined", dataSource.getDataSourceName());
			Assert.assertNotNull("DataSource class is not defined", dataSource.getDataSourceClass());
			Assert.assertNotNull("DataSource args not defined", dataSource.getArgs());
			Assert.assertTrue("DataSource args empty", dataSource.getArgs().length>0);
			Assert.assertNull("Duplicate DataSource name found", dataSourceMap.get(dataSource.getDataSourceName()));
			
			TestDataSource testDataSource = null;
			if(SQLDatabaseTestDataSource.class.getCanonicalName().equals(dataSource.getDataSourceClass())) {
				testDataSource = new SQLDatabaseTestDataSource();
			} else if(MongoDBTestDataSource.class.getCanonicalName().equals(dataSource.getDataSourceClass())) {
				testDataSource = new MongoDBTestDataSource();
			} else {
				try {
					Class claz = loadCustomClass(dataSource.getDataSourceClass());
					Class[] classes = claz.getInterfaces();
					boolean validProvider = false;
					if(classes!=null) {
						for (Class class1 : classes) {
							if(class1.equals(TestDataSource.class)) {
								validProvider = true;
								break;
							}
						}
					}
					Assert.assertTrue("DataSource class should extend the TestDataSource class", validProvider);
					@SuppressWarnings("unchecked")
					Object providerInstance = claz.getDeclaredConstructor().newInstance();
					testDataSource = (TestDataSource)providerInstance;
				} catch (Throwable e) {
					throw new AssertionError(e);
				}
			}
			
			testDataSource.setClassName(dataSource.getDataSourceClass());
			testDataSource.setArgs(dataSource.getArgs());
			testDataSource.setContext(this);
			testDataSource.setDataSourceName(dataSource.getDataSourceName());
			if(dataSource.getPoolSize()>1)
			{
				testDataSource.setPoolSize(dataSource.getPoolSize());
			}
			
			if(forProfiling)
			{
				testDataSource.setPoolSize(1);
				testDataSource.init();
				dataSourceMapForProfiling.put(dataSource.getDataSourceName(), testDataSource);
			}
			else
			{
				testDataSource.init();
				dataSourceMap.put(dataSource.getDataSourceName(), testDataSource);
			}
		}
	}
	
	public Map<String, TestDataSource> getDataSourceMapForProfiling()
	{
		return dataSourceMapForProfiling;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void handleHooks(List<GatfTestDataSourceHook> dataSourceHooks)
	{
		for (GatfTestDataSourceHook dataSourceHook : dataSourceHooks) {
			Assert.assertNotNull("DataSourceHook name is not defined", dataSourceHook.getHookName());
			Assert.assertNotNull("DataSourceHook query string is not defined", dataSourceHook.getQueryStrs());
			Assert.assertNull("Duplicate DataSourceHook name found", dataSourceHooksMap.get(dataSourceHook.getHookName()));
			
			TestDataSource dataSource = null;
			if(dataSourceHook.getDataSourceName()!=null && dataSourceHook.getHookClass()==null)
			{
				dataSource = dataSourceMap.get(dataSourceHook.getDataSourceName());
				Assert.assertNotNull("No DataSource found", dataSource);
			}
			else if(dataSourceHook.getDataSourceName()!=null && dataSourceHook.getHookClass()!=null)
			{
				Assert.assertNotNull("Specify either hookClass or dataSourceName", null);
			}
			else if(dataSourceHook.getDataSourceName()==null && dataSourceHook.getHookClass()==null)
			{
				Assert.assertNotNull("Specify any one of hookClass or dataSourceName", null);
			}
			
			dataSourceHooksMap.put(dataSourceHook.getHookName(), dataSourceHook);
			
			TestDataHook testDataHook = null;
			if(dataSourceHook.getHookClass()!=null) {
				try {
					Class claz = loadCustomClass(dataSourceHook.getHookClass());
					Class[] classes = claz.getInterfaces();
					boolean validProvider = false;
					if(classes!=null) {
						for (Class class1 : classes) {
							if(class1.equals(TestDataProvider.class)) {
								validProvider = true;
								break;
							}
						}
					}
					Assert.assertTrue("Hook class should implement the TestDataHook class", validProvider);
					Object providerInstance = claz.getDeclaredConstructor().newInstance();
					testDataHook = (TestDataHook)providerInstance;
				} catch (Throwable e) {
					throw new AssertionError(e);
				}
			} else {
				testDataHook = dataSource;
			}
			
			if(dataSourceHook.isExecuteOnStart()) {
				for (String query : dataSourceHook.getQueryStrs()) {
					boolean flag = false;
					try {
						flag = testDataHook.execute(query);
					} catch (Throwable e) {
					}
					if(!flag) {
						logger.severe("Startup DataSourceHook execution for " + dataSourceHook.getHookName()
								+ " failed, queryString = " + query);
					}
				}
			}
		}
	}
	
	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())) {
				testDataProvider = new InlineValueTestDataProvider();
			} else if(RandomValueTestDataProvider.class.getCanonicalName().equals(provider.getProviderClass().trim())) {
				testDataProvider = new RandomValueTestDataProvider();
			} else {
				try {
					Class claz = loadCustomClass(provider.getProviderClass());
					Class[] classes = claz.getInterfaces();
					boolean validProvider = false;
					if(classes!=null) {
						for (Class class1 : classes) {
							if(class1.equals(TestDataProvider.class)) {
								validProvider = true;
								break;
							}
						}
					}
					Assert.assertTrue("Provider class should implement the TestDataProvider class", validProvider);
					Object providerInstance = claz.getDeclaredConstructor().newInstance();
					testDataProvider = (TestDataProvider)providerInstance;
				} catch (Throwable e) {
					throw new AssertionError(e);
				}
			}
		} else {
			testDataProvider = dataSource;
		}
		
		//Live provider queries can have templatized parameter names
		if(provider.isLive() && provider.getQueryStr()!=null)
		{
			String oQs = provider.getQueryStr();
			provider = new GatfTestDataProvider(provider);
			if(testCase!=null)
			{
				provider.setQueryStr(getWorkflowContextHandler().evaluateTemplate(testCase, provider.getQueryStr(), this));
				if(provider.getQueryStr()==null || provider.getQueryStr().isEmpty()) {
					provider.setQueryStr(oQs);
				}
			}
		}
		
		testData = testDataProvider.provide(provider, this);
		if(testDataProvider instanceof FileTestDataProvider) {
			fileProviderStateMap.put(provider.getProviderName(), ((FileTestDataProvider)testDataProvider).getHash());
		}
		return testData;
	}

	public DistributedAcceptanceContext getDistributedContext(String node, List<Object[]> selTestdata)
	{
		DistributedAcceptanceContext distributedTestContext = new DistributedAcceptanceContext();
		distributedTestContext.setConfig(gatfExecutorConfig);
		distributedTestContext.setHttpHeaders(httpHeaders);
		distributedTestContext.setSoapActions(soapActions);
		distributedTestContext.setSoapEndpoints(soapEndpoints);
		distributedTestContext.setSoapMessages(soapStrMessages);
		distributedTestContext.setNode(node);
		distributedTestContext.setSelTestdata(selTestdata);
		
		return distributedTestContext;
	}
	
	@SuppressWarnings("rawtypes")
	private Class loadCustomClass(String className) throws ClassNotFoundException
	{
		return getProjectClassLoader().loadClass(className);
	}
	
	public List<TestCase> getServerLogsApiLst() {
		return serverLogsApiLst;
	}
	
	public TestCase getServerLogApi(boolean isAuth)
	{
		if(serverLogsApiLst.size()>0)
		{
			for (TestCase tc : serverLogsApiLst) {
				if(isAuth && gatfExecutorConfig.isServerLogsApiAuthEnabled() && "authapi".equals(tc.getName()))
				{
					tc.setServerApiAuth(true);
					tc.setExternalApi(true);
					return tc;
				}
				else if(!isAuth && "targetapi".equals(tc.getName()))
				{
					tc.setServerApiTarget(true);
					tc.setSecure(gatfExecutorConfig.isServerLogsApiAuthEnabled());
					tc.setExternalApi(true);
					return tc;
				}
			}
		}
		return null;
	}
}
 @XStreamAlias("TestCase")
@JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY, isGetterVisibility=Visibility.NONE)
@JsonInclude(value = Include.NON_NULL)
public class TestCase implements Serializable {

	private static final long serialVersionUID = 1L;
	
	private static final Logger logger = Logger.getLogger(TestCase.class);
	
	private static final String PRE = UUID.randomUUID().toString() + "__";
	
	private static final Pattern quotedString = Pattern.compile("((?<![\\\\])['\"])((?:.(?!(?<![\\\\])\\1))*.?)\\1");
	
	public static final String[] CDATA_NODES = new String[]{"content","expectedResContent"};
	
	private String baseUrl;
	
	private String url;
	
	private String name;
	
	@XStreamAsAttribute
	private String method;
	
	private String description;
	
	private String content;
	
	private String contentFile;
	
	private Map<String, String> headers = new HashMap<String, String>();
	
	@XStreamAsAttribute
	private String exQueryPart;
	
	@XStreamAsAttribute
	private int expectedResCode;
	
	@XStreamAsAttribute
	private String expectedResContentType;
	
	private String expectedResContent;
	
	@XStreamAsAttribute
	private boolean skipTest;
	
	@XStreamAsAttribute
	private boolean detailedLog;
	
	@XStreamAsAttribute
	private boolean secure;
	
	private List<String> expectedNodes = new ArrayList<String>();
	
	@XStreamAsAttribute
	private boolean soapBase;
	
	private String wsdlKey;
	
	private String operationName;
	
	private Map<String, String> soapParameterValues = new HashMap<String, String>();
	
	private Map<String, String> workflowContextParameterMap = new HashMap<String, String>();
	
	@XStreamAsAttribute
	private int sequence = 0;
	
	private List<String> multipartContent = new ArrayList<String>();
	
	private String outFileName;
	
	private List<Map<String, String>> repeatScenarios = new ArrayList<Map<String,String>>();
	
	@XStreamOmitField
	@JsonIgnore
	private List<Map<String, String>> repeatScenariosOrig = new ArrayList<Map<String,String>>();
	
	private String repeatScenarioProviderName;
	
	@XStreamOmitField
	@JsonIgnore
	private String sourcefileName;
	
	@XStreamOmitField
	@JsonIgnore
	private String aurl;
	
	@XStreamOmitField
	@JsonIgnore
	private String aexQueryPart;
	
	@XStreamOmitField
	@JsonIgnore
	private String acontent;
	
	@XStreamOmitField
	@JsonIgnore
	private List<String> aexpectedNodes = new ArrayList<String>();
	
	@XStreamAsAttribute
	private Integer numberOfExecutions = 1;
	
	@XStreamAsAttribute
	private boolean repeatScenariosConcurrentExecution;
	
	@XStreamAsAttribute	
	private boolean stopOnFirstFailureForPerfTest;

	@XStreamOmitField
	@JsonIgnore
	private volatile boolean failed;
	
	@XStreamOmitField
	private Integer simulationNumber;
	
	@XStreamAsAttribute
	private Long preWaitMs;
	
	@XStreamAsAttribute
	private Long postWaitMs;
	
	@XStreamAsAttribute
	private Boolean reportResponseContent = true;
	
	private String preExecutionDataSourceHookName;
	
	private String postExecutionDataSourceHookName;
	
	@XStreamAsAttribute
	private boolean abortOnInvalidStatusCode;
	
	@XStreamAsAttribute
	private boolean abortOnInvalidContentType;
	
	private String relatedTestName;
	
	@XStreamOmitField
	@JsonIgnore
	//Parameters carried over from related testcases
	private Map<String, String> carriedOverVariables;
	
	@XStreamOmitField
	@JsonIgnore
	private String identifierPrefix =  "Run";
	
	private String executeOnCondition;
	
	private List<String> logicalValidations;
	
	@XStreamAsAttribute
	private boolean disablePreHooks = false;
	
	@XStreamAsAttribute
	private boolean disablePostHooks = false;
	
	@XStreamOmitField
	@JsonIgnore
	private Map<String, String> currentScenarioVariables;
	
	@XStreamOmitField
	@JsonIgnore
	private boolean isServerApiAuth = false;
	
	@XStreamOmitField
	@JsonIgnore
	private boolean isServerApiTarget = false;
	
	@XStreamOmitField
	@JsonIgnore
	private boolean isExternalApi = false;
	
	private PerformanceConfig perfConfig;
	
	public String getBaseUrl() {
		return baseUrl;
	}

	public void setBaseUrl(String baseUrl) {
		this.baseUrl = baseUrl;
	}

	public String getUrl() {
		return url;
	}

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

	public String getMethod() {
		return method;
	}

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

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getContentFile() {
		return contentFile;
	}

	public void setContentFile(String contentFile) {
		this.contentFile = contentFile;
	}

	public Map<String, String> getHeaders() {
		return headers;
	}

	public void setHeaders(Map<String, String> headers) {
		this.headers = headers;
	}

	public int getExpectedResCode() {
		return expectedResCode;
	}

	public void setExpectedResCode(int expectedResCode) {
		this.expectedResCode = expectedResCode;
	}

	public String getExpectedResContent() {
		return expectedResContent;
	}

	public void setExpectedResContent(String expectedResContent) {
		this.expectedResContent = expectedResContent;
	}

	public boolean isSkipTest() {
		return skipTest;
	}

	public void setSkipTest(boolean skipTest) {
		this.skipTest = skipTest;
	}

	public List<String> getExpectedNodes() {
		return expectedNodes;
	}

	public void setExpectedNodes(List<String> expectedNodes) {
		this.expectedNodes = expectedNodes;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getExpectedResContentType() {
		return expectedResContentType;
	}

	public void setExpectedResContentType(String expectedResContentType) {
		this.expectedResContentType = expectedResContentType;
	}

	public String getExQueryPart() {
		return exQueryPart;
	}

	public void setExQueryPart(String exQueryPart) {
		this.exQueryPart = exQueryPart;
	}

	public boolean isDetailedLog() {
		return detailedLog;
	}

	public void setDetailedLog(boolean detailedLog) {
		this.detailedLog = detailedLog;
	}

	public boolean isSecure() {
		return secure;
	}

	public void setSecure(boolean secure) {
		this.secure = secure;
	}

	public Map<String, String> getSoapParameterValues() {
		return soapParameterValues;
	}

	public void setSoapParameterValues(Map<String, String> soapParameterValues) {
		this.soapParameterValues = soapParameterValues;
	}
	
	public boolean isSoapBase() {
		return soapBase;
	}

	public void setSoapBase(boolean soapBase) {
		this.soapBase = soapBase;
	}
	
	public String getWsdlKey() {
		return wsdlKey;
	}

	public void setWsdlKey(String wsdlKey) {
		this.wsdlKey = wsdlKey;
	}

	public String getOperationName() {
		return operationName;
	}

	public void setOperationName(String operationName) {
		this.operationName = operationName;
	}

	public List<String> getMultipartContent() {
		return multipartContent;
	}

	public void setMultipartContent(List<String> multipartContent) {
		this.multipartContent = multipartContent;
	}
	
	public Map<String, String> getWorkflowContextParameterMap() {
		return workflowContextParameterMap;
	}

	public void setWorkflowContextParameterMap(
			Map<String, String> workflowContextParameterMap) {
		this.workflowContextParameterMap = workflowContextParameterMap;
	}

	public int getSequence() {
		return sequence;
	}

	public void setSequence(int sequence) {
		this.sequence = sequence;
	}

	public String getOutFileName() {
		return outFileName;
	}

	public void setOutFileName(String outFileName) {
		this.outFileName = outFileName;
	}

	public List<Map<String, String>> getRepeatScenarios() {
		return repeatScenarios;
	}

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

	public List<Map<String, String>> getRepeatScenariosOrig() {
		return repeatScenariosOrig;
	}

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

	public void setSourcefileName(String sourcefileName) {
		this.sourcefileName = sourcefileName;
	}

	public String getAurl() {
		return aurl;
	}

	public void setAurl(String aurl) {
		this.aurl = aurl;
	}

	public String getAexQueryPart() {
		return aexQueryPart;
	}

	public void setAexQueryPart(String aexQueryPart) {
		this.aexQueryPart = aexQueryPart;
	}

	public String getAcontent() {
		return acontent;
	}

	public void setAcontent(String acontent) {
		this.acontent = acontent;
	}

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

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

	public Integer getNumberOfExecutions() {
		return numberOfExecutions;
	}

	public void setNumberOfExecutions(Integer numberOfExecutions) {
		this.numberOfExecutions = numberOfExecutions;
	}
	
	public boolean isRepeatScenariosConcurrentExecution() {
		return repeatScenariosConcurrentExecution;
	}

	public void setRepeatScenariosConcurrentExecution(
			boolean repeatScenariosConcurrentExecution) {
		this.repeatScenariosConcurrentExecution = repeatScenariosConcurrentExecution;
	}

	public String getRepeatScenarioProviderName() {
		return repeatScenarioProviderName;
	}

	public void setRepeatScenarioProviderName(String repeatScenarioProviderName) {
		this.repeatScenarioProviderName = repeatScenarioProviderName;
	}

	public boolean isStopOnFirstFailureForPerfTest() {
		return stopOnFirstFailureForPerfTest;
	}

	public void setStopOnFirstFailureForPerfTest(
			boolean stopOnFirstFailureForPerfTest) {
		this.stopOnFirstFailureForPerfTest = stopOnFirstFailureForPerfTest;
	}

	public Integer getSimulationNumber() {
		return simulationNumber;
	}

	public void setSimulationNumber(Integer simulationNumber) {
		this.simulationNumber = simulationNumber;
	}

	public Long getPreWaitMs() {
		return preWaitMs;
	}

	public void setPreWaitMs(Long preWaitMs) {
		this.preWaitMs = preWaitMs;
	}

	public Long getPostWaitMs() {
		return postWaitMs;
	}

	public void setPostWaitMs(Long postWaitMs) {
		this.postWaitMs = postWaitMs;
	}

	public Boolean getReportResponseContent() {
		return reportResponseContent;
	}

	public void setReportResponseContent(Boolean reportResponseContent) {
		this.reportResponseContent = reportResponseContent;
	}

	public String getPreExecutionDataSourceHookName() {
		return preExecutionDataSourceHookName;
	}

	public void setPreExecutionDataSourceHookName(
			String preExecutionDataSourceHookName) {
		this.preExecutionDataSourceHookName = preExecutionDataSourceHookName;
	}

	public String getPostExecutionDataSourceHookName() {
		return postExecutionDataSourceHookName;
	}

	public void setPostExecutionDataSourceHookName(
			String postExecutionDataSourceHookName) {
		this.postExecutionDataSourceHookName = postExecutionDataSourceHookName;
	}

	public boolean isAbortOnInvalidStatusCode() {
		return abortOnInvalidStatusCode;
	}

	public void setAbortOnInvalidStatusCode(boolean abortOnInvalidStatusCode) {
		this.abortOnInvalidStatusCode = abortOnInvalidStatusCode;
	}

	public boolean isAbortOnInvalidContentType() {
		return abortOnInvalidContentType;
	}

	public void setAbortOnInvalidContentType(boolean abortOnInvalidContentType) {
		this.abortOnInvalidContentType = abortOnInvalidContentType;
	}

	public String getIdentifierPrefix() {
		if(identifierPrefix==null) {
			identifierPrefix = "Run";
		}
		return identifierPrefix;
	}

	public void setIdentifierPrefix(String identifierPrefix) {
		this.identifierPrefix = identifierPrefix;
	}

	public boolean isFailed() {
		return failed;
	}

	public void setFailed(boolean failed) {
		this.failed = failed;
	}

	public String getIdentifier() {
		if(getSimulationNumber()==null || getSimulationNumber()==0) {
			return sourcefileName;
		} else {
			return getIdentifierPrefix() + "-" + getSimulationNumber();
		}
	}
	
	public String getSourcefileName() {
		return sourcefileName;
	}
	
	public String getRelatedTestName() {
		return relatedTestName;
	}

	public void setRelatedTestName(String relatedTestName) {
		this.relatedTestName = relatedTestName;
	}

	public Map<String, String> getCarriedOverVariables() {
		return carriedOverVariables;
	}

	public void setCarriedOverVariables(Map<String, String> carriedOverVariables) {
		this.carriedOverVariables = carriedOverVariables;
	}

	public String getExecuteOnCondition() {
		return executeOnCondition;
	}

	public void setExecuteOnCondition(String executeOnCondition) {
		this.executeOnCondition = executeOnCondition;
	}

	public List<String> getLogicalValidations() {
		return logicalValidations;
	}

	public void setLogicalValidations(List<String> logicalValidations) {
		this.logicalValidations = logicalValidations;
	}

	public Map<String, String> getCurrentScenarioVariables() {
		return currentScenarioVariables;
	}

	public void setCurrentScenarioVariables(
			Map<String, String> currentScenarioVariables) {
		this.currentScenarioVariables = currentScenarioVariables;
	}

	public boolean isServerApiAuth() {
		return isServerApiAuth;
	}

	public void setServerApiAuth(boolean isServerApiAuth) {
		this.isServerApiAuth = isServerApiAuth;
	}

	public boolean isServerApiTarget() {
		return isServerApiTarget;
	}

	public void setServerApiTarget(boolean isServerApiTarget) {
		this.isServerApiTarget = isServerApiTarget;
	}

	public boolean isDisablePreHooks() {
		return disablePreHooks;
	}

	public void setDisablePreHooks(boolean disablePreHooks) {
		this.disablePreHooks = disablePreHooks;
	}

	public boolean isDisablePostHooks() {
		return disablePostHooks;
	}

	public void setDisablePostHooks(boolean disablePostHooks) {
		this.disablePostHooks = disablePostHooks;
	}

	public boolean isExternalApi() {
		return isExternalApi;
	}

	public void setExternalApi(boolean isExternalApi) {
		this.isExternalApi = isExternalApi;
	}

	public PerformanceConfig getPerfConfig() {
		return perfConfig;
	}

	public void setPerfConfig(PerformanceConfig perfConfig) {
		this.perfConfig = perfConfig;
	}

	@Override
	public String toString() {
		final int maxLen = 10;
		StringBuilder builder = new StringBuilder();
		builder.append("TestCase = [\n");
		builder.append("baseUrl=");
		builder.append(baseUrl);
		builder.append("\nurl=");
		builder.append(url);
		builder.append("\nname=");
		builder.append(name);
		builder.append("\nmethod=");
		builder.append(method);
		builder.append("\ndescription=");
		builder.append(description);
		builder.append("\ncontent=");
		builder.append(content);
		builder.append("\nheaders=");
		builder.append(headers != null ? toString(headers.entrySet(), maxLen)
				: null);
		builder.append("\nexQueryPart=");
		builder.append(exQueryPart);
		builder.append("\nexpectedResCode=");
		builder.append(expectedResCode);
		builder.append("\nexpectedResContentType=");
		builder.append(expectedResContentType);
		builder.append("\nexpectedResContent=");
		builder.append(expectedResContent);
		builder.append("\nskipTest=");
		builder.append(skipTest);
		builder.append("\ndetailedLog=");
		builder.append(detailedLog);
		builder.append("\nsecure=");
		builder.append(secure);
		builder.append("\nexpectedNodes=");
		builder.append(expectedNodes != null ? toString(expectedNodes, maxLen)
				: null);
		builder.append("\nsoapBase=");
		builder.append(soapBase);
		builder.append("\nwsdlKey=");
		builder.append(wsdlKey);
		builder.append("\noperationName=");
		builder.append(operationName);
		builder.append("\nsoapParameterValues=");
		builder.append(soapParameterValues != null ? toString(
				soapParameterValues.entrySet(), maxLen) : null);
		builder.append("\nworkflowContextParameterMap=");
		builder.append(workflowContextParameterMap != null ? toString(
				workflowContextParameterMap.entrySet(), maxLen) : null);
		builder.append("\nsequence=");
		builder.append(sequence);
		builder.append("\nfilesToUpload=");
		builder.append(multipartContent != null ? toString(multipartContent, maxLen)
				: null);
		builder.append("\noutFileName=");
		builder.append(outFileName);
		builder.append("\nrepeatScenarios=");
		builder.append(repeatScenarios != null ? toString(repeatScenarios,
				maxLen) : null);
		builder.append("\nrepeatScenarioProviderName=");
		builder.append(repeatScenarioProviderName);
		builder.append("\nsourcefileName=");
		builder.append(sourcefileName);
		builder.append("\naurl=");
		builder.append(aurl);
		builder.append("\naexQueryPart=");
		builder.append(aexQueryPart);
		builder.append("\nacontent=");
		builder.append(acontent);
		builder.append("\naexpectedNodes=");
		builder.append(aexpectedNodes != null ? toString(aexpectedNodes, maxLen)
				: null);
		builder.append("\nnumberOfExecutions=");
		builder.append(numberOfExecutions);
		builder.append("\nrepeatScenariosConcurrentExecution=");
		builder.append(repeatScenariosConcurrentExecution);
		builder.append("\nstopOnFirstFailureForPerfTest=");
		builder.append(stopOnFirstFailureForPerfTest);
		builder.append("\nfailed=");
		builder.append(failed);
		builder.append("\nsimulationNumber=");
		builder.append(simulationNumber);
		builder.append("\npreWaitMs=");
		builder.append(preWaitMs);
		builder.append("\npostWaitMs=");
		builder.append(postWaitMs);
		builder.append("\nreportResponseContent=");
		builder.append(reportResponseContent);
		builder.append("\npreExecutionDataSourceHookName=");
		builder.append(preExecutionDataSourceHookName);
		builder.append("\npostExecutionDataSourceHookName=");
		builder.append(postExecutionDataSourceHookName);
		builder.append("\nabortOnInvalidContentType=");
		builder.append(abortOnInvalidContentType);
		builder.append("\nabortOnInvalidStatusCode=");
		builder.append(abortOnInvalidStatusCode);
		builder.append("\nrelatedTestName=");
		builder.append(relatedTestName);
		builder.append("\nexecuteOnCondition=");
		builder.append(executeOnCondition);
		builder.append("\nlogicalValidations=");
		builder.append(logicalValidations != null ? toString(logicalValidations, maxLen)
				: null);
		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(", ");
			builder.append(iterator.next());
		}
		builder.append("]");
		return builder.toString();
	}

	public TestCase() {
	}
	
	public TestCase(String[] csvP) {
		boolean valid = false;
		if(csvP!=null) {
			List<String> csvParts = Arrays.asList(csvP);
			for (int i = 0; i < csvParts.size(); i++) {
				setProperties(csvParts, i);
			}
			valid = csvParts.size()>4;
		} else {
			valid = false;
		}
		if(!valid)
		{
			throw new RuntimeException("Invalid CSV data provided for creating TestCase");
		}
	}
	
	public TestCase(String[] csvP, Map<Integer, Integer> mappings) {
		boolean valid = false;
		if(csvP!=null) {
			List<String> csvParts = Arrays.asList(csvP);
			List<String> csvPartsN = new ArrayList<String>(40);
			for (int i = 0; i < 40; i++) {
				csvPartsN.add(null);
			}
			for (int i = 0; i < csvParts.size(); i++) {
				if(mappings.get(i)!=null)
				{
					csvPartsN.set(mappings.get(i), csvParts.get(i));
				}
			}
			for (int i = 0; i < csvPartsN.size(); i++) {
				setProperties(csvPartsN, i);
			}
			valid = csvPartsN.size()>4;
		} else {
			valid = false;
		}
		if(!valid)
		{
			throw new RuntimeException("Invalid CSV data provided for creating TestCase");
		}
	}
	
	public String toCSV() {
		StringBuilder build = new StringBuilder();
		build.append(getCsvValue(getUrl()));
		build.append(",");
		build.append(getCsvValue(getName()));
		build.append(",");
		build.append(getCsvValue(getMethod()));
		build.append(",");
		build.append(getCsvValue(getDescription()));
		build.append(",");
		build.append(getCsvValue(getContent()));
		build.append(",");
		if(getHeaders()!=null)
		{
			for (Map.Entry<String, String> entry : getHeaders().entrySet()) {
				build.append(getInternalCsvValue(entry.getKey()));
				build.append(":");
				build.append(getInternalCsvValue(entry.getValue()));
				build.append("|");
			}
		}
		build.append(",");
		build.append(getCsvValue(getExQueryPart()));
		build.append(",");
		build.append(getExpectedResCode());
		build.append(",");
		build.append(getCsvValue(getExpectedResContentType()));
		build.append(",");
		build.append(getCsvValue(getExpectedResContent()));
		build.append(",");
		if(getExpectedNodes()!=null)
		{
			for (String node : getExpectedNodes()) {
				build.append(getInternalCsvValue(node));
				build.append("|");
			}
		}
		build.append(",");
		build.append(isSkipTest());
		build.append(",");
		build.append(isDetailedLog());
		build.append(",");
		build.append(isSecure());
		build.append(",");
		if(getMultipartContent()!=null)
		{
			for (String node : getMultipartContent()) {
				build.append(getInternalCsvValue(node));
				build.append("|");
			}
		}
		build.append(",");
		build.append(isSoapBase());
		build.append(",");
		if(getSoapParameterValues()!=null)
		{
			for (Map.Entry<String, String> entry : getSoapParameterValues().entrySet()) {
				build.append(getInternalCsvValue(entry.getKey()));
				build.append(":");
				build.append(getInternalCsvValue(entry.getValue()));
				build.append("|");
			}
		}
		build.append(",");
		build.append(getCsvValue(getWsdlKey()));
		build.append(",");
		build.append(getCsvValue(getOperationName()));
		build.append(",");
		build.append(getSequence());
		build.append(",");
		if(getWorkflowContextParameterMap()!=null)
		{
			for (Map.Entry<String, String> entry : getWorkflowContextParameterMap().entrySet()) {
				build.append(getInternalCsvValue(entry.getKey()));
				build.append(":");
				build.append(getInternalCsvValue(entry.getValue()));
				build.append("|");
			}
		}
		build.append(",");
		build.append(getCsvValue(getOutFileName()));
		build.append(",");
		build.append(getCsvValue(getRepeatScenarioProviderName()));
		build.append(",");
		build.append(getNumberOfExecutions());
		build.append(",");
		build.append(isRepeatScenariosConcurrentExecution());
		build.append(",");
		build.append(isStopOnFirstFailureForPerfTest());
		build.append(",");
		build.append(getPreWaitMs());
		build.append(",");
		build.append(getPostWaitMs());
		build.append(",");
		build.append(getReportResponseContent());
		build.append(",");
		build.append(getPreExecutionDataSourceHookName());
		build.append(",");
		build.append(getPostExecutionDataSourceHookName());
		build.append(",");
		build.append(isAbortOnInvalidContentType());
		build.append(",");
		build.append(isAbortOnInvalidStatusCode());
		build.append(",");
		build.append(getRelatedTestName());
		build.append(",");
		build.append(getExecuteOnCondition());
		build.append(",");
		if(getLogicalValidations()!=null)
		{
			for (String node : getLogicalValidations()) {
				build.append(getInternalCsvValue(node));
				build.append(":");
			}
		}
		return build.toString();
	}
	
	private String getCsvValue(String value)
	{
		if(value==null) {
			return "";
		} else {
			return "\"" + value + "\"";
		}
	}
	
	private String getInternalCsvValue(String value)
	{
		if(value==null) {
			return "";
		} else {
			return value.replaceAll(",", "").replaceAll("\\|", "").replaceAll("\n", "");
		}
	}
	
	public void validate(Map<String, String> httpHeaders, String baseUrl) {
		
		if(StringUtils.isBlank(getName()))
			throw new RuntimeException("Blank Name specified for testcase");
		
		if(!isSoapBase())
		{
			if(StringUtils.isBlank(baseUrl) && StringUtils.isBlank(getBaseUrl()))
				throw new RuntimeException("No Base URL specified");
				
			if(StringUtils.isNotBlank(getBaseUrl()))
				Assert.assertTrue("Base URL is not valid", AcceptanceTestContext.isValidUrl(getBaseUrl()));
			
			String fullUrl = StringUtils.isBlank(getBaseUrl())?baseUrl:getBaseUrl();
			fullUrl = fullUrl.trim() + "/" + getUrl().trim();
			String parturl = fullUrl.substring(fullUrl.indexOf("://")+3);
			fullUrl = fullUrl.substring(0, fullUrl.indexOf("://")+3) + parturl.replace("//", "/");
			
			if(StringUtils.isBlank(getUrl()))
				throw new RuntimeException("Blank URL sepcified for testcase");
			
			if(StringUtils.isBlank(getMethod()))
				throw new RuntimeException("Blank HTTP Method specified for testcase");
			
			if(!getMethod().toUpperCase().equals(HttpMethod.GET) && !getMethod().toUpperCase().equals(HttpMethod.PUT)
					&& !getMethod().toUpperCase().equals(HttpMethod.POST) && !getMethod().toUpperCase().equals(HttpMethod.DELETE))
				throw new RuntimeException("Invalid HTTP Method specified for testcase");
			
			//if(("POST".equalsIgnoreCase(getMethod()) || "PUT".equalsIgnoreCase(getMethod())) && getHeaders().isEmpty())
			//	throw new RuntimeException("No Content-Type Header provided");
		}
		
		Map<String, String> nHeaders = new HashMap<String, String>();
		if(getHeaders()!=null)
		{
			for (Map.Entry<String, String> entry : getHeaders().entrySet()) {
				if(httpHeaders.containsKey(entry.getKey().toLowerCase())) {
					nHeaders.put(httpHeaders.get(entry.getKey().toLowerCase()), entry.getValue());
				} else {
					logger.info("Custom HTTP Header " + entry.getKey() + " specified for testcase");
					nHeaders.put(entry.getKey(), entry.getValue());
				}
			}
			setHeaders(nHeaders);
		}
		
		if(!isSoapBase() && (StringUtils.isNotBlank(getContent()) || StringUtils.isNotBlank(getContentFile())) && 
				(getHeaders()==null || !getHeaders().containsKey(HttpHeaders.CONTENT_TYPE)))
			throw new RuntimeException("No Content-Type Header provided");
		
		/*try {
			MediaType.valueOf(getHeaders().get(HttpHeaders.CONTENT_TYPE));
		} catch (Exception e) {
			throw new RuntimeException("Invalid Content-Type Header specified for testcase");
		}*/
		
		try {
			Status.fromStatusCode(getExpectedResCode());
		} catch (Exception e) {
			throw new RuntimeException("Invalid expected Status code specified for testcase");
		}
		
		if(StringUtils.isNotBlank(getExpectedResContentType()))
		{
			try {
				MediaType.valueOf(getExpectedResContentType());
				getHeaders().put(HttpHeaders.ACCEPT, getExpectedResContentType());
			} catch (Exception e) {
				//throw new RuntimeException("Invalid expected response content type specified for testcase");
			}
		}
		
		setExQueryPart(getExQueryPart()==null?"":getExQueryPart());
		
		if(!isSoapBase())
		{
			if(getUrl().indexOf("?")==-1 && !getExQueryPart().trim().isEmpty()) {
				setUrl(getUrl() + "?" + getExQueryPart());
			} else if(getUrl().indexOf("?")!=-1 && !getExQueryPart().trim().isEmpty()) {
				setUrl(getUrl() + "&" + getExQueryPart());
			}
		}
		
		setDescription(getDescription()==null?"":getDescription());
		
		if(isSoapBase()) {
			if(getWsdlKey()==null || getOperationName()==null) {
				throw new RuntimeException("Invalid wsdlkey/operationName specified for testcase");
			}
		}
		
		if(getWorkflowContextParameterMap()!=null)
		{
			for (Map.Entry<String, String> entry : getWorkflowContextParameterMap().entrySet()) {
				if(!entry.getKey().trim().matches("(^[a-zA-Z][a-zA-Z0-9_]*)|(^[_][a-zA-Z0-9_]+)")) {
					throw new RuntimeException("Invalid workflow variable name " + entry.getKey() + " specified for testcase...skipping...");
				}
			}
		}
		
		if(getRepeatScenarios()!=null && !getRepeatScenarios().isEmpty()) {
			Assert.assertNull("Only one of repeatScenarios or repeatScenarioProviderName allowed", getRepeatScenarioProviderName());
		}
	}

	public TestCase(TestCase other) {
		super();
		this.url = other.url;
		this.name = other.name;
		this.method = other.method;
		this.description = other.description;
		this.content = other.content;
		if(other.headers!=null)
		{
			this.headers = new HashMap<String, String>(other.headers);
		}
		this.exQueryPart = other.exQueryPart;
		this.expectedResCode = other.expectedResCode;
		this.expectedResContentType = other.expectedResContentType;
		this.expectedResContent = other.expectedResContent;
		this.skipTest = other.skipTest;
		this.detailedLog = other.detailedLog;
		this.secure = other.secure;
		if(other.expectedNodes!=null)
		{
			this.expectedNodes = new ArrayList<String>(other.expectedNodes);
		}
		this.soapBase = other.soapBase;
		this.wsdlKey = other.wsdlKey;
		this.operationName = other.operationName;
		if(other.soapParameterValues!=null)
		{
			this.soapParameterValues = new HashMap<String, String>(other.soapParameterValues);
		}
		if(other.workflowContextParameterMap!=null)
		{
			this.workflowContextParameterMap = new HashMap<String, String>(other.workflowContextParameterMap);
		}
		this.sequence = other.sequence;
		this.multipartContent = other.multipartContent;
		this.outFileName = other.outFileName;
		if(other.repeatScenarios!=null)
		{
			this.repeatScenarios = new ArrayList<Map<String,String>>(other.repeatScenarios);
		}
		if(other.repeatScenariosOrig!=null)
		{
			this.repeatScenariosOrig = new ArrayList<Map<String,String>>(other.repeatScenariosOrig);
		}
		this.sourcefileName = other.sourcefileName;
		this.aurl = other.aurl;
		this.aexQueryPart = other.aexQueryPart;
		this.acontent = other.acontent;
		this.aexpectedNodes = new ArrayList<String>();
		this.numberOfExecutions = other.numberOfExecutions;
		this.repeatScenariosConcurrentExecution = other.repeatScenariosConcurrentExecution;
		this.stopOnFirstFailureForPerfTest = other.stopOnFirstFailureForPerfTest;
		this.failed = other.failed;
		this.simulationNumber = other.simulationNumber;
		this.baseUrl = other.baseUrl;
		this.preWaitMs = other.preWaitMs;
		this.postWaitMs = other.postWaitMs;
		this.reportResponseContent = other.reportResponseContent;
		this.repeatScenarioProviderName = other.repeatScenarioProviderName;
		this.preExecutionDataSourceHookName = other.preExecutionDataSourceHookName;
		this.postExecutionDataSourceHookName = other.postExecutionDataSourceHookName;
		this.abortOnInvalidContentType = other.abortOnInvalidContentType;
		this.abortOnInvalidStatusCode = other.abortOnInvalidStatusCode;
		this.identifierPrefix = other.identifierPrefix;
		this.relatedTestName = other.relatedTestName;
		this.carriedOverVariables = other.carriedOverVariables;
		this.executeOnCondition = other.executeOnCondition;
		this.logicalValidations = other.logicalValidations;
		this.currentScenarioVariables = other.currentScenarioVariables;
		this.isServerApiAuth = other.isServerApiAuth;
		this.isServerApiTarget = other.isServerApiTarget;
		this.isExternalApi = other.isExternalApi;
		this.perfConfig = other.perfConfig;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (abortOnInvalidContentType ? 1231 : 1237);
		result = prime * result + (abortOnInvalidStatusCode ? 1231 : 1237);
		result = prime * result
				+ ((acontent == null) ? 0 : acontent.hashCode());
		result = prime * result
				+ ((aexQueryPart == null) ? 0 : aexQueryPart.hashCode());
		result = prime * result
				+ ((aexpectedNodes == null) ? 0 : aexpectedNodes.hashCode());
		result = prime * result + ((aurl == null) ? 0 : aurl.hashCode());
		result = prime * result + ((baseUrl == null) ? 0 : baseUrl.hashCode());
		result = prime
				* result
				+ ((carriedOverVariables == null) ? 0 : carriedOverVariables
						.hashCode());
		result = prime * result + ((content == null) ? 0 : content.hashCode());
		result = prime * result
				+ ((description == null) ? 0 : description.hashCode());
		result = prime * result + (detailedLog ? 1231 : 1237);
		result = prime * result
				+ ((exQueryPart == null) ? 0 : exQueryPart.hashCode());
		result = prime
				* result
				+ ((executeOnCondition == null) ? 0 : executeOnCondition
						.hashCode());
		result = prime * result
				+ ((expectedNodes == null) ? 0 : expectedNodes.hashCode());
		result = prime * result + expectedResCode;
		result = prime
				* result
				+ ((expectedResContent == null) ? 0 : expectedResContent
						.hashCode());
		result = prime
				* result
				+ ((expectedResContentType == null) ? 0
						: expectedResContentType.hashCode());
		result = prime * result
				+ ((multipartContent == null) ? 0 : multipartContent.hashCode());
		result = prime * result + ((headers == null) ? 0 : headers.hashCode());
		result = prime
				* result
				+ ((identifierPrefix == null) ? 0 : identifierPrefix.hashCode());
		result = prime
				* result
				+ ((logicalValidations == null) ? 0 : logicalValidations
						.hashCode());
		result = prime * result + ((method == null) ? 0 : method.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime
				* result
				+ ((numberOfExecutions == null) ? 0 : numberOfExecutions
						.hashCode());
		result = prime * result
				+ ((operationName == null) ? 0 : operationName.hashCode());
		result = prime * result
				+ ((outFileName == null) ? 0 : outFileName.hashCode());
		result = prime
				* result
				+ ((postExecutionDataSourceHookName == null) ? 0
						: postExecutionDataSourceHookName.hashCode());
		result = prime * result
				+ ((postWaitMs == null) ? 0 : postWaitMs.hashCode());
		result = prime
				* result
				+ ((preExecutionDataSourceHookName == null) ? 0
						: preExecutionDataSourceHookName.hashCode());
		result = prime * result
				+ ((preWaitMs == null) ? 0 : preWaitMs.hashCode());
		result = prime * result
				+ ((relatedTestName == null) ? 0 : relatedTestName.hashCode());
		result = prime
				* result
				+ ((repeatScenarioProviderName == null) ? 0
						: repeatScenarioProviderName.hashCode());
		result = prime * result
				+ ((repeatScenarios == null) ? 0 : repeatScenarios.hashCode());
		result = prime * result
				+ (repeatScenariosConcurrentExecution ? 1231 : 1237);
		result = prime
				* result
				+ ((repeatScenariosOrig == null) ? 0 : repeatScenariosOrig
						.hashCode());
		result = prime
				* result
				+ ((reportResponseContent == null) ? 0 : reportResponseContent
						.hashCode());
		result = prime * result + (secure ? 1231 : 1237);
		result = prime * result + sequence;
		result = prime
				* result
				+ ((simulationNumber == null) ? 0 : simulationNumber.hashCode());
		result = prime * result + (skipTest ? 1231 : 1237);
		result = prime * result + (soapBase ? 1231 : 1237);
		result = prime
				* result
				+ ((soapParameterValues == null) ? 0 : soapParameterValues
						.hashCode());
		result = prime * result
				+ ((sourcefileName == null) ? 0 : sourcefileName.hashCode());
		result = prime * result + (stopOnFirstFailureForPerfTest ? 1231 : 1237);
		result = prime * result + ((url == null) ? 0 : url.hashCode());
		result = prime
				* result
				+ ((workflowContextParameterMap == null) ? 0
						: workflowContextParameterMap.hashCode());
		result = prime * result + ((wsdlKey == null) ? 0 : wsdlKey.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		TestCase other = (TestCase) obj;
		if (abortOnInvalidContentType != other.abortOnInvalidContentType)
			return false;
		if (abortOnInvalidStatusCode != other.abortOnInvalidStatusCode)
			return false;
		if (acontent == null) {
			if (other.acontent != null)
				return false;
		} else if (!acontent.equals(other.acontent))
			return false;
		if (aexQueryPart == null) {
			if (other.aexQueryPart != null)
				return false;
		} else if (!aexQueryPart.equals(other.aexQueryPart))
			return false;
		if (aexpectedNodes == null) {
			if (other.aexpectedNodes != null)
				return false;
		} else if (!aexpectedNodes.equals(other.aexpectedNodes))
			return false;
		if (aurl == null) {
			if (other.aurl != null)
				return false;
		} else if (!aurl.equals(other.aurl))
			return false;
		if (baseUrl == null) {
			if (other.baseUrl != null)
				return false;
		} else if (!baseUrl.equals(other.baseUrl))
			return false;
		if (carriedOverVariables == null) {
			if (other.carriedOverVariables != null)
				return false;
		} else if (!carriedOverVariables.equals(other.carriedOverVariables))
			return false;
		if (content == null) {
			if (other.content != null)
				return false;
		} else if (!content.equals(other.content))
			return false;
		if (description == null) {
			if (other.description != null)
				return false;
		} else if (!description.equals(other.description))
			return false;
		if (detailedLog != other.detailedLog)
			return false;
		if (exQueryPart == null) {
			if (other.exQueryPart != null)
				return false;
		} else if (!exQueryPart.equals(other.exQueryPart))
			return false;
		if (executeOnCondition == null) {
			if (other.executeOnCondition != null)
				return false;
		} else if (!executeOnCondition.equals(other.executeOnCondition))
			return false;
		if (expectedNodes == null) {
			if (other.expectedNodes != null)
				return false;
		} else if (!expectedNodes.equals(other.expectedNodes))
			return false;
		if (expectedResCode != other.expectedResCode)
			return false;
		if (expectedResContent == null) {
			if (other.expectedResContent != null)
				return false;
		} else if (!expectedResContent.equals(other.expectedResContent))
			return false;
		if (expectedResContentType == null) {
			if (other.expectedResContentType != null)
				return false;
		} else if (!expectedResContentType.equals(other.expectedResContentType))
			return false;
		if (multipartContent == null) {
			if (other.multipartContent != null)
				return false;
		} else if (!multipartContent.equals(other.multipartContent))
			return false;
		if (headers == null) {
			if (other.headers != null)
				return false;
		} else if (!headers.equals(other.headers))
			return false;
		if (identifierPrefix == null) {
			if (other.identifierPrefix != null)
				return false;
		} else if (!identifierPrefix.equals(other.identifierPrefix))
			return false;
		if (logicalValidations == null) {
			if (other.logicalValidations != null)
				return false;
		} else if (!logicalValidations.equals(other.logicalValidations))
			return false;
		if (method == null) {
			if (other.method != null)
				return false;
		} else if (!method.equals(other.method))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (numberOfExecutions == null) {
			if (other.numberOfExecutions != null)
				return false;
		} else if (!numberOfExecutions.equals(other.numberOfExecutions))
			return false;
		if (operationName == null) {
			if (other.operationName != null)
				return false;
		} else if (!operationName.equals(other.operationName))
			return false;
		if (outFileName == null) {
			if (other.outFileName != null)
				return false;
		} else if (!outFileName.equals(other.outFileName))
			return false;
		if (postExecutionDataSourceHookName == null) {
			if (other.postExecutionDataSourceHookName != null)
				return false;
		} else if (!postExecutionDataSourceHookName
				.equals(other.postExecutionDataSourceHookName))
			return false;
		if (postWaitMs == null) {
			if (other.postWaitMs != null)
				return false;
		} else if (!postWaitMs.equals(other.postWaitMs))
			return false;
		if (preExecutionDataSourceHookName == null) {
			if (other.preExecutionDataSourceHookName != null)
				return false;
		} else if (!preExecutionDataSourceHookName
				.equals(other.preExecutionDataSourceHookName))
			return false;
		if (preWaitMs == null) {
			if (other.preWaitMs != null)
				return false;
		} else if (!preWaitMs.equals(other.preWaitMs))
			return false;
		if (relatedTestName == null) {
			if (other.relatedTestName != null)
				return false;
		} else if (!relatedTestName.equals(other.relatedTestName))
			return false;
		if (repeatScenarioProviderName == null) {
			if (other.repeatScenarioProviderName != null)
				return false;
		} else if (!repeatScenarioProviderName
				.equals(other.repeatScenarioProviderName))
			return false;
		if (repeatScenarios == null) {
			if (other.repeatScenarios != null)
				return false;
		} else if (!repeatScenarios.equals(other.repeatScenarios))
			return false;
		if (repeatScenariosConcurrentExecution != other.repeatScenariosConcurrentExecution)
			return false;
		if (repeatScenariosOrig == null) {
			if (other.repeatScenariosOrig != null)
				return false;
		} else if (!repeatScenariosOrig.equals(other.repeatScenariosOrig))
			return false;
		if (reportResponseContent == null) {
			if (other.reportResponseContent != null)
				return false;
		} else if (!reportResponseContent.equals(other.reportResponseContent))
			return false;
		if (secure != other.secure)
			return false;
		if (sequence != other.sequence)
			return false;
		if (simulationNumber == null) {
			if (other.simulationNumber != null)
				return false;
		} else if (!simulationNumber.equals(other.simulationNumber))
			return false;
		if (skipTest != other.skipTest)
			return false;
		if (soapBase != other.soapBase)
			return false;
		if (soapParameterValues == null) {
			if (other.soapParameterValues != null)
				return false;
		} else if (!soapParameterValues.equals(other.soapParameterValues))
			return false;
		if (sourcefileName == null) {
			if (other.sourcefileName != null)
				return false;
		} else if (!sourcefileName.equals(other.sourcefileName))
			return false;
		if (stopOnFirstFailureForPerfTest != other.stopOnFirstFailureForPerfTest)
			return false;
		if (url == null) {
			if (other.url != null)
				return false;
		} else if (!url.equals(other.url))
			return false;
		if (workflowContextParameterMap == null) {
			if (other.workflowContextParameterMap != null)
				return false;
		} else if (!workflowContextParameterMap
				.equals(other.workflowContextParameterMap))
			return false;
		if (wsdlKey == null) {
			if (other.wsdlKey != null)
				return false;
		} else if (!wsdlKey.equals(other.wsdlKey))
			return false;
		return true;
	}
	
	protected static List<String> getAndSanitizeParts(String csvLine) {
		
		Matcher match = quotedString.matcher(csvLine);
		int i = 0;
		List<String> csvParts = new ArrayList<String>();
		Map<String, String> params = new HashMap<String, String>();
		while(match.find())
		{
			csvLine = csvLine.replace(match.group(0), PRE+i);
			params.put(PRE+i, match.group(2));
			i++;
		}
		String[] parts = csvLine.split(",");
		for (i=0;i<parts.length;i++) {
			String part = parts[i];
			if(params.containsKey(part)) {
				part = params.get(part);
			}
			csvParts.add(part);
		}
		return csvParts;
	}
	
	private void setProperties(List<String> csvParts, int index)
	{
		if(index==0 && csvParts.size()>0 && csvParts.get(0)!=null)
			setUrl(csvParts.get(0));
		if(index==1 && csvParts.size()>1 && csvParts.get(1)!=null)
			setName(csvParts.get(1));
		if(index==2 && csvParts.size()>2 && csvParts.get(2)!=null)
			setMethod(csvParts.get(2));
		if(index==3 && csvParts.size()>3 && csvParts.get(3)!=null)
			setDescription(csvParts.get(3));
		if(index==4 && csvParts.size()>4 && StringUtils.isNotBlank(csvParts.get(4)))
			setContent(StringEscapeUtils.unescapeJava(csvParts.get(4)));
		else if(index==4 && csvParts.size()>4)
			setContent(null);
		if(index==5 && csvParts.size()>5 && csvParts.get(5)!=null)
		{	
			setHeaders(new HashMap<String, String>());
			String[] headers = csvParts.get(5).split("\\|");
			for (String header : headers) {
				String[] kv = header.split(":");
				if(kv.length!=2) {
					logger.error("Invalid Header specified for testcase - " + header);
				}
				if(!kv[0].isEmpty() && !kv[1].isEmpty())
				{
					getHeaders().put(kv[0], kv[1]);
				}
			}
		}
		if(index==6 && csvParts.size()>6 && csvParts.get(6)!=null)
			setExQueryPart(csvParts.get(6));
		if(index==7 && csvParts.size()>7 && csvParts.get(7)!=null)
			setExpectedResCode(Integer.parseInt(csvParts.get(7)));
		if(index==8 && csvParts.size()>8 && csvParts.get(8)!=null)
			setExpectedResContentType(csvParts.get(8));
		if(index==9 && csvParts.size()>9 && StringUtils.isNotBlank(csvParts.get(9)))
			setExpectedResContent(StringEscapeUtils.unescapeJava(csvParts.get(9)));
		else if(index==9 && csvParts.size()>9)
			setExpectedResContent(null);
		if(index==10 && csvParts.size()>10 && csvParts.get(10)!=null)
		{
			setExpectedNodes(new ArrayList<String>());
			String[] nodes = csvParts.get(10).split("\\|");
			for (String node : nodes) {
				if(!node.isEmpty())
				{
					getExpectedNodes().add(node);
				}
			}
		}
		if(index==11 && csvParts.size()>11 && csvParts.get(11)!=null)
			setSkipTest(getBooleanValue(csvParts.get(11)));
		if(index==12 && csvParts.size()>12 && csvParts.get(12)!=null)
			setDetailedLog(getBooleanValue(csvParts.get(12)));
		if(index==13 && csvParts.size()>13 && csvParts.get(13)!=null)
			setSecure(getBooleanValue(csvParts.get(13)));
		if(index==14 && csvParts.size()>14 && csvParts.get(14)!=null)
		{
			setMultipartContent(new ArrayList<String>());
			String[] files = csvParts.get(14).split("\\|");
			for (String multfile : files) {
				if(!multfile.isEmpty())
				{
					String[] mulff = multfile.split(":");
					//controlname:type:filenameOrText:contentType
					if(mulff.length==4 || mulff.length==3) {
						getMultipartContent().add(multfile);
					}
				}
			}
		}
		if(index==15 && csvParts.size()>15 && csvParts.get(15)!=null)
			setSoapBase(getBooleanValue(csvParts.get(15)));
		if(index==16 && csvParts.size()>16 && csvParts.get(16)!=null)
		{
			setSoapParameterValues(new HashMap<String, String>());
			String[] soapVals = csvParts.get(16).split("\\|");
			for (String soapVal : soapVals) {
				String[] kv = soapVal.split(":");
				if(kv.length!=2) {
					logger.error("Invalid Soap Parameter key/value specified for testcase - " + soapVal);
				}
				if(!kv[0].isEmpty() && !kv[1].isEmpty())
				{
					getSoapParameterValues().put(kv[0], kv[1]);
				}
			}
		}
		if(index==17 && csvParts.size()>17 && csvParts.get(17)!=null)
			setWsdlKey(csvParts.get(17));
		if(index==18 && csvParts.size()>18 && csvParts.get(18)!=null)
			setOperationName(csvParts.get(18));
		if(index==19 && csvParts.size()>19 && csvParts.get(19)!=null)
			setSequence(Integer.parseInt(csvParts.get(19)));
		if(index==20 && csvParts.size()>20 && csvParts.get(20)!=null)
		{	
			setWorkflowContextParameterMap(new HashMap<String, String>());
			String[] workflowParams = csvParts.get(20).split("\\|");
			for (String workflowParam : workflowParams) {
				String[] kv = workflowParam.split(":");
				if(kv.length!=2) {
					logger.error("Invalid Workflow Parameter key/value specified for testcase - " + workflowParam);
				}
				if(!kv[0].isEmpty() && !kv[1].isEmpty())
				{
					getWorkflowContextParameterMap().put(kv[0], kv[1]);
				}
			}
		}
		if(index==21 && csvParts.size()>21 && csvParts.get(21)!=null)
			setOutFileName(csvParts.get(21));
		if(index==22 && csvParts.size()>22 && csvParts.get(22)!=null)
			setRepeatScenarioProviderName(csvParts.get(22));
		if(index==23 && csvParts.size()>23 && csvParts.get(23)!=null)
			setNumberOfExecutions(Integer.parseInt(csvParts.get(23)));
		if(index==24 && csvParts.size()>24 && csvParts.get(24)!=null)
			setRepeatScenariosConcurrentExecution(getBooleanValue(csvParts.get(24)));
		if(index==25 && csvParts.size()>25 && csvParts.get(25)!=null)
			setStopOnFirstFailureForPerfTest(getBooleanValue(csvParts.get(25)));
		if(index==26 && csvParts.size()>26 && csvParts.get(26)!=null)
			setPreWaitMs(Long.parseLong(csvParts.get(26)));
		if(index==27 && csvParts.size()>27 && csvParts.get(27)!=null)
			setPostWaitMs(Long.parseLong(csvParts.get(27)));
		if(index==28 && csvParts.size()>28 && csvParts.get(28)!=null)
			setReportResponseContent(getBooleanValue(csvParts.get(28)));
		if(index==29 && csvParts.size()>29 && csvParts.get(29)!=null)
			setPreExecutionDataSourceHookName(csvParts.get(29));
		if(index==30 && csvParts.size()>30 && csvParts.get(30)!=null)
			setPostExecutionDataSourceHookName(csvParts.get(30));
		if(index==31 && csvParts.size()>31 && csvParts.get(31)!=null)
			setAbortOnInvalidContentType(getBooleanValue(csvParts.get(31)));
		if(index==32 && csvParts.size()>32 && csvParts.get(32)!=null)
			setAbortOnInvalidStatusCode(getBooleanValue(csvParts.get(32)));
		if(index==33 && csvParts.size()>33 && csvParts.get(33)!=null)
			setRelatedTestName(csvParts.get(33));
		if(index==34 && csvParts.size()>34 && csvParts.get(34)!=null)
			setExecuteOnCondition(csvParts.get(34));
		if(index==35 && csvParts.size()>35 && csvParts.get(35)!=null)
		{
			setLogicalValidations(new ArrayList<String>());
			String[] nodes = csvParts.get(35).split(":");
			for (String node : nodes) {
				if(!node.isEmpty())
				{
					getLogicalValidations().add(node);
				}
			}
		}
		if(index==35)
		{	
			if(getNumberOfExecutions()==null)
				setNumberOfExecutions(1);
			if(getHeaders().size()==0)
				setHeaders(null);
			if(getExpectedNodes().size()==0)
				setExpectedNodes(null);
			if(getWorkflowContextParameterMap().size()==0)
				setWorkflowContextParameterMap(null);
			if(getSoapParameterValues().size()==0)
				setSoapParameterValues(null);
			if(getRepeatScenarios().size()==0)
				setRepeatScenarios(null);
			if(getMultipartContent().size()==0)
				setMultipartContent(null);
		}
	}
	
	private boolean getBooleanValue(String value)
	{
		if(value.trim().equalsIgnoreCase("y") || value.trim().equalsIgnoreCase("yes")
				|| value.trim().equalsIgnoreCase("1"))
			return true;
		else if(value.trim().equalsIgnoreCase("n") || value.trim().equalsIgnoreCase("no")
				|| value.trim().equalsIgnoreCase("0"))
			return false;
		return Boolean.valueOf(value);
	}
}
 public class AutocannonTestCaseExecutor {

	public static void execute(AcceptanceTestContext context, TestCase tc, TestCaseReport tcr, boolean isSingleExecutionContext) throws Exception {
		GatfExecutorConfig config = context.getGatfExecutorConfig();

		if(tc.getPerfConfig()!=null && tc.getPerfConfig().getType()!=null && tc.getPerfConfig().getType().startsWith("autocannon")) {
			List<String> builderList = new ArrayList<>();
			builderList.add(config.getAutocannonPath());
			
			Integer c = tc.getPerfConfig().getConnections();
			if(c>=0) {
				builderList.add("-c");
				builderList.add(c.toString());
			}
			
			Integer t = tc.getPerfConfig().getThreads();
			if(t>=0) {
				builderList.add("-w");
				builderList.add(t.toString());
			}
			
			Integer r = tc.getPerfConfig().getRate();
			if(r>0) {
				builderList.add("-R");
				builderList.add(c.toString());
			}
			
			builderList.add("--no-progress");
			
			Integer d = tc.getPerfConfig().getDurationSeconds();
			if(d<=0) {
				d = 10;
			}
			builderList.add("-d");
			builderList.add(d.toString());
			
			Integer to = tc.getPerfConfig().getTimeout();
			if(to>0) {
				builderList.add("-t");
				builderList.add(to.toString());
			}
			
			if(tc.getPerfConfig().isLatency()) {
				builderList.add("--latency");
			}
			
			File script = null;
			
			if(StringUtils.isNotBlank(tc.getPerfConfig().getFilePath()) && context.getResourceFile(tc.getPerfConfig().getFilePath().trim()).exists()) {
				script = context.getResourceFile(tc.getPerfConfig().getFilePath().trim());
			}
			
			if(script==null && StringUtils.isNotBlank(tc.getContentFile())) {
				if(context.getResourceFile(tc.getContentFile().trim()).exists()) {
					script = context.getResourceFile(tc.getContentFile().trim());
				}
			}
			
			if(script==null && StringUtils.isNotBlank(tc.getContent())) {
				script = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
				FileUtils.write(script, tc.getContent(), "UTF-8");
			}
			
			if(!tc.getMethod().equalsIgnoreCase("GET")) {
				if(script!=null) {
					builderList.add("-b");
					builderList.add(script.getAbsolutePath());
				}
			}
			
			for (String hdr : tc.getHeaders().keySet()) {
				builderList.add("-H");
				builderList.add(hdr+"="+tc.getHeaders().get(hdr));
			}
			
			builderList.add(tc.getAurl());
			
			StringBuilder out = new StringBuilder();
			StringBuilder err = new StringBuilder();
			
			long start = System.currentTimeMillis();
 			
 			if(tcr.getTestCase()==null) {
 				tcr.setTestCase(tc);
 			}
			executeInt(builderList, out, err, tcr, tc.getBaseUrl());
			
			if(isSingleExecutionContext) {
				if(config.getGatfTestDataConfig()!=null && config.isCompareEnabled() && config.getGatfTestDataConfig().getCompareEnvBaseUrls()!=null) {
					for (String burl : config.getGatfTestDataConfig().getCompareEnvBaseUrls()) {
						String aurl = tc.getAurl().replaceFirst(config.getBaseUrl(), burl);
						builderList.set(builderList.size()-1, aurl);
						executeInt(builderList, out, err, tcr, burl);
					}
				}
			}
			
			if(script!=null) {
				script.delete();
			}

			if(out.length()>0) {
				tcr.setResponseContent(out.toString());
				tcr.setResponseContentType(MediaType.TEXT_PLAIN);
				tcr.setStatus(TestStatus.Success.status);
			}
 			if(err.length()>0) {
 				tcr.setErrorText(err.toString());
				tcr.setStatus(TestStatus.Failed.status);
			}
 			
 			tcr.setExecutionTime(System.currentTimeMillis() - start);
		}
	}
	
	public static void executeInt(List<String> builderList, StringBuilder out, StringBuilder err, TestCaseReport tcr, String baseUrl) {
		StringBuilder out1 = new StringBuilder();
		StringBuilder err1 = new StringBuilder();
		GatfFunctionHandler.executeCmd(builderList, out1, err1, true);
		if(err1.length()>0) {
			//Map<String, Object> retval = parse_autocannon_output(out1);
			//tcr.getPerfResult().add(retval);
			out.append(String.join(" ", builderList)+"\n\n");
			out.append("Result for ");
			out.append(baseUrl);
			out.append("\n");
			String errd = err1.toString();
			errd = errd.replace('?', '*');
			out.append(errd);
			out.append("\n\n");
		}
		/*if(err1.length()>0) {
			err.append(String.join(" ", builderList)+"\n\n");
			err.append("Error for ");
			err.append(baseUrl);
			err.append("\n");
			err.append(err1.toString());
			err.append("\n\n");
		}*/
	}
} public class VegetaTestCaseExecutor {

	public static void execute(AcceptanceTestContext context, TestCase tc, TestCaseReport tcr, boolean isSingleExecutionContext) throws Exception {
		GatfExecutorConfig config = context.getGatfExecutorConfig();

		if(tc.getPerfConfig()!=null && tc.getPerfConfig().getType()!=null && tc.getPerfConfig().getType().startsWith("vegeta")) {
			List<String> builderList = new ArrayList<>();
			builderList.add(config.getVegetaPath());
			builderList.add("attack");
			
			Integer c = tc.getPerfConfig().getConnections();
			if(c>=0) {
				builderList.add("-connections");
				builderList.add(c.toString());
			}
			
			Integer t = tc.getPerfConfig().getThreads();
			if(t>=0) {
				builderList.add("-workers");
				builderList.add(t.toString());
			}
			
			Integer r = tc.getPerfConfig().getRate();
			if(r>0) {
				builderList.add("-rate");
				builderList.add(c.toString());
			}
			
			Integer d = tc.getPerfConfig().getDurationSeconds();
			if(d<=0) {
				d = 10;
			}
			builderList.add("-duration");
			builderList.add(d+"s");
			
			Integer to = tc.getPerfConfig().getTimeout();
			if(to>0) {
				builderList.add("-timeout");
				builderList.add(to+"s");
			}
			
			boolean isPlot = StringUtils.isNotBlank(tc.getPerfConfig().getExtras()) && tc.getPerfConfig().getExtras().indexOf("plot=1")!=-1;
			
			File script = null;
			
			if(StringUtils.isNotBlank(tc.getPerfConfig().getFilePath()) && context.getResourceFile(tc.getPerfConfig().getFilePath().trim()).exists()) {
				script = context.getResourceFile(tc.getPerfConfig().getFilePath().trim());
			}
			
			if(script==null && StringUtils.isNotBlank(tc.getContentFile())) {
				if(context.getResourceFile(tc.getContentFile().trim()).exists()) {
					script = context.getResourceFile(tc.getContentFile().trim());
				}
			}
			
			if(script==null && StringUtils.isNotBlank(tc.getContent())) {
				script = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
				FileUtils.write(script, tc.getContent(), "UTF-8");
			}
			
			StringBuilder out = new StringBuilder();
			StringBuilder err = new StringBuilder();
			
			long start = System.currentTimeMillis();
 			
 			if(tcr.getTestCase()==null) {
 				tcr.setTestCase(tc);
 			}
 			
			execCmd(config.getVegetaPath(), out, err, tcr, tc, script, builderList, tc.getAurl(), tc.getBaseUrl(), isPlot);
			
			if(isSingleExecutionContext) {
				if(config.getGatfTestDataConfig()!=null && config.isCompareEnabled() && config.getGatfTestDataConfig().getCompareEnvBaseUrls()!=null) {
					for (String burl : config.getGatfTestDataConfig().getCompareEnvBaseUrls()) {
						String aurl = tc.getAurl().replaceFirst(config.getBaseUrl(), burl);
						execCmd(config.getVegetaPath(), out, err, tcr, tc, script, builderList, aurl, burl, isPlot);
					}
				}
			}
			
			if(script!=null) {
				script.delete();
			}

			if(out.length()>0) {
				tcr.setResponseContent(out.toString());
				tcr.setResponseContentType(MediaType.TEXT_PLAIN);
				tcr.setStatus(TestStatus.Success.status);
			}
 			if(err.length()>0) {
 				tcr.setErrorText(err.toString());
				tcr.setStatus(TestStatus.Failed.status);
			}
 			
 			tcr.setExecutionTime(System.currentTimeMillis() - start);
		}
	}
	
	@SuppressWarnings("unchecked")
	private static void execCmd(String vegetaPath, StringBuilder out, StringBuilder err, TestCaseReport tcr, TestCase tc, File script, List<String> bl, String url, String baseUrl, boolean isPlot) throws Exception {
		File results = File.createTempFile(UUID.randomUUID().toString(), ".bin");
		File target = null;
		List<String> builderList = new ArrayList<>();
		if(tc.getHeaders()!=null && tc.getHeaders().size()>0) {
			StringBuilder httpreq = new StringBuilder(tc.getMethod().toUpperCase() + " " + url);
			httpreq.append("\r\n");
			for (String hdr : tc.getHeaders().keySet()) {
				httpreq.append(hdr);
				httpreq.append(": ");
				httpreq.append(tc.getHeaders().get(hdr));
				httpreq.append("\r\n");
			}
			if(script!=null) {
				httpreq.append("@");
				httpreq.append(script.getAbsolutePath());
			}
			target = File.createTempFile(UUID.randomUUID().toString(), ".tmp");
			FileUtils.write(target, httpreq.toString(), "UTF-8");
			
			bl.add("-format");
			bl.add("http");
			bl.add("-targets");
			bl.add(target.getAbsolutePath());
			builderList = bl;
		} else {
			builderList.add("echo");
			builderList.add("'GET");
			builderList.add(tc.getAurl());
			builderList.add("'|");
			builderList.addAll(bl);
		}
		
		builderList.add("|");
		builderList.add("tee");
		builderList.add(results.getAbsolutePath());
		builderList.add("|");
		builderList.add(vegetaPath);
		builderList.add("report");
		
		StringBuilder out1 = new StringBuilder();
		StringBuilder err1 = new StringBuilder();
		GatfFunctionHandler.executeCmd(builderList, out1, err1, true);
		if(out1.length()>0) {
			List<String> rbl = new ArrayList<>();
			rbl.add(vegetaPath);
			rbl.add("report");
			rbl.add("-type=json");
			rbl.add(results.getAbsolutePath());
			StringBuilder out2 = new StringBuilder();
			GatfFunctionHandler.executeCmd(rbl, out2, null, true);
			Map<String, Object> retval = WorkflowContextHandler.OM.readValue(out2.toString(), Map.class);
			tcr.getPerfResult().add(retval);
			
			if(isPlot) {
				File plothtml = File.createTempFile(UUID.randomUUID().toString(), ".html");
				rbl = new ArrayList<>();
				rbl.add("cat");
				rbl.add(results.getAbsolutePath());
				rbl.add("|");
				rbl.add(vegetaPath);
				rbl.add("plot");
				rbl.add(">");
				rbl.add(plothtml.getAbsolutePath());
				
				if(retval!=null) {
					GatfFunctionHandler.executeCmd(rbl, null, null, true);
					retval.put("_plot_html_", plothtml.getAbsolutePath());
				}
			}
			
			out.append(String.join(" ", builderList)+"\n\n");
			out.append("Result for ");
			out.append(baseUrl);
			out.append("\n");
			out.append(out1.toString());
			out.append("\n\n");
		}
		if(err1.length()>0) {
			err.append(String.join(" ", builderList)+"\n\n");
			err.append("Error for ");
			err.append(baseUrl);
			err.append("\n");
			err.append(err1.toString());
			err.append("\n\n");
		}
		
		if(target!=null) {
			target.delete();
		}
		
		results.delete();
	}
} public class WrkTestCaseExecutor {

	//Transfomed from https://github.com/MaartenSmeets/db_perftest/blob/master/test_scripts/wrk_parser.py
	
	static Pattern B1_PAT = Pattern.compile("^(\\d+\\.*\\d*)(\\w+)$");
	private static BigDecimal get_bytes(String size_str) {
		Matcher m = B1_PAT.matcher(size_str);
		if(m.matches()) {
			float size = Float.valueOf(m.group(1)).floatValue();
			String suffix = m.group(2).toLowerCase();
			if(suffix.equals("b"))
		        return new BigDecimal(size).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("kb") || suffix.equals("kib"))
		        return new BigDecimal(size * 1024).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("mb") || suffix.equals("mib"))
		        return new BigDecimal(size * 1024 * 1024).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("gb") || suffix.equals("gib"))
		        return new BigDecimal(size * 1024 * 1024 * 1024).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("tb") || suffix.equals("tib"))
		        return new BigDecimal(size * 1024 * 1024 * 1024 * 1024).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("pb") || suffix.equals("pib"))
		        return new BigDecimal(size * 1024 * 1024 * 1024 * 1024 * 1024).setScale(2, RoundingMode.HALF_DOWN);
		}
	    return BigDecimal.ZERO;
	}


	static Pattern B2_PAT = Pattern.compile("^(\\d+\\.*\\d*)(\\w*)$");
	private static BigDecimal get_number(String size_str) {
		Matcher m = B2_PAT.matcher(size_str);
		if(m.matches()) {
			float size = Float.valueOf(m.group(1)).floatValue();
			String suffix = m.group(2).toLowerCase();
			if(suffix.equals("k"))
		        return new BigDecimal(size * 1000).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("m"))
		        return new BigDecimal(size * 1000 * 1000).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("g"))
		        return new BigDecimal(size * 1000 * 1000 * 1000).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("t"))
		        return new BigDecimal(size * 1000 * 1000 * 1000 * 1000).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("p"))
		        return new BigDecimal(size * 1000 * 1000 * 1000 * 1000 * 1000).setScale(2, RoundingMode.HALF_DOWN);
		    else
		        return new BigDecimal(size).setScale(2, RoundingMode.HALF_DOWN);
		}
	    return BigDecimal.ZERO;
	}

	private static BigDecimal get_ms(String size_str) {
		Matcher m = B2_PAT.matcher(size_str);
		if(m.matches()) {
			float size = Float.valueOf(m.group(1)).floatValue();
			String suffix = m.group(2).toLowerCase();
			if(suffix.equals("us"))
		        return new BigDecimal(size / 1000).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("ms"))
		        return new BigDecimal(size).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("s"))
		        return new BigDecimal(size * 1000).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("m"))
		        return new BigDecimal(size * 1000 * 60).setScale(2, RoundingMode.HALF_DOWN);
		    else if(suffix.equals("h"))
		        return new BigDecimal(size * 1000 * 60 * 60).setScale(2, RoundingMode.HALF_DOWN);
		    else
		        return new BigDecimal(size).setScale(2, RoundingMode.HALF_DOWN);
		}
	    return BigDecimal.ZERO;
	}
	
	static Pattern LAT_PAT = Pattern.compile("^\\s*Latency\\s+(\\d+\\.\\d+\\w*)\\s+(\\d+\\.\\d+\\w*)\\s+(\\d+\\.\\d+\\w*)\\s+(\\d+\\.\\d+%).*$");
	static Pattern RQS_PAT = Pattern.compile("^\\s*Req/Sec\\s+(\\d+\\.\\d+\\w*)\\s+(\\d+\\.\\d+\\w*)\\s+(\\d+\\.\\d+\\w*)\\s+(\\d+\\.\\d+%).*$");
	static Pattern RQI_PAT = Pattern.compile("^\\s*(\\d+)\\ requests in (\\d+\\.\\d+\\w*)\\,\\ (\\d+\\.\\d+\\w*)\\ read.*$");
	static Pattern FRQS_PAT = Pattern.compile("^Requests\\/sec\\:\\s+(\\d+\\.*\\d*).*$");
	static Pattern TRFS_PAT = Pattern.compile("^Transfer\\/sec\\:\\s+(\\d+\\.*\\d*\\w+).*$");
	static Pattern SCE_PAT = Pattern.compile("^\\s+Socket errors:\\ connect (\\d+\\w*)\\,\\ read (\\d+\\w*)\\,\\ write\\ (\\d+\\w*)\\,\\ timeout\\ (\\d+\\w*).*$");
	static Pattern LPER_PAT = Pattern.compile("^(50|75|90|99)%\\s+(\\d+\\.\\d+\\w*)$");
	static Pattern LPER_PAT2 = Pattern.compile("^(\\d+\\.\\d+)%\\s+(\\d+\\.\\d+\\w*)$");
	static Pattern HDR_PAT2 = Pattern.compile("^(\\d+\\.\\d+)\\s+(\\d+\\.\\d+)\\s+(\\d+)\\s+(\\d+\\.\\d+|inf)$");
	@SuppressWarnings("unchecked")
	private static Map<String, Object> parse_wrk_output(StringBuilder out) {
		Map<String, Object> retval = new HashMap<String, Object>();
		String[] lines = out.toString().split("\\r?\\n");
		boolean latstarted = false;
		boolean onelatdone = false;
		boolean onehdrdone = false;
		int hdrstarted = 0;
		for (String line : lines) {
			line = line.trim();
			if(line.startsWith("Latency Distribution")) {
				latstarted = true;
				continue;
			} else if(line.startsWith("Non-2xx or 3xx responses: ")) {
				retval.put("non_2xx_resp", Long.valueOf(line.substring(line.lastIndexOf(" ")+1)));
				continue;
			} else if(line.startsWith("Detailed Percentile spectrum:")) {
				hdrstarted++;
				continue;
			} else if(hdrstarted>0) {
				if(hdrstarted==3) {
					boolean matched = false;
					Matcher m = HDR_PAT2.matcher(line);
					if(m.matches()) {
						matched = true;
						String key = onehdrdone?"dps_details_2":"dps_details";
						List<String[]> rows = (List<String[]>)retval.get(key);
						if(rows==null) {
							rows = new ArrayList<String[]>();
							retval.put(key, rows);
						}
						rows.add(new String[] {m.group(1), m.group(2), m.group(3), m.group(4)});
						
					}
					if(matched) {
						continue;
					} else {
						hdrstarted = 0;
						onehdrdone = true;
					}
				} else {
					hdrstarted++;
					continue;
				}
			} else if(latstarted) {
				boolean matched = false;
				Matcher m = LPER_PAT.matcher(line);
				if(m.matches()) {
					matched = true;
					retval.put("lat_dis_"+m.group(1)+(onelatdone?"_2":"")+"_s", m.group(2));
					retval.put("lat_dis_"+m.group(1)+(onelatdone?"_2":""), get_ms(m.group(2)));
				} else {
					m = LPER_PAT2.matcher(line);
					if(m.matches()) {
						String vals = "";
						BigDecimal val = new BigDecimal(m.group(1)).setScale(3, RoundingMode.UNNECESSARY);
						BigDecimal frc = val.remainder(BigDecimal.ONE);
						if(frc.floatValue()>0) {
							frc = frc.multiply(new BigDecimal(1000)).setScale(0, RoundingMode.UNNECESSARY);
							vals = val.intValue() + "_" +frc.toPlainString();
						} else {
							vals = val.intValue() + "";
						}
						retval.put("lat_dis_"+vals+(onelatdone?"_2":"")+"_s", m.group(2));
						retval.put("lat_dis_"+vals+(onelatdone?"_2":""), get_ms(m.group(2)));
						matched = true;
					}
				}
				if(matched) {
					continue;
				} else {
					latstarted = false;
					onelatdone = true;
				}
			}
			
			Matcher m = LAT_PAT.matcher(line);
			if(m.matches()) {
				retval.put("lat_avg_s", m.group(1));
	            retval.put("lat_stdev_s", m.group(2));
	            retval.put("lat_max_s", m.group(3));
				retval.put("lat_avg", get_ms(m.group(1)));
	            retval.put("lat_stdev", get_ms(m.group(2)));
	            retval.put("lat_max", get_ms(m.group(3)));
	            retval.put("lat_stdev_perc", m.group(4));
			} else {
				m = RQS_PAT.matcher(line);
				if(m.matches()) {
					retval.put("req_avg_s", m.group(1));
		            retval.put("req_stdev_s", m.group(2));
		            retval.put("req_max_s", m.group(3));
					retval.put("req_avg", get_number(m.group(1)));
		            retval.put("req_stdev", get_number(m.group(2)));
		            retval.put("req_max", get_number(m.group(3)));
		            retval.put("req_stdev_perc", m.group(4));
				} else {
					m = RQI_PAT.matcher(line);
					if(m.matches()) {
						retval.put("tot_requests_s", m.group(1));
			            retval.put("tot_duration_s", m.group(2));
			            retval.put("read_s", m.group(3));
						retval.put("tot_requests", get_number(m.group(1)));
			            retval.put("tot_duration", get_ms(m.group(2)));
			            retval.put("read", get_bytes(m.group(3)));
					} else {
						m = FRQS_PAT.matcher(line);
						if(m.matches()) {
							retval.put("req_sec_tot_s", m.group(1));
							retval.put("req_sec_tot", get_number(m.group(1)));
						} else {
							m = TRFS_PAT.matcher(line);
							if(m.matches()) {
								retval.put("read_tot_s", m.group(1));
								retval.put("read_tot", get_bytes(m.group(1)));
							} else {
								m = SCE_PAT.matcher(line);
								if(m.matches()) {
									retval.put("err_connect_s", m.group(1));
						            retval.put("err_read_s", m.group(2));
						            retval.put("err_write_s", m.group(3));
						            retval.put("err_timeout_s", m.group(4));
									retval.put("err_connect", get_number(m.group(1)));
						            retval.put("err_read", get_number(m.group(2)));
						            retval.put("err_write", get_number(m.group(3)));
						            retval.put("err_timeout", get_number(m.group(4)));
								}
							}
						}
					}
				}
			}
		}
	    if(!retval.containsKey("err_connect")) {
	        retval.put("err_connect_s", "0");
	        retval.put("err_connect", 0);
	    }
	    if(!retval.containsKey("err_read")) {
	        retval.put("err_read_s", "0");
	        retval.put("err_read", 0);
	    }
	    if(!retval.containsKey("err_write")) {
	        retval.put("err_write_s", "0");
	        retval.put("err_write", 0);
	    }
	    if(!retval.containsKey("err_timeout")) {
	        retval.put("err_timeout_s", "0");
	        retval.put("err_timeout", 0);
	    }
	    
	    return retval;
	}

	public static void execute(AcceptanceTestContext context, TestCase tc, TestCaseReport tcr, boolean isSingleExecutionContext) throws Exception {
		GatfExecutorConfig config = context.getGatfExecutorConfig();

		if(tc.getPerfConfig()!=null && tc.getPerfConfig().getType()!=null && tc.getPerfConfig().getType().startsWith("wrk")) {
			List<String> builderList = new ArrayList<>();
			builderList.add(tc.getPerfConfig().getType().equals("wrk")?config.getWrkPath():config.getWrk2Path());
			
			Integer c = tc.getPerfConfig().getConnections();
			if(c<=0) {
				c = 10; 
			}
			builderList.add("-c");
			builderList.add(c.toString());
			
			Integer r = tc.getPerfConfig().getRate();
			if(tc.getPerfConfig().getType().equals("wrk2")) {
				if(r<=0) {
					r = 1000;
				}
				builderList.add("-R");
				builderList.add(r.toString());
			}
			
			Integer d = tc.getPerfConfig().getDurationSeconds();
			if(d<=0) {
				d = 10;
			}
			builderList.add("-d");
			builderList.add(d+"s");
			
			Integer t = tc.getPerfConfig().getThreads();
			if(t<=0) {
				t = 10;
			}
			builderList.add("-t");
			builderList.add(t.toString());
			
			Integer to = tc.getPerfConfig().getTimeout();
			if(to>0) {
				builderList.add("--timeout");
				builderList.add(to+"s");
			}
			
			if(tc.getPerfConfig().isLatency()) {
				builderList.add("--latency");
			}
			
			if(tc.getPerfConfig().getType().equals("wrk2")) {
				builderList.add("--u_latency");
			}
			
			File script = null;
			
			if(StringUtils.isNotBlank(tc.getPerfConfig().getFilePath()) && context.getResourceFile(tc.getPerfConfig().getFilePath().trim()).exists()) {
				script = context.getResourceFile(tc.getPerfConfig().getFilePath().trim());
				builderList.add("-s ");
				builderList.add(context.getResourceFile(tc.getPerfConfig().getFilePath().trim()).getAbsolutePath());
			}
			
			if(script==null) {
				if(tc.getHeaders()!=null && tc.getHeaders().size()>0) {
					StringBuilder prog = new StringBuilder();
					prog.append("wrk.method = \"");
					prog.append(tc.getMethod().toUpperCase());
					prog.append("\"\n");
					for (String hdr : tc.getHeaders().keySet()) {
						prog.append("wrk.headers[\""+hdr+"] = \"");
						prog.append(tc.getHeaders().get(hdr));
						prog.append("\"\n");
					}
					prog.append("wrk.body = [====================[\n");
					String content = tc.getContent();
					if(StringUtils.isBlank(content) && tc.getContentFile()!=null) {
						if(context.getResourceFile(tc.getContentFile().trim()).exists()) {
							content = FileUtils.readFileToString(context.getResourceFile(tc.getContentFile().trim()), "UTF-8");
						}
					}
					if(content==null) {
						content = "";
					}
					prog.append(content);
					prog.append("\n]====================]\n");
					
					script = File.createTempFile(UUID.randomUUID().toString(), ".lua");
					FileUtils.write(script, prog.toString(), "UTF-8");
					
					builderList.add("-s");
					builderList.add(script.getAbsolutePath());
				}
			}
			
			builderList.add(tc.getAurl());
 			
 			if(tcr.getTestCase()==null) {
 				tcr.setTestCase(tc);
 			}
			
			StringBuilder out = new StringBuilder();
			StringBuilder err = new StringBuilder();
			
			long start = System.currentTimeMillis();
			executeInt(builderList, out, err, tcr, tc.getBaseUrl());
			
			if(isSingleExecutionContext) {
				if(config.getGatfTestDataConfig()!=null && config.isCompareEnabled() && config.getGatfTestDataConfig().getCompareEnvBaseUrls()!=null) {
					for (String burl : config.getGatfTestDataConfig().getCompareEnvBaseUrls()) {
						String aurl = tc.getAurl().replaceFirst(config.getBaseUrl(), burl);
						builderList.set(builderList.size()-1, aurl);
						executeInt(builderList, out, err, tcr, burl);
					}
				}
			}
			
			if(script!=null) {
				script.delete();
			}

			if(out.length()>0) {
				tcr.setResponseContent(out.toString());
				tcr.setResponseContentType(MediaType.TEXT_PLAIN);
				tcr.setStatus(TestStatus.Success.status);
			}
 			if(err.length()>0) {
 				tcr.setErrorText(err.toString());
				tcr.setStatus(TestStatus.Failed.status);
			}
 			
 			tcr.setExecutionTime(System.currentTimeMillis() - start);
		}
	}
	
	public static void executeInt(List<String> builderList, StringBuilder out, StringBuilder err, TestCaseReport tcr, String baseUrl) {
		StringBuilder out1 = new StringBuilder();
		StringBuilder err1 = new StringBuilder();
		GatfFunctionHandler.executeCmd(builderList, out1, err1, false);
		if(out1.length()>0) {
			Map<String, Object> retval = parse_wrk_output(out1);
			tcr.getPerfResult().add(retval);
			out.append(String.join(" ", builderList)+"\n\n");
			out.append("Result for ");
			out.append(baseUrl);
			out.append("\n");
			out.append(out1.toString());
			out.append("\n\n");
		}
		if(err1.length()>0) {
			err.append(String.join(" ", builderList)+"\n\n");
			err.append("Error for ");
			err.append(baseUrl);
			err.append("\n");
			err.append(err1.toString());
			err.append("\n\n");
		}
	}
} @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();
	}
}
 public static enum TestFailureReason {
	InvalidStatusCode("InvalidStatusCode"),
	Exception("Exception"),
	NodeValidationFailed("NodeValidationFailed"),
	InvalidContentType("InvalidContentType"),
	ExecuteConditionFailed("ExecConditionFailed");
		
	public String status;
	private TestFailureReason(String status) {
		this.status = status;
	}
}
 public static enum TestStatus {
	Success("Success"),
	Skipped("Skipped"),
	Failed("Failed");
		
	public String status;
	private TestStatus(String status) {
		this.status = status;
	}
}
 public class JSONResponseValidator extends ResponseValidator {

	protected Object getInternalObject(TestCaseReport testCaseReport) throws Exception
	{
		return testCaseReport.getResponseContent();
	}

	protected String getNodeValue(Object intObj, String node) throws Exception {
		String nvalue = null;
		try {
			nvalue = JsonPath.read((String)intObj, node).toString();
		} catch (Exception e) {
			//throw new AssertionError("Expected Node " + node + " not found");
		}
		return nvalue;
	}

	protected ResponseType getType() {
		return ResponseType.JSON;
	}

	protected List<Map<String, String>> getResponseMappedValue(String expression, String propNames, Object nodeLst) throws Exception {
		if(expression.equals("")) {
			expression = "$";
		}
		
		List<Map<String, String>> jsonValues = JsonPath.read((String)nodeLst, expression);
		return jsonValues;
	}

	protected int getResponseMappedCount(String expression, Object nodeLst) throws Exception {
		String responseMappedCount = JsonPath.read((String)nodeLst, expression).toString();
		
		int responseCount = -1;
		try {
			responseCount = Integer.valueOf(responseMappedCount);
		} catch (Exception e) {
			throw new AssertionError("Invalid responseMappedCount variable defined, " +
					"derived value should be number - "+expression);
		}
		
		return responseCount;
	}
}
 public class NoContentResponseValidator extends ResponseValidator {

	protected Object getInternalObject(TestCaseReport testCaseReport) throws Exception {
		return null;
	}

	protected ResponseType getType() {
		return ResponseType.NONE;
	}

	protected String getNodeValue(Object intObj, String node) throws Exception {
		return null;
	}

	protected List<Map<String, String>> getResponseMappedValue(
			String expression, String propNames, Object nodeLst)
			throws Exception {
		return null;
	}

	protected int getResponseMappedCount(
			String expression, Object nodeLst) throws Exception {
		return -1;
	}
}
 public class SOAPResponseValidator extends ResponseValidator {

	public static String getLocalNodeName(String nodeName) {
		if(nodeName!=null && nodeName.indexOf(":")!=-1) {
			return nodeName.substring(nodeName.indexOf(":")+1);
		}
		return nodeName;
	}
	
	public static Node getNodeByNameCaseInsensitive(Node node, String nodeName) {
		if(node.getNodeName().equalsIgnoreCase(nodeName) ||
				getLocalNodeName(node.getNodeName()).equalsIgnoreCase(nodeName))
			return node;
		for (int i=0;i<node.getChildNodes().getLength();i++) {
			if(node.getChildNodes().item(i).getNodeType()==Node.ELEMENT_NODE) {
				if(node.getChildNodes().item(i).getNodeName().equalsIgnoreCase(nodeName) ||
						getLocalNodeName(node.getChildNodes().item(i).getNodeName()).equalsIgnoreCase(nodeName)) {
					return node.getChildNodes().item(i);
				}
			}
		}
		return null;
	}
	
	public static Node getNextElement(Node node) {
		for (int i=0;i<node.getChildNodes().getLength();i++) {
			if(node.getChildNodes().item(i).getNodeType()==Node.ELEMENT_NODE) {
				return node.getChildNodes().item(i);
			}
		}
		return null;
	}
	
	public static String createXPathExpression(String suffix, Node... nodes) {
		StringBuilder build = new StringBuilder();
		for (Node node : nodes) {
			build.append("/");
			build.append(getLocalNodeName(node.getNodeName()));
		}
		build.append("/");
		build.append(suffix.replaceAll("\\.", "\\/"));
		return build.toString();
	}
	
	public static void processSOAPRequest(Document soapMessage, TestCase testCase) throws Exception
	{
		if(testCase.getSoapParameterValues()==null)return;
		for (Map.Entry<String, String> entry : testCase.getSoapParameterValues().entrySet()) {
			Node envelope = getNodeByNameCaseInsensitive(soapMessage.getFirstChild(), "envelope");
			Node body = getNodeByNameCaseInsensitive(envelope, "body");
			Node requestBody = getNextElement(body);
			String expression = createXPathExpression(entry.getKey(), envelope, body, requestBody);
			XPath xPath = XPathFactory.newInstance().newXPath();
			NodeList nodelist = (NodeList) xPath.compile(expression).evaluate(soapMessage, XPathConstants.NODESET);
			Assert.assertNotNull("Cannot find soap parameter " + entry.getKey(), 
					nodelist!=null && nodelist.getLength()>0);
			nodelist.item(0).getFirstChild().setNodeValue(entry.getValue());
		}
	}
	
	protected Object getInternalObject(TestCaseReport testCaseReport) throws Exception
	{
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document xmlDocument = db.parse(new ByteArrayInputStream(testCaseReport.getResponseContent().getBytes()));
		return xmlDocument;
	}

	protected String getNodeValue(Object intObj, String node) throws Exception {
	    NodeList xmlNodeList = null;
	    String xmlValue = null;
        try
        {
            Document xmlDocument = (Document)intObj;
            Node envelope = getNodeByNameCaseInsensitive(xmlDocument.getFirstChild(), "envelope");
            Node body = getNodeByNameCaseInsensitive(envelope, "body");
            Node requestBody = getNextElement(body);
            Node returnBody = getNextElement(requestBody);
            String expression = createXPathExpression(node, envelope, body, requestBody, returnBody);
            if(expression.indexOf("/[")!=-1)
                expression = expression.replaceAll("/[", "[");
            xmlNodeList = getNodeByXpath(node, (Document)intObj);
            //Assert.assertTrue("Expected Node " + node + " is null", xmlNodeList!=null && xmlNodeList.getLength()>0);
            xmlValue = getXMLNodeValue(xmlNodeList.item(0));
        }
        catch (Exception e)
        {
        }
		return xmlValue;
	}

	protected ResponseType getType() {
		return ResponseType.SOAP;
	}

	protected List<Map<String, String>> getResponseMappedValue(String expression, String propNames, Object nodeLst) throws Exception {
		expression = expression.replaceAll("\\.", "\\/");
		if(expression.length()>1 && expression.charAt(0)!='/')
			expression = "/" + expression;
		
		Node envelope = getNodeByNameCaseInsensitive(((Document)nodeLst).getFirstChild(), "envelope");
		Node body = getNodeByNameCaseInsensitive(envelope, "body");
		Node requestBody = getNextElement(body);
		Node returnBody = getNextElement(requestBody);
		if(expression.equals(""))
			expression = SOAPResponseValidator.createXPathExpression(expression, envelope, body, requestBody) + "*";
		else	
			expression = SOAPResponseValidator.createXPathExpression(expression, envelope, body, requestBody, returnBody);
		
		XPath xPath =  XPathFactory.newInstance().newXPath();
		NodeList xmlNodeList = (NodeList) xPath.compile(expression).evaluate((Document)nodeLst, XPathConstants.NODESET);
		Assert.assertTrue("Workflow soap variable " + expression +" is null",  
				xmlNodeList!=null && xmlNodeList.getLength()>0);
		
		List<Map<String, String>> soapValues = WorkflowContextHandler.getNodeValueMapList(propNames, xmlNodeList);
		return soapValues;
	}

	protected int getResponseMappedCount(String expression, Object nodeLst) throws Exception {
		expression = expression.replaceAll("\\.", "\\/");
		if(expression.length()>1 && expression.charAt(0)!='/')
			expression = "/" + expression;
		
		Node envelope = getNodeByNameCaseInsensitive(((Document)nodeLst).getFirstChild(), "envelope");
		Node body = getNodeByNameCaseInsensitive(envelope, "body");
		Node requestBody = getNextElement(body);
		Node returnBody = getNextElement(requestBody);
		if(expression.equals(""))
			expression = SOAPResponseValidator.createXPathExpression(expression, envelope, body, requestBody) + "*";
		else	
			expression = SOAPResponseValidator.createXPathExpression(expression, envelope, body, requestBody, returnBody);
		
		XPath xPath =  XPathFactory.newInstance().newXPath();
		NodeList xmlNodeList = (NodeList) xPath.compile(expression).evaluate((Document)nodeLst, XPathConstants.NODESET);
		Assert.assertTrue("Workflow soap variable " + expression +" is null",  
				xmlNodeList!=null && xmlNodeList.getLength()>0);

		String xmlValue = XMLResponseValidator.getXMLNodeValue(xmlNodeList.item(0));
		Assert.assertNotNull("Workflow soap variable " + expression +" is null", xmlValue);
		
		int responseCount = -1;
		try {
			responseCount = Integer.valueOf(xmlValue);
		} catch (Exception e) {
			throw new AssertionError("Invalid responseMappedCount variable defined, " +
					"derived value should be number - "+expression);
		}
		return responseCount;
	}
}
 public class TextResponseValidator extends ResponseValidator {

	protected Object getInternalObject(TestCaseReport testCaseReport) throws Exception {
		return testCaseReport.getResponseContent();
	}

	protected ResponseType getType() {
		return ResponseType.PLAIN;
	}

	protected String getNodeValue(Object intObj, String node) throws Exception {
		String nvalue = null;
		try {
			String cont = (String)intObj;
			if(cont!=null && cont.indexOf("&")!=-1) {
				String[] params = cont.split("&");
				for (String pdef : params) {
					String[] pv = pdef.split("=");
					if(pv.length>1 && pv[0].equals(node)) {
						nvalue = pv[1];
						break;
					}
				}
			}
		} catch (Exception e) {
			throw new AssertionError("Expected Node " + node + " not found");
		}
		return nvalue;
	}

	protected List<Map<String, String>> getResponseMappedValue(
			String expression, String propNames, Object nodeLst)
			throws Exception {
		return null;
	}

	protected int getResponseMappedCount(
			String expression, Object nodeLst) throws Exception {
		return -1;
	}
}
 public class XMLResponseValidator extends ResponseValidator {

	protected Object getInternalObject(TestCaseReport testCaseReport) throws Exception
	{
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document xmlDocument = db.parse(new ByteArrayInputStream(testCaseReport.getResponseContent().getBytes()));
		return xmlDocument;
	}

	protected String getNodeValue(Object intObj, String node) throws Exception {
	    NodeList xmlNodeList = null;
	    String xmlValue = null;
	    try
        {
	        xmlNodeList = getNodeByXpath(node, (Document)intObj);
	        xmlValue = getXMLNodeValue(xmlNodeList.item(0));
        }
        catch (Exception e)
        {
        }
	    //Assert.assertTrue("Expected Node " + node + " is null", xmlNodeList!=null && xmlNodeList.getLength()>0);
	    return xmlValue;
	}

	protected ResponseType getType() {
		return ResponseType.XML;
	}

	protected List<Map<String, String>> getResponseMappedValue(String expression, String propNames, Object initObj) throws Exception {
		expression = expression.replaceAll("\\.", "\\/");
		if(expression.equals("")) {
			expression = "/*";
		}
		
		if(expression.charAt(0)!='/')
			expression = "/" + expression;
		
		XPath xPath =  XPathFactory.newInstance().newXPath();
		NodeList xmlNodeList = (NodeList) xPath.compile(expression).evaluate((Document)initObj, XPathConstants.NODESET);
		Assert.assertTrue("Workflow xml variable " + expression +" is null",  
				xmlNodeList!=null && xmlNodeList.getLength()>0);
		
		List<Map<String, String>> xmlValues = WorkflowContextHandler.getNodeValueMapList(propNames, xmlNodeList);
		return xmlValues;
	}

	protected int getResponseMappedCount(String expression, Object initObj) throws Exception {
		expression = expression.replaceAll("\\.", "\\/");
		if(expression.charAt(0)!='/')
			expression = "/" + expression;
		
		XPath xPath =  XPathFactory.newInstance().newXPath();
		NodeList xmlNodeList = (NodeList) xPath.compile(expression).evaluate((Document)initObj, XPathConstants.NODESET);
		Assert.assertTrue("Workflow xml variable " + expression +" is null", 
				xmlNodeList!=null && xmlNodeList.getLength()>0);

		String xmlValue = XMLResponseValidator.getXMLNodeValue(xmlNodeList.item(0));
		Assert.assertNotNull("Workflow xml variable " + expression +" is null", xmlValue);
		
		int responseCount = -1;
		try {
			responseCount = Integer.valueOf(xmlValue);
		} catch (Exception e) {
			throw new AssertionError("Invalid responseMappedCount variable defined, " +
					"derived value should be number - "+expression);
		}
		return responseCount;
	}
}

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.junit.Assert;
import org.w3c.dom.Document;
import com.gatf.executor.core.AcceptanceTestContext;
import com.gatf.executor.core.TestCase;
import com.gatf.executor.ext.AutocannonTestCaseExecutor;
import com.gatf.executor.ext.VegetaTestCaseExecutor;
import com.gatf.executor.ext.WrkTestCaseExecutor;
import com.gatf.executor.report.TestCaseReport;
import com.gatf.executor.report.TestCaseReport.TestFailureReason;
import com.gatf.executor.report.TestCaseReport.TestStatus;
import com.gatf.executor.validator.JSONResponseValidator;
import com.gatf.executor.validator.NoContentResponseValidator;
import com.gatf.executor.validator.SOAPResponseValidator;
import com.gatf.executor.validator.TextResponseValidator;
import com.gatf.executor.validator.XMLResponseValidator;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.ConnectionPool;
import okhttp3.MultipartBody;
import okhttp3.MultipartBody.Part;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/*
    Copyright 2013-2019, Sumeet Chhetri
    
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    
    http://www.apache.org/licenses/LICENSE-2.0
    
    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 com.gatf.executor.executor;






/**
 * @author Sumeet Chhetri
 * The main executor class which handles http layer invocations/reponse handling
 * Uses the async-http-client library to make the execution performant/concurrent
 */
public class TestCaseExecutorUtil {

	private Logger logger = Logger.getLogger(TestCaseExecutorUtil.class.getSimpleName());
	
	private static final JSONResponseValidator jsonResponseValidator = new JSONResponseValidator();
	

	private static final XMLResponseValidator xmlResponseValidator = new XMLResponseValidator();
