All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kg.apc.jmeter.threads.UltimateThreadGroup Maven / Gradle / Ivy

package kg.apc.jmeter.threads;

import kg.apc.jmeter.JMeterPluginsUtils;
import org.apache.jmeter.gui.util.PowerTableModel;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.NullProperty;
import org.apache.jmeter.testelement.property.PropertyIterator;
import org.apache.jmeter.threads.JMeterThread;
import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class UltimateThreadGroup
        extends AbstractSimpleThreadGroup
        implements Serializable, TestStateListener {

    private static final Logger log = LoggerFactory.getLogger(UltimateThreadGroup.class);

    public static final String DATA_PROPERTY = "ultimatethreadgroupdata";
    public static final String EXTERNAL_DATA_PROPERTY = "threads_schedule";

    public static final int START_THREADS_CNT_FIELD_NO = 0;
    public static final int INIT_DELAY_FIELD_NO = 1;
    public static final int STARTUP_TIME_FIELD_NO = 2;
    public static final int HOLD_LOAD_FOR_FIELD_NO = 3;
    public static final int SHUTDOWN_TIME_FIELD_NO = 4;

    private PropertyIterator scheduleIT;
    private int threadsToSchedule;
    private CollectionProperty currentRecord;

    public UltimateThreadGroup() {
        super();
    }

    @Override
    protected void scheduleThread(JMeterThread thread, long tgStartTime) {
        log.debug("Scheduling thread: " + thread.getThreadName());
        if (threadsToSchedule < 1) {
            if (!scheduleIT.hasNext()) {
                throw new RuntimeException("Not enough schedule records for thread #" + thread.getThreadName());
            }

            currentRecord = (CollectionProperty) scheduleIT.next();
            threadsToSchedule = currentRecord.get(0).getIntValue();
        }

        int numThreads = currentRecord.get(START_THREADS_CNT_FIELD_NO).getIntValue();
        int initialDelay = currentRecord.get(INIT_DELAY_FIELD_NO).getIntValue();
        int startRampUp = currentRecord.get(STARTUP_TIME_FIELD_NO).getIntValue();
        int flightTime = currentRecord.get(HOLD_LOAD_FOR_FIELD_NO).getIntValue();
        int endRampUp = currentRecord.get(SHUTDOWN_TIME_FIELD_NO).getIntValue();

        long ascentPoint = tgStartTime + 1000 * initialDelay;
        final int rampUpDelayForThread = (int) Math.floor(1000 * startRampUp * (double) threadsToSchedule / numThreads);
        long startTime = ascentPoint + rampUpDelayForThread;
        long descentPoint = startTime + 1000 * flightTime + 1000 * startRampUp - rampUpDelayForThread;

        thread.setStartTime(startTime);
        thread.setEndTime(descentPoint + (int) Math.floor(1000 * endRampUp * (double) threadsToSchedule / numThreads));

        thread.setScheduled(true);
        threadsToSchedule--;
    }

    public JMeterProperty getData() {
        JMeterProperty brokenProp = getProperty(EXTERNAL_DATA_PROPERTY);
        JMeterProperty usualProp = getProperty(DATA_PROPERTY);

        if (brokenProp instanceof CollectionProperty) {
            if (usualProp == null || usualProp instanceof NullProperty) {
                log.warn("Copying '" + EXTERNAL_DATA_PROPERTY + "' into '" + DATA_PROPERTY + "'");
                JMeterProperty newProp = brokenProp.clone();
                newProp.setName(DATA_PROPERTY);
                setProperty(newProp);
            }
            log.warn("Removing property '" + EXTERNAL_DATA_PROPERTY + "' as invalid");
            removeProperty(EXTERNAL_DATA_PROPERTY);
        }

        //log.info("getData: "+getProperty(DATA_PROPERTY));
        CollectionProperty overrideProp = getLoadFromExternalProperty();
        if (overrideProp != null) {
            return overrideProp;
        }

        return getProperty(DATA_PROPERTY);
    }

    public void setData(CollectionProperty rows) {
        //log.info("setData");
        setProperty(rows);
    }


    private CollectionProperty getLoadFromExternalProperty() {
        String loadProp = JMeterUtils.getProperty(EXTERNAL_DATA_PROPERTY);
        log.debug("Profile prop: " + loadProp);
        if (loadProp != null && loadProp.length() > 0) {
            //expected format : threads_schedule="spawn(1,1s,1s,1s,1s) spawn(2,1s,3s,1s,2s)"
            log.info("GUI threads profile will be ignored");
            PowerTableModel dataModel = new PowerTableModel(UltimateThreadGroupGui.columnIdentifiers, UltimateThreadGroupGui.columnClasses);
            String[] chunks = loadProp.split("\\)");

            for (String chunk : chunks) {
                try {
                    parseChunk(chunk, dataModel);
                } catch (RuntimeException e) {
                    log.warn("Wrong  chunk ignored: " + chunk, e);
                }
            }

            log.info("Setting threads profile from property " + EXTERNAL_DATA_PROPERTY + ": " + loadProp);
            return JMeterPluginsUtils.tableModelRowsToCollectionProperty(dataModel, UltimateThreadGroup.DATA_PROPERTY);
        }
        return null;
    }

    private static void parseChunk(String chunk, PowerTableModel model) {
        log.debug("Parsing chunk: " + chunk);
        String[] parts = chunk.split("[(,]");
        String loadVar = parts[0].trim();

        if (loadVar.equalsIgnoreCase("spawn")) {
            Integer[] row = new Integer[5];
            row[START_THREADS_CNT_FIELD_NO] = Integer.parseInt(parts[1].trim());
            row[INIT_DELAY_FIELD_NO] = JMeterPluginsUtils.getSecondsForShortString(parts[2]);
            row[STARTUP_TIME_FIELD_NO] = JMeterPluginsUtils.getSecondsForShortString(parts[3]);
            row[HOLD_LOAD_FOR_FIELD_NO] = JMeterPluginsUtils.getSecondsForShortString(parts[4]);
            row[SHUTDOWN_TIME_FIELD_NO] = JMeterPluginsUtils.getSecondsForShortString(parts[5]);
            model.addRow(row);
        } else {
            throw new RuntimeException("Unknown load type: " + parts[0]);
        }
    }

    @Override
    public int getNumThreads() {
        int result = 0;

        JMeterProperty threadValues = getData();
        if (!(threadValues instanceof NullProperty)) {
            CollectionProperty columns = (CollectionProperty) threadValues;
            List rows = (List) columns.getObjectValue();
            for (Object row1 : rows) {
                CollectionProperty prop = (CollectionProperty) row1;
                ArrayList row = (ArrayList) prop.getObjectValue();
                //log.info(prop.getStringValue());
                result += row.get(0).getIntValue();
            }
        }

        return result;
    }

    @Override
    public void testStarted() {
        JMeterProperty data = getData();
        if (!(data instanceof NullProperty)) {
            scheduleIT = ((CollectionProperty) data).iterator();
        }
        threadsToSchedule = 0;
    }

    @Override
    public void testStarted(String host) {
        testStarted();
    }

    @Override
    public void testEnded() {
    }

    @Override
    public void testEnded(String host) {
        testEnded();
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy