package at.ac.tuwien.iter.executors; import java.io.IOException; import java.io.StringReader; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.tapestry5.ioc.services.TypeCoercer; import org.slf4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import at.ac.tuwien.iter.data.Test; import at.ac.tuwien.iter.data.TestResult; import at.ac.tuwien.iter.exceptions.TestExecutionException; import at.ac.tuwien.iter.exceptions.TestTimeoutException; import at.ac.tuwien.iter.services.DataCollectionService; /** * Basic workflow manager. It has the methods to execute a test, to load * execution trace from a mysql dump , to analyze results through octave. * * Note that this implementation is specific to bind AUToCLES APIs * * @author alessiogambi * */ public class BasicRunner { // Refactoring and configuration: // Make this more robust for exceptions ! // Store the experiment data somewhere / somehow -> bootstrap ! private static final long POLLING_INTERVAL = 60 * 1000l; private final ConfigurationManager configurationManager; private final DataCollectionService dataCollector; private long timeoutMillis; private Logger logger; // TODO Use Registry to inject the configurationManager Object public BasicRunner(Logger logger, ConfigurationManager configurationManager, TypeCoercer typeCoercer, // MathEngineDao mathEnginedao, DataCollectionService dataCollector, long timeout) { this.logger = logger; this.configurationManager = configurationManager; this.dataCollector = dataCollector; this.timeoutMillis = timeout; } // NOTE When a timeout goes off there are two cases, the exeperiment is // still going, the experiment is in the tearing down phase. // In the latter case, forceUndeploy() will fail with an exception private void forceUndeploy(Test test, String startPage) throws TestExecutionException { logger.warn("BasicRunner.forceUndeploy()"); logger.debug(startPage); int instanceId = -1; try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder; Document doc = null; XPathExpression expr = null; builder = factory.newDocumentBuilder(); doc = builder.parse(new InputSource(new StringReader(startPage))); XPathFactory xFactory = XPathFactory.newInstance(); XPath xpath = xFactory.newXPath(); expr = xpath.compile("//html/body/instance/text()"); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; instanceId = Integer.parseInt(nodes.item(0).getNodeValue()); } catch (Exception e) { e.printStackTrace(); throw new TestExecutionException(e); } String experimentURL = this.configurationManager.getUrlTester() + instanceId + "/WaitEndOfTheRun/SystemInput/URI"; logger.debug("RETRIEVE THE TARGT URL FROM EXPERIMENT URL: " + experimentURL); String _endOfExperimentURL = getURL(experimentURL); try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder; Document doc = null; XPathExpression expr = null; builder = factory.newDocumentBuilder(); doc = builder.parse(new InputSource(new StringReader( _endOfExperimentURL))); XPathFactory xFactory = XPathFactory.newInstance(); XPath xpath = xFactory.newXPath(); expr = xpath.compile("//HTML/BODY//text()"); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; String endOfExperimentURL = nodes.item(0).getNodeValue(); logger.debug("END EXPERIMENT URL IS: " + endOfExperimentURL); HttpClient client = new HttpClient(); PostMethod formSubmission = new PostMethod(endOfExperimentURL); try { int statusCode = client.executeMethod(formSubmission); if (!(statusCode >= 200 && statusCode < 300)) { throw new RuntimeException("Method failed: " + formSubmission.getStatusLine()); } } catch (HttpException e) { logger.error("Fatal protocol violation: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { logger.error("Fatal transport error: " + e.getMessage()); e.printStackTrace(); } finally { formSubmission.releaseConnection(); } } catch (XPathExpressionException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NumberFormatException e) { e.printStackTrace(); } } public TestResult executeTest(Test test) throws TestExecutionException { String testStartResponse = startTest(test); if (testStartResponse == null) { throw new TestExecutionException("Cannot start test " + test); } try { logger.info("BasicRunner.executeTest() with timeout " + (timeoutMillis / (60 * 1000)) + " mins"); waitForCompletion(test, testStartResponse); } catch (TestTimeoutException e) { e.printStackTrace(); // This may fail simply because the experiment is alreay in the // undeployStage try { forceUndeploy(test, testStartResponse); } catch (Throwable ee) { logger.info("SILENT Exception. " + ee.getMessage()); } throw new TestExecutionException(e); } TestResult testResult = TestResult.newTestResult(test, configurationManager.getCustomerName(), configurationManager.getServiceName()); // Here we can INJECT the pipeline/chain of data collectors. // All the data are downloaded somewhere by each of the contributes // service // and assertions and other services will find them try { dataCollector.collectDataForExperiment(test, testStartResponse, testResult); } catch (RuntimeException e) { if (e.getMessage().contains( "FATAL: Database cannot be created or read !")) { // Rethrow this logger.error("BasicRunner.executeTest() FATAL !"); throw e; } else { throw new TestExecutionException("While collecting data", e); } } return testResult; } private void waitForCompletion(Test test, String startPage) throws TestExecutionException { final long pollingStarted = System.currentTimeMillis(); int instanceId = -1; try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder; Document doc = null; XPathExpression expr = null; builder = factory.newDocumentBuilder(); doc = builder.parse(new InputSource(new StringReader(startPage))); XPathFactory xFactory = XPathFactory.newInstance(); XPath xpath = xFactory.newXPath(); expr = xpath.compile("//html/body/instance/text()"); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; instanceId = Integer.parseInt(nodes.item(0).getNodeValue()); } catch (Exception e) { e.printStackTrace(); throw new TestExecutionException(e); } String stateUrl = this.configurationManager.getUrlTester() + instanceId + "/0/System/STATE"; logger.debug("STATE URL: " + stateUrl); long currentTime = System.currentTimeMillis(); while ((currentTime - pollingStarted) <= timeoutMillis) { try { Thread.sleep(POLLING_INTERVAL); } catch (InterruptedException e) { e.printStackTrace(); throw new TestExecutionException(e); } String currentStateAnswer = getURL(stateUrl); try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder; Document doc = null; XPathExpression expr = null; builder = factory.newDocumentBuilder(); doc = builder.parse(new InputSource(new StringReader( currentStateAnswer))); XPathFactory xFactory = XPathFactory.newInstance(); XPath xpath = xFactory.newXPath(); expr = xpath.compile("//HTML/BODY//text()"); Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList nodes = (NodeList) result; int currentState = Integer.parseInt(nodes.item(0) .getNodeValue()); if (currentState != 3) { logger.debug("STATE: " + currentState); } if (currentState == 4) { return; } else if (currentState == 8) { throw new TestExecutionException( "Test Failed with status 8. STOP THE TEST"); } else if (currentState == 27) { throw new TestExecutionException( "Test Failed with status 27"); } // Update the time currentTime = System.currentTimeMillis(); } catch (Exception e) { e.printStackTrace(); throw new TestExecutionException(e); } } throw new TestTimeoutException(); } private String startTest(Test test) { HttpClient client = new HttpClient(); // long currentTime = System.currentTimeMillis(); PostMethod formSubmission = new PostMethod( configurationManager.getUrlTester()); formSubmission.addParameter("Action", "run"); formSubmission.addParameter("customerName", configurationManager.getCustomerName()); // configurationManager.getServiceName() + "" + "" + (currentTime%1000) // formSubmission.addParameter("serviceName", "S" + (test.getId() % // 100)); // TODO Use alsways the very same name otherwise we run out of security // groups! String serviceName = configurationManager.getServiceName(); if (serviceName.length() > 3) { serviceName = serviceName.substring(0, 2); } formSubmission.addParameter("serviceName", serviceName); formSubmission.addParameter("traceURI", test.getTraceURL()); formSubmission.addParameter("manifestURI", test.getManifestURL()); formSubmission.addParameter("clientsURI", test.getClientsURL()); try { int statusCode = client.executeMethod(formSubmission); if (!(statusCode >= 200 && statusCode < 300)) { throw new RuntimeException("Method failed: " + formSubmission.getStatusLine()); } byte[] responseBody = formSubmission.getResponseBody(); return new String(responseBody); } catch (HttpException e) { System.err.println("Fatal protocol violation: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.err.println("Fatal transport error: " + e.getMessage()); e.printStackTrace(); } finally { formSubmission.releaseConnection(); } return null; } // Move to a service and inject it private String getURL(String url) { HttpClient client = new HttpClient(); try { GetMethod getMethod = new GetMethod(url); int statusCode = client.executeMethod(getMethod); if (!(statusCode >= 200 && statusCode < 300)) {// != // HttpStatus.SC_OK throw new RuntimeException("Method failed with code " + statusCode + ": " + getMethod.getStatusLine() + "\nURL: " + url); } return new String(getMethod.getResponseBody()); } catch (HttpException e) { System.err.println("Fatal protocol violation: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.err.println("Fatal transport error: " + e.getMessage()); e.printStackTrace(); } return null; } }