com.smartitengineering.loadtest.engine.impl.LoadTestEngineImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.smartitengineering.loadtest.engine.impl.LoadTestEngineImpl.java

Source

/**
 *    This module represents an engine IMPL for the load testing framework
 *    Copyright (C) 2008  Imran M Yousuf (imran@smartitengineering.com)
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.smartitengineering.loadtest.engine.impl;

import com.smartitengineering.loadtest.engine.TestCase;
import com.smartitengineering.loadtest.engine.UnitTestInstance;
import com.smartitengineering.loadtest.engine.events.BatchEvent;
import com.smartitengineering.loadtest.engine.events.TestCaseBatchListener;
import com.smartitengineering.loadtest.engine.events.TestCaseStateChangeListener;
import com.smartitengineering.loadtest.engine.events.TestCaseStateChangedEvent;
import com.smartitengineering.loadtest.engine.events.TestCaseTransitionListener;
import com.smartitengineering.loadtest.engine.management.TestCaseBatchCreator;
import com.smartitengineering.loadtest.engine.management.TestCaseBatchCreator.Batch;
import com.smartitengineering.loadtest.engine.result.KeyedInformation;
import com.smartitengineering.loadtest.engine.result.TestCaseInstanceResult;
import com.smartitengineering.loadtest.engine.result.TestCaseResult;
import com.smartitengineering.loadtest.engine.result.TestProperty;
import com.smartitengineering.loadtest.engine.result.TestResult;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import org.apache.commons.lang.mutable.MutableInt;

/**
 * Default implementation of the LoadTestEngine
 *
 */
public class LoadTestEngineImpl extends AbstractLoadTestEngineImpl {

    protected EngineBatchListener engineBatchListener;
    private Map<TestCaseBatchCreator, UnitTestInstance> creators;
    private Map<UnitTestInstance, UnitTestInstanceRecord> instanceRecords;
    private Map<TestCase, UnitTestInstanceRecord> caseRecords;
    private Semaphore semaphore;
    private EngineJobFinishedDetector finishedDetector;
    private ExecutorService executorService;
    private TestCaseTransitionListener transitionListener;
    private TestCaseStateChangeListener caseStateChangeListener;
    private TestResult result;

    @Override
    protected void initializeBeforeCreatedState() {
        setTestInstances(new HashSet<UnitTestInstance>());
        creators = new HashMap<TestCaseBatchCreator, UnitTestInstance>();
        instanceRecords = new HashMap<UnitTestInstance, UnitTestInstanceRecord>();
    }

    public void init(String testName, Set<UnitTestInstance> testInstances, Properties initProperties)
            throws IllegalArgumentException, IllegalStateException {
        if (getState().getStateStep() != State.CREATED.getStateStep()) {
            throw new IllegalStateException();
        }
        if (testName == null || testName.length() <= 0) {
            throw new IllegalArgumentException();
        }
        if (testInstances == null || testInstances.isEmpty()) {
            throw new IllegalArgumentException();
        }

        if (initProperties != null && !initProperties.isEmpty()) {
            if (initProperties.containsKey(PROPS_PERMIT_KEY)) {
                try {
                    setPermits(Integer.parseInt(initProperties.getProperty(PROPS_PERMIT_KEY)));
                } catch (Exception ex) {
                    ex.printStackTrace();
                    setPermits(-1);
                }
            }
        }
        initializeInternals(testName);
        initializeFinishDetector();
        initializeResult(testName);
        initializeTestInstances(testInstances);

        setState(State.INITIALIZED);
    }

    public void start() throws IllegalStateException {
        if (getState().getStateStep() != State.INITIALIZED.getStateStep()) {
            throw new IllegalStateException();
        }
        getTestCaseThreadManager().startManager();
        final Set<TestCaseBatchCreator> allBatchCreators = getAllBatchCreators();
        for (TestCaseBatchCreator creator : allBatchCreators) {
            creator.start();
        }
        setState(State.STARTED);
        result.setStartDateTime(getStartTime());
    }

    public TestResult getTestResult() throws IllegalStateException {
        if (getState().getStateStep() < State.FINISHED.getStateStep()) {
            throw new IllegalStateException();
        }
        if (result.isValid()) {
            return result;
        } else {
            throw new IllegalStateException("Test result in invalid state");
        }
    }

    @Override
    protected void rollBackToCreatedState() {
        getTestInstances().clear();
        setTestName(null);
        semaphore = null;
        engineBatchListener = null;
        finishedDetector = null;
        executorService = null;
        caseRecords = null;
        caseStateChangeListener = null;
        removeTestCaseTransitionListener(transitionListener);
        transitionListener = null;
        result = null;
        instanceRecords.clear();
        creators.clear();
    }

    private void initializeFinishDetector() {

        finishedDetector = new EngineJobFinishedDetector();
        executorService = Executors.newSingleThreadExecutor();
    }

