/*
* Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.automation.extensions.jmeter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jmeter.JMeter;
import org.apache.jmeter.engine.StandardJMeterEngine;
import org.apache.jmeter.util.ShutdownClient;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.wso2.carbon.automation.engine.exceptions.AutomationFrameworkException;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
public class JMeterTestManager {
private static final Pattern PAT_ERROR = Pattern.compile(".*\\s+ERROR\\s+.*");
private static final Log log = LogFactory.getLog(JMeterTestManager.class);
private String jmeterLogLevel = "INFO";
private File testFile = null;
private File jmeterHome = null;
private File jmeterLogFile = null;
private DateFormat fmt = new SimpleDateFormat("yyMMdd-HH-mm-ss");
private File jmeterProps = null;
public void runTest(JMeterTest jMeterTest)
throws AutomationFrameworkException {
JMeterResult results;
// Init JMeter
jmeterHome = JMeterInstallationProvider.getInstance().getJMeterHome();
testFile = jMeterTest.getTestFile();
//setting jmeter.properties file parameter
try {
setJMeterPropertyFile(jMeterTest);
} catch (IOException e) {
throw new AutomationFrameworkException("Set property failed " + e.getMessage(), e);
}
if (jMeterTest.getLogLevel() != null) {
jmeterLogLevel = jMeterTest.getLogLevel();
}
results = executeMe();
// checkForErrors();
log.info("for more info. " + results.getFileName());
if (results.getErrorCount() > 0) {
throw new AutomationFrameworkException("Test Failed. " + results.getErrorCount() + " Error/s Found.\n"
+ results.getErrorList().toString() + "\nRefer "
+ results.getFileName() + " for test result");
}
if (results.getFailureCount() > 0) {
throw new AssertionError("Test Failed. " + results.getFailureCount() + " Assertion Failure/s.\n"
+ results.getAssertList().toString() + "\nRefer "
+ results.getFileName() + " for test result");
}
}
private JMeterResult executeMe() throws AutomationFrameworkException {
try {
addLogFile(testFile.getName());
} catch (IOException e) {
throw new AutomationFrameworkException("Can't add log file " + e.getMessage(), e);
}
Boolean resultState = true;
JMeterResult results;
String resultFile = executeTest(testFile);
try {
// Force shutdown
StandardJMeterEngine.stopEngineNow();
ShutdownClient.main(new String[]{"Shutdown"});
} catch (IOException ex) {
log.error(ex);
resultState = false;
}
results = resultValidator(resultFile);
results.setFileName(resultFile);
results.setExecutionState(resultState);
return results;
}
private void checkForErrors() throws AutomationFrameworkException, IOException {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(jmeterLogFile), Charset.defaultCharset()));
String line;
while ((line = in.readLine()) != null) {
if (PAT_ERROR.matcher(line).find()) {
throw new AutomationFrameworkException("There were test errors, see logfile '" + jmeterLogFile + "' for further information");
}
}
in.close();
} catch (IOException e) {
throw new IOException("Can't read log file", e);
}
}
private void setJMeterPropertyFile(JMeterTest jMeterTest) throws IOException {
if (jMeterTest.getJMeterPropertyFile() == null) {
log.info("Loading default jmeter.properties...");
jmeterProps = JMeterInstallationProvider.getInstance().getJMeterPropertyFile();
System.setProperty("jmeter_properties",
File.separator + "bin" + File.separator + "jmeter.properties");
} else {
log.info("Loading custom jmeter.properties from " + jMeterTest.getJMeterPropertyFile().getCanonicalPath());
jmeterProps = jMeterTest.getJMeterPropertyFile();
System.setProperty("jmeter_properties", jmeterProps.getCanonicalPath());
}
}
private String executeTest(File test) throws AutomationFrameworkException {
String reportFileName;
String reportFileFullPath;
JMeter jmeterInstance = new JMeter();
try {
log.info("Executing test: " + test.getCanonicalPath());
reportFileName = test.getName().substring(0,
test.getName().lastIndexOf(".")) + "-"
+ fmt.format(new Date()) + ".jmeterResult" + ".jtl";
File reportDir = JMeterInstallationProvider.getInstance().getReportDir();
reportFileFullPath = reportDir.toString() + File.separator + reportFileName;
List<String> argsTmp = Arrays.asList("-n",
"-t", test.getCanonicalPath(),
"-l", reportDir.toString() + File.separator + reportFileName,
"-p", jmeterProps.toString(),
"-d", jmeterHome.getCanonicalPath(),
"-L", "jorphan=" + jmeterLogLevel,
"-L", "jmeter.util=" + jmeterLogLevel);
List<String> args = new ArrayList<String>();
args.addAll(argsTmp);
SecurityManager oldManager = System.getSecurityManager();
UncaughtExceptionHandler oldHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
if (e instanceof ExitException && ((ExitException) e).getCode() == 0) {
return; //Ignore
}
log.error("Error in thread " + t.getName());
}
});
try {
logParamsAndProps(args);
jmeterInstance.start(args.toArray(new String[]{}));
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(jmeterLogFile), Charset.defaultCharset()));
while (!checkForEndOfTest(in)) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
} catch (ExitException e) {
if (e.getCode() != 0) {
throw new AutomationFrameworkException("Test failed " + e.getMessage(), e);
}
} catch (Exception e) {
log.error(e);
} finally {
System.setSecurityManager(oldManager);
Thread.setDefaultUncaughtExceptionHandler(oldHandler);
}
} catch (IOException e) {
throw new AutomationFrameworkException("Can't execute test " + e.getMessage(), e);
}
return reportFileFullPath;
}
private void logParamsAndProps(List<String> args) {
log.debug("Starting JMeter with the following parameters:");
for (String arg : args) {
log.debug(arg);
}
Properties props = System.getProperties();
Set<Object> keysUnsorted = props.keySet();
SortedSet<Object> keys = new TreeSet<Object>(keysUnsorted);
log.debug("... and the following properties:");
for (Object k : keys) {
String key = (String) k;
String value = props.getProperty(key);
log.debug(key + " = " + value);
}
}
private JMeterResult resultValidator(String fileName) throws AutomationFrameworkException {
JMeterResult result = null;
XMLStreamReader parser = null;
FileInputStream inputStream = null;
File file = new File(fileName);
List<String> assertionFailureList;
List<String> errorList;
if (file.exists()) {
try {
inputStream = new FileInputStream(file);
parser = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(file);
doc.getDocumentElement().normalize();
errorList = new ArrayList<String>();
result = new JMeterResult();
assertionFailureList = new ArrayList<String>();
NodeList nodeList = doc.getElementsByTagName("httpSample");
for (int temp = 0; temp < nodeList.getLength(); temp++) {
String responseMessage;
String label;
String name = null;
String error = null;
String failure = null;
String failureMessage = null;
Node node = nodeList.item(temp);
responseMessage = node.getAttributes().getNamedItem("s").getTextContent();
label = node.getAttributes().getNamedItem("lb").getTextContent();
if ("false".equalsIgnoreCase(responseMessage)) {
result.increaseErrorCount();
String errorMessage = label + " > " + responseMessage;
if (!errorList.contains(errorMessage)) {
errorList.add(errorMessage);
}
}
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
if (element.getElementsByTagName("name").getLength() != 0) {
name = element.getElementsByTagName("name").item(0).getTextContent();
}
if (element.getElementsByTagName("error").getLength() != 0) {
error = element.getElementsByTagName("error").item(0).getTextContent();
}
if (element.getElementsByTagName("failure").getLength() != 0) {
failure = element.getElementsByTagName("failure").item(0).getTextContent();
}
if (element.getElementsByTagName("failureMessage").getLength() != 0) {
failureMessage = element.getElementsByTagName("failureMessage").item(0).getTextContent();
}
if ("true".equalsIgnoreCase(failure) || "true".equalsIgnoreCase(error)) {
result.increaseFailureCount();
String assertionFailure = label + " : " + name + " > " + failureMessage;
if (!assertionFailureList.contains(assertionFailure)) {
assertionFailureList.add(assertionFailure);
}
}
}
}
result.setErrorList(errorList);
result.setAssertList(assertionFailureList);
} catch (FileNotFoundException e) {
throw new AutomationFrameworkException("Result File is not Created");
} catch (ParserConfigurationException e) {
throw new AutomationFrameworkException("Result File is not Created");
} catch (SAXException e) {
throw new AutomationFrameworkException("Result File is not Created");
} catch (IOException e) {
throw new AutomationFrameworkException("Result File is not Created");
} catch (XMLStreamException e) {
throw new AutomationFrameworkException("Result File is not Created");
} finally {
if (parser != null) {
try {
parser.close();
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
///ignore
}
}
}
} else {
throw new AutomationFrameworkException("Result File is not Created");
}
return result;
}
private boolean checkForEndOfTest(BufferedReader in) throws IOException {
boolean testEnded = false;
try {
String line;
while ((line = in.readLine()) != null) {
if (line.contains("Test has ended")) {
testEnded = true;
break;
}
}
} catch (IOException e) {
throw new IOException("Can't read log file", e);
}
return testEnded;
}
private static class ExitException extends SecurityException {
private static final long serialVersionUID = 5544099211927987521L;
public int _rc;
public ExitException(int rc) {
super(Integer.toString(rc));
_rc = rc;
}
public int getCode() {
return _rc;
}
}
private void addLogFile(String fileName) throws IOException {
jmeterLogFile = new File(JMeterInstallationProvider.getInstance().getLogDir().getCanonicalPath()
+ File.separator + fileName.substring(0, fileName.lastIndexOf(".")) + "-" + fmt.format(new Date()) + ".log");
if (!jmeterLogFile.createNewFile()) {
log.error("unable to create log file");
}
try {
System.setProperty("log_file", jmeterLogFile.getCanonicalPath());
} catch (IOException e) {
throw new IOException("Can't get canonical path for log file " + e.getMessage(), e);
}
}
}