package betsy.tools;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.xml.bind.JAXBException;
import betsy.Main;
import betsy.bpel.BPELMain;
import betsy.bpel.soapui.SoapUIShutdownHelper;
import betsy.common.tasks.FileTasks;
import betsy.common.timeouts.CSV;
import betsy.common.timeouts.Properties;
import betsy.common.timeouts.TimeoutIOOperations;
import betsy.common.timeouts.calibration.CalibrationTimeout;
import betsy.common.timeouts.calibration.CalibrationTimeoutRepository;
import betsy.common.timeouts.timeout.Timeout;
import betsy.common.timeouts.timeout.TimeoutRepository;
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;
/**
* @author Christoph Broeker
* @version 1.0
*/
public class TimeoutCalibrator {
private static final Logger LOGGER = Logger.getLogger(TimeoutCalibrator.class);
public static void main(String[] args) throws JAXBException, IOException, SAXException {
calibrateTimeouts(args);
}
private static void calibrateTimeouts(String[] args) throws JAXBException, IOException, SAXException {
LOGGER.info("Calibration is started.");
//If it's true, SoapUI turns off after first run
BPELMain.shutdownSoapUiAfterCompletion(false);
int numberOfDuration = 0;
boolean isCalibrated = false;
Path properties = Paths.get("timeout.properties");
Path csv = Paths.get("calibration_timeouts.csv");
FileTasks.deleteFile(csv);
HashMap<String, CalibrationTimeout> timeouts = new HashMap<>();
while (numberOfDuration < 4) {
//clean the calibrationTimeoutRepository for the next run
CalibrationTimeoutRepository.clean();
//execute betsy
Main.main(addChangedTestFolderToArgs(args, numberOfDuration));
//get used timeouts
timeouts = CalibrationTimeoutRepository.getAllNonRedundantTimeouts();
//evaluate the timeouts
if (evaluateTimeouts(timeouts)) {
if (numberOfDuration < 1 && !TimeoutIOOperations.testsAreCorrect("test/test" + numberOfDuration)) {
SoapUIShutdownHelper.shutdownSoapUIForReal();
LOGGER.info("Calibration finished, because tests failed.");
break;
}else if(numberOfDuration > 0){
//write all timeouts to csv for traceability
CalibrationTimeoutRepository.writeToCSV(csv, numberOfDuration++);
}
}else{
SoapUIShutdownHelper.shutdownSoapUIForReal();
LOGGER.info("Calibration finished, because timeouts exceeded.");
break;
}
numberOfDuration++;
}
while (!isCalibrated && numberOfDuration > 3) {
//get used timeouts
timeouts = CalibrationTimeoutRepository.getAllNonRedundantTimeouts();
//determine the timeouts
timeouts = determineTimeouts(timeouts, csv);
//write all timeouts to the console
for (CalibrationTimeout timeout : timeouts.values()) {
LOGGER.info(timeout.getKey() + " " + timeout.getStatus() + " " + timeout.getTimeoutInMs());
}
//set all values to the repositories
TimeoutRepository.setAllCalibrationTimeouts(timeouts);
//write timeouts to properties
Properties.write(properties, new ArrayList<>(timeouts.values()));
//clean the calibrationTimeoutRepository for the next run
CalibrationTimeoutRepository.clean();
//execute betsy
Main.main(addChangedTestFolderToArgs(args, numberOfDuration));
//write all timeouts to csv
CalibrationTimeoutRepository.writeToCSV(csv, numberOfDuration++);
if (evaluateTimeouts(timeouts)) {
isCalibrated = true;
//shutdown SoapUI
SoapUIShutdownHelper.shutdownSoapUIForReal();
LOGGER.info("Calibration is finished.");
} else {
isCalibrated = false;
}
}
for (CalibrationTimeout timeout : timeouts.values()) {
LOGGER.info(timeout.getKey() + " " + timeout.getStatus() + " " + timeout.getTimeoutInMs());
}
}
/**
* The method extended the args with the argument for test folder.
*
* @param args The arguments to extend.
* @param testFolderNumber The number of the folder (test1, test2...).
* @return Returns the extended arguments.
*/
public static String[] addChangedTestFolderToArgs(String args[], int testFolderNumber) {
Objects.requireNonNull(args, "The arguments can't be null.");
String[] destination = new String[args.length + 1];
if (args.length > 0) {
System.arraycopy(args, 0, destination, 0, 1);
destination[1] = "-f" + "test/test" + testFolderNumber;
System.arraycopy(args, 1, destination, 2, args.length - 1);
} else {
LOGGER.info("Can't add test folder to args, because the args aren't greater than null.");
}
return destination;
}
/**
* This method evaluates the the given timeouts. If one of the timeouts has the status exceeded, the method returns false.
*
* @param timeouts The timeouts to evaluate.
* @return The result of the evaluation
*/
public static boolean evaluateTimeouts(HashMap<String, CalibrationTimeout> timeouts) {
Objects.requireNonNull(timeouts, "The timeouts can't be null.");
boolean allTimeoutsAreCalibrated = true;
for (CalibrationTimeout timeout : timeouts.values()) {
if (timeout.getStatus() == CalibrationTimeout.Status.EXCEEDED) {
allTimeoutsAreCalibrated = false;
}
}
return allTimeoutsAreCalibrated;
}
/**
* This method determines the values of the given timeouts. The calculated value of the timeout consists of
* the expectation and the standardDeviation based on the values of the csv file.
*
* @param timeouts The timeouts, which should be calculated.
* @param csv The csv path with the timeouts for calculation.
* @return Returns the new determined timeouts.
*/
public static HashMap<String, CalibrationTimeout> determineTimeouts(HashMap<String, CalibrationTimeout> timeouts, Path csv) {
Objects.requireNonNull(timeouts, "The timeouts can't be null.");
Objects.requireNonNull(csv, "The csv file can't be null.");
if (timeouts.size() > 0) {
for (CalibrationTimeout timeout : timeouts.values()) {
timeout.setValue(calculateTimeout(timeout, 2, csv));
}
} else {
LOGGER.info("The number of the timeouts has to be greater than null to determine the timeouts.");
}
return timeouts;
}
/**
* This method calculates the expectation of the given timeouts.
*
* @param timeouts The timeouts for calculation of the expectation.
* @return Returns the calculated expectation.
*/
public static int calculateExpectation(List<CalibrationTimeout> timeouts) {
if (timeouts.size() > 0) {
Objects.requireNonNull(timeouts, "The timeouts can't be null.");
int expectation = 0;
for (CalibrationTimeout timeoutValue : timeouts) {
expectation = expectation + timeoutValue.getMeasuredTime();
}
return expectation / timeouts.size();
} else {
LOGGER.info("The number of the timeouts has to be greater than null to calculate the expectation.");
return 0;
}
}
/**
* This method calculates the variance for the given timeouts.
*
* @param timeouts The timeouts for calculating the variance.
* @param expectation The expectation of the timeouts.
* @return Returns the calculated variance.
*/
public static double calculateVariance(List<CalibrationTimeout> timeouts, int expectation) {
if (timeouts.size() > 0) {
Objects.requireNonNull(timeouts, "The timeouts can't be null.");
double standardVariance = 0;
for (CalibrationTimeout timeoutValue : timeouts) {
standardVariance = standardVariance + Math.pow(timeoutValue.getMeasuredTime() - expectation, 2);
}
return standardVariance / timeouts.size();
} else {
LOGGER.info("The number of the timeouts has to be greater than null to calculate the variance.");
return 0;
}
}
/**
* This method calculates the standardDeviation of given
*
* @param timeouts The timeouts for calculating the standardDeviation
* @param expectation The expectation of the timeouts.
* @return Returns the calculated standardDeviation.
*/
public static double standardDeviation(List<CalibrationTimeout> timeouts, int expectation) {
if (timeouts.size() > 0) {
Objects.requireNonNull(timeouts, "The timeouts can't be null.");
return Math.sqrt(calculateVariance(timeouts, expectation));
} else {
LOGGER.info("The number of the timeouts has to be greater than null to calculate the standardDeviation.");
return 0;
}
}
/**
* This method calculates the timeout based on the timeouts values in the csv. The calculated timeout consists of the
* expectation and the x-fold of the standardDeviation.
*
* @param timeout The {@link Timeout}, which should be calculated.
* @param standardDeviationMultiplier The value indicates, how often the standardDeviation is multiplied.
* @param csv The csv path withe the timeout values.
* @return returns the calculated timeout value.
*/
public static int calculateTimeout(Timeout timeout, int standardDeviationMultiplier, Path csv) {
Objects.requireNonNull(timeout, "The timeout can't be null.");
Objects.requireNonNull(csv, "The csv file can't be null.");
List<CalibrationTimeout> timeouts = CSV.read(csv).stream().filter(actualTimeout -> actualTimeout.getKey().equals(timeout.getKey())).collect(Collectors.toList());
int expectation = calculateExpectation(timeouts);
return expectation + (standardDeviationMultiplier * new Double(standardDeviation(timeouts, expectation)).intValue());
}
}