    private void initializeInternals(String testName) {

        engineBatchListener = new EngineBatchListener();
        semaphore = new Semaphore(getPermits());
        transitionListener = new TestCaseStateTransitionMonitor();
        caseStateChangeListener = new TestCaseStateListenerImpl();
        caseRecords = new HashMap<TestCase, UnitTestInstanceRecord>();
        setTestName(testName);
        addTestCaseTransitionListener(transitionListener);
    }

    private void initializeResult(String testName) {

        result = new TestResult();
        result.setTestName(testName);
        HashSet<TestCaseResult> resultSet = new HashSet<TestCaseResult>();
        result.setTestCaseRunResults(resultSet);
    }

    private void initializeTestInstances(Set<UnitTestInstance> testInstances) {

        getTestInstances().addAll(testInstances);
        for (UnitTestInstance instance : getTestInstances()) {
            registerToBatchCreator(instance);
            createTestCaseResult(instance);
        }
    }

    private void createTestCaseResult(UnitTestInstance instance) {
        TestCaseResult caseResult = new TestCaseResult();
        caseResult.setName(instance.getName());
        caseResult.setInstanceFactoryClassName(instance.getInstanceFactoryClassName());
        final Properties testProperties = instance.getProperties();
        Set<TestProperty> testPropertySet = new HashSet<TestProperty>(testProperties.size());
        final Iterator<Object> keySetIterator = testProperties.keySet().iterator();
        while (keySetIterator.hasNext()) {
            TestProperty property = new TestProperty();
            final String key = keySetIterator.next().toString();
            property.setKey(key);
            property.setValue(testProperties.getProperty(key));
            testPropertySet.add(property);
        }
        caseResult.setTestProperties(testPropertySet);
        caseResult.setTestCaseInstanceResults(new HashSet<TestCaseInstanceResult>());
        UnitTestInstanceRecord record = new UnitTestInstanceRecord(caseResult);
        instanceRecords.put(instance, record);
    }

    private void registerToBatchCreator(UnitTestInstance instance) {
        try {
            TestCaseBatchCreator creator = getTestCaseBatchCreatorInstance();
            creator.init(instance);
            creator.addBatchCreatorListener(engineBatchListener);
            creators.put(creator, instance);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    protected class EngineBatchListener implements TestCaseBatchListener {

        public void batchAvailable(BatchEvent event) {
            boolean acquired = false;
            try {
                semaphore.acquire();
                acquired = true;
                Batch batch;
                Map.Entry<ThreadGroup, Map<Thread, TestCase>> batchThreads;
                batch = event.getBatch();
                if (batch != null) {
                    batchThreads = batch.getBatch();
                    for (Map.Entry<Thread, TestCase> thread : batchThreads.getValue().entrySet()) {
                        if (thread.getKey() != null && thread.getValue() != null) {
                            thread.getKey().start();
                            thread.getValue().addTestCaseStateChangeListener(caseStateChangeListener);
                            getTestCaseThreadManager().manageThread(thread.getKey(), thread.getValue());
                            final TestCaseBatchCreator batchCreator = batch.getBatchCreator();
                            if (batchCreator != null) {
                                final UnitTestInstance testInstance = creators.get(batchCreator);
                                if (testInstance != null) {
                                    final UnitTestInstanceRecord instanceRecord = instanceRecords.get(testInstance);
                                    if (instanceRecord != null) {
                                        instanceRecord.incrementCount();
                                        caseRecords.put(thread.getValue(), instanceRecord);
                                    }
                                }
                            }
                        }
                    }
                    batch.setBatchStarted();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                if (acquired) {
                    semaphore.release();
                    acquired = false;
                }
            }
        }

        public void batchCreationEnded(BatchEvent event) {
            if (event.getBatch() != null && event.getBatch().getBatchCreator() != null) {
                UnitTestInstance testInstance = creators.get(event.getBatch().getBatchCreator());
                if (testInstance != null) {
                    UnitTestInstanceRecord record = instanceRecords.get(testInstance);
                    record.setIntanceFinished();
                }
            }
        }
    }

    protected class TestCaseStateTransitionMonitor implements TestCaseTransitionListener {

        private MutableInt startedThreadCount;

        public TestCaseStateTransitionMonitor() {
            startedThreadCount = new MutableInt(0);
        }

        public void testCaseInitialized(TestCaseStateChangedEvent event) {
        }

        public void testCaseStarted(TestCaseStateChangedEvent event) {
            synchronized (startedThreadCount) {
                startedThreadCount.setValue(startedThreadCount.intValue() + 1);
            }
        }

        public void testCaseFinished(TestCaseStateChangedEvent event) {
            testCaseIsDone(event);
        }

        public void testCaseStopped(TestCaseStateChangedEvent event) {
            testCaseIsDone(event);
        }

        private void testCaseIsDone(TestCaseStateChangedEvent event) {
            UnitTestInstanceRecord record = caseRecords.remove(event.getSource());
            if (record != null) {
                TestCase testCase = event.getSource();
                TestCaseInstanceResult instanceResult = new TestCaseInstanceResult();
                instanceResult.setStartTime(testCase.getStartTimeOfTest());
                instanceResult.setEndTime(testCase.getEndTimeOfTest());
                instanceResult.setEndTestCaseState(testCase.getState());
                instanceResult.setInstanceNumber(record.getTotalTestCaseCount());
                Map<String, String> extraInfo = testCase.getTestCaseResultExtraInfo();
                if (extraInfo != null && !extraInfo.isEmpty()) {
                    Set<Map.Entry<String, String>> entries = extraInfo.entrySet();
                    Set<KeyedInformation> otherInfo = new HashSet<KeyedInformation>(entries.size());
                    for (Map.Entry<String, String> extraInfoEntry : entries) {
                        KeyedInformation information = new KeyedInformation();
                        information.setKey(extraInfoEntry.getKey());
                        information.setValue(extraInfoEntry.getValue());
                        otherInfo.add(information);
                    }
                    instanceResult.setOtherInfomations(otherInfo);
                }
                record.getTestCaseResult().getTestCaseInstanceResults().add(instanceResult);
                record.decrementCount();
                if (record.hasUnitTestInstanceFinished()) {
                    result.getTestCaseRunResults().add(record.getTestCaseResult());
                }
                synchronized (startedThreadCount) {
                    startedThreadCount.setValue(startedThreadCount.intValue() - 1);
                    if (startedThreadCount.intValue() <= 0) {
                        executorService.submit(finishedDetector);
                    }
                }
            }
        }
    }

    protected class TestCaseStateListenerImpl implements TestCaseStateChangeListener {

        public void stateChanged(TestCaseStateChangedEvent stateChangeEvent) {
            fireTestCaseStateTransitionEvent(stateChangeEvent);
        }
    }

    protected class UnitTestInstanceRecord {

        private int testCaseCount;
        private TestCaseResult testCaseResult;
        private boolean instanceFinished;
        private int totalTestCount;

        public UnitTestInstanceRecord(TestCaseResult result) {
            if (result == null) {
                throw new IllegalArgumentException();
            }
            testCaseCount = 0;
            totalTestCount = 0;
            testCaseResult = result;
            instanceFinished = false;
        }

        public synchronized void incrementCount() {
            testCaseCount++;
        }

        public synchronized void decrementCount() {
            testCaseCount--;
        }

        public int getTestCaseCount() {
            return testCaseCount;
        }

        public synchronized int getTotalTestCaseCount() {
            totalTestCount++;
            return totalTestCount;
        }

        public boolean isInstanceFinished() {
            return instanceFinished;
        }

        public synchronized void setIntanceFinished() {
            instanceFinished = true;
        }

        public TestCaseResult getTestCaseResult() {
            return testCaseResult;
        }

        public boolean hasUnitTestInstanceFinished() {
            return isInstanceFinished() && getTestCaseCount() <= 0;
        }
    }

    protected class EngineJobFinishedDetector implements Runnable {

        public void run() {
            ArrayList<UnitTestInstance> toBeDeletedInstances = new ArrayList<UnitTestInstance>();
            for (Map.Entry<UnitTestInstance, UnitTestInstanceRecord> instanceRecord : instanceRecords.entrySet()) {
                if (instanceRecord.getValue() != null && instanceRecord.getValue().hasUnitTestInstanceFinished()) {
                    toBeDeletedInstances.add(instanceRecord.getKey());
                }
            }
            synchronized (instanceRecords) {
                for (UnitTestInstance instance : toBeDeletedInstances) {
                    instanceRecords.remove(instance);
                }
            }
            if (instanceRecords.isEmpty()) {
                setState(State.FINISHED);
                result.setEndDateTime(getEndTime());
            }
        }
    }

    @Override
    protected TestCaseBatchCreator getBatchCreatorForTestInstance(UnitTestInstance instance) {
        if (instance == null) {
            return null;
        }
        for (Map.Entry<TestCaseBatchCreator, UnitTestInstance> entry : creators.entrySet()) {
            if (entry.getValue().equals(instance)) {
                return entry.getKey();
            }
        }
        return null;
    }

    @Override
    protected Set<TestCaseBatchCreator> getAllBatchCreators() {
        final Set<TestCaseBatchCreator> allCreators = creators.keySet();
        if (allCreators == null) {
            return Collections.emptySet();
        }
        return allCreators;
    }

    @Override
    protected Map<UnitTestInstance, TestCaseBatchCreator> getAllBatchCreatorsForTestInstances() {
        Map<UnitTestInstance, TestCaseBatchCreator> resultMap = new HashMap<UnitTestInstance, TestCaseBatchCreator>();
        for (Map.Entry<TestCaseBatchCreator, UnitTestInstance> entry : creators.entrySet()) {
            resultMap.put(entry.getValue(), entry.getKey());
        }
        return resultMap;
    }
}