com.bt.aloha.batchtest.BatchTest.java Source code

Java tutorial

Introduction

Here is the source code for com.bt.aloha.batchtest.BatchTest.java

Source

/*
 * Aloha Open Source SIP Application Server- https://trac.osmosoft.com/Aloha
 *
 * Copyright (c) 2008, British Telecommunications plc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 3.0 of the License, or (at your option) any later
 * version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package com.bt.aloha.batchtest;

import java.util.Hashtable;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.bt.aloha.batchtest.utils.AnnouncementCallScenarioExtractor;
import com.bt.aloha.batchtest.utils.BasicCallAndMediaCallSharedDialogScenarioExtractor;
import com.bt.aloha.batchtest.utils.BasicConferenceScenarioMultiStackExtractor;
import com.bt.aloha.batchtest.utils.ByeScenarioExtractor;
import com.bt.aloha.batchtest.utils.CallAnswerTimeoutScenarioExtractor;
import com.bt.aloha.batchtest.utils.ConferenceScenarioTerminateParticipantsExtractor;
import com.bt.aloha.batchtest.utils.MaxCallDurationScenarioExtractor;
import com.bt.aloha.batchtest.utils.TwoCallsSharingCallLegScenarioExtractor;
import com.bt.aloha.call.CallBeanImpl;
import com.bt.aloha.call.collections.CallCollection;
import com.bt.aloha.callleg.InboundCallLegBeanImpl;
import com.bt.aloha.callleg.OutboundCallLegBeanImpl;
import com.bt.aloha.dialog.DialogJainSipListener;
import com.bt.aloha.dialog.collections.DialogCollection;
import com.bt.aloha.media.conference.collections.ConferenceCollection;
import com.bt.aloha.media.convedia.MediaCallBeanImpl;
import com.bt.aloha.media.convedia.MediaCallLegBeanImpl;
import com.bt.aloha.media.convedia.conference.ConferenceBeanImpl;

public class BatchTest implements BatchTestScenarioResultListener {
    protected MultistackApplicationContextManager manager;
    protected ClassPathXmlApplicationContext applicationContext;
    private static final Log log = LogFactory.getLog(BatchTest.class);
    private Map<String, Double> scenarios = new Hashtable<String, Double>();
    private String audioFileUri;
    private int maximumScenarioCompletionWaitTimeSeconds;
    private int numberOfRuns;
    protected int numberOfConcurrentStarts;
    protected int numberCompleted = 0;
    protected int numberSucceeded = 0;
    private long totalTime = 0;
    private ConcurrentMap<String, Result> results;
    private Map<String, Analysis> analysis = new TreeMap<String, Analysis>();

    private DialogCollectionImplWithStats dialogCollection;
    private CallCollectionImplWithStats callCollection;
    private ConferenceCollectionImplWithStats conferenceCollection;
    protected ExecutorService executorService;
    private int executorServiceNewFixedThreadPool;

    private Vector<String> failedScenarios = new Vector<String>();
    private TwoCallsSharingCallLegScenarioExtractor twoCallsSharingCallLegScenarioExtractor = new TwoCallsSharingCallLegScenarioExtractor(
            "app.log");
    private ConferenceScenarioTerminateParticipantsExtractor conferenceScenarioTerminateParticipantsLogExtractor = new ConferenceScenarioTerminateParticipantsExtractor(
            "app.log");
    private CallAnswerTimeoutScenarioExtractor callAnswerTimeoutScenarioLogExtractor = new CallAnswerTimeoutScenarioExtractor(
            "app.log");
    private BasicCallAndMediaCallSharedDialogScenarioExtractor basicCallAndMediaCallSharedDialogScenarioLogExtractor = new BasicCallAndMediaCallSharedDialogScenarioExtractor(
            "app.log");
    private BasicCallAndMediaCallSharedDialogScenarioExtractor promptAndCollectScenarioLogExtractor = new BasicCallAndMediaCallSharedDialogScenarioExtractor(
            "app.log");
    private BasicConferenceScenarioMultiStackExtractor basicConferenceScenarioMultiStackExtractor = new BasicConferenceScenarioMultiStackExtractor(
            "app.log");
    private AnnouncementCallScenarioExtractor announcementCallScenarioLogExtractor = new AnnouncementCallScenarioExtractor(
            "app.log");
    private MaxCallDurationScenarioExtractor maxCallDurationScenarioLogExtractor = new MaxCallDurationScenarioExtractor(
            "app.log");
    private ByeScenarioExtractor loadBalancedByeScenarioMultistackExtractor = new ByeScenarioExtractor("app.log");

    public void setApplicationContext(ClassPathXmlApplicationContext theApplicationContext) {
        this.applicationContext = theApplicationContext;
    }

    public ClassPathXmlApplicationContext getApplicationContext() {
        return this.applicationContext;
    }

    public void setApplicationContextManager(MultistackApplicationContextManager m) {
        this.manager = m;
    }

    public int getExecutorServiceNewFixedThreadPool() {
        return executorServiceNewFixedThreadPool;
    }

    public void setExecutorServiceNewFixedThreadPool(int theExecutorServiceNewFixedThreadPool) {
        this.executorServiceNewFixedThreadPool = theExecutorServiceNewFixedThreadPool;
    }

    public void init() {
        dialogCollection = new DialogCollectionImplWithStats(
                (DialogCollection) applicationContext.getBean("dialogCollection"));
        callCollection = new CallCollectionImplWithStats(
                (CallCollection) applicationContext.getBean("callCollection"));
        conferenceCollection = new ConferenceCollectionImplWithStats(
                (ConferenceCollection) applicationContext.getBean("conferenceCollection"));
    }

    public void destroy() {
        applicationContext.destroy();
    }

    @SuppressWarnings("unchecked")
    public void reset() {
        this.scenarios = new Hashtable<String, Double>();
        getCallCollection().reset();
        getDialogCollection().reset();
        getConferenceCollection().reset();

        Map<String, Resetable> resetables = (Map<String, Resetable>) applicationContext
                .getBeansOfType(Resetable.class);
        for (Resetable resetable : resetables.values())
            resetable.reset();
    }

    class Result {
        private boolean succeeded;
        private String message;

        public Result(boolean b) {
            this.succeeded = b;
        }

        public Result(String m) {
            this.message = m;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        public boolean isSucceeded() {
            return succeeded;
        }

        public void setSucceeded(boolean succeeded) {
            this.succeeded = succeeded;
        }

        @Override
        public String toString() {
            if (isSucceeded())
                return "succeeded";
            return "failed: " + getMessage();
        }
    }

    class Analysis {
        private int successCount;
        private int failureCount;

        public int getFailureCount() {
            return failureCount;
        }

        public void setFailureCount(int failureCount) {
            this.failureCount = failureCount;
        }

        public int getSuccessCount() {
            return successCount;
        }

        public void setSuccessCount(int successCount) {
            this.successCount = successCount;
        }

        @Override
        public String toString() {
            StringBuffer result = new StringBuffer();
            int total = getSuccessCount() + getFailureCount();
            result.append(getSuccessCount() + "/" + total);
            float percentage = 0;
            if (total > 0)
                percentage = 100.0f * getSuccessCount() / total;
            result.append(" (" + percentage + "%)");
            return result.toString();
        }
    }

    class RunScenario implements Runnable {
        BatchTestScenario batchTestScenario;
        String beanName;
        CyclicBarrier barrier;
        CountDownLatch latch;

        public RunScenario(BatchTestScenario aBatchTestScenario, String aBeanName, CyclicBarrier aBarrier,
                CountDownLatch aLatch) {
            batchTestScenario = aBatchTestScenario;
            beanName = aBeanName;
            barrier = aBarrier;
            latch = aLatch;
        }

        public void run() {
            try {
                barrier.await();
                String scenarioId = batchTestScenario.start(beanName);
                results.putIfAbsent(scenarioId, new Result(false));

                log.info("Started scenario " + scenarioId + ", " + beanName);
            } catch (Throwable t) {
                log.error(String.format("Test for scenario %s threw an exception", beanName), t);
            } finally {
                latch.countDown();
            }
        }
    }

    public void setAudioFileUri(String theAudioFileUri) {
        audioFileUri = theAudioFileUri;
    }

    public String getAudioFileUri() {
        return audioFileUri;
    }

    public void addBatchTestScenario(String batchTestScenarioNameAndWeight) {
        String[] split = batchTestScenarioNameAndWeight.split(",");
        scenarios.put(split[0], Double.parseDouble(split[1]));
        analysis.put(split[0], new Analysis());
    }

    protected void normalizeWeightings() {
        double cumulative = 0;
        for (String key : scenarios.keySet())
            cumulative += scenarios.get(key).doubleValue();

        for (String key : scenarios.keySet()) {
            double currentValue = scenarios.get(key);
            scenarios.put(key.toString(), currentValue / cumulative);
        }
    }

    public void setMaximumScenarioCompletionWaitTimeSeconds(int t) {
        maximumScenarioCompletionWaitTimeSeconds = t;
    }

    public void setNumberOfRuns(int numberOfRuns) {
        this.numberOfRuns = numberOfRuns;
    }

    public void setNumberOfConcurrentStarts(int numberOfConcurrentStarts) {
        this.numberOfConcurrentStarts = numberOfConcurrentStarts;
    }

    /*
     * Run random scenario
     */
    public void run() {
        run(null);
    }

    /*
     * Run specific scenario
     */
    public void run(String scenarioName) {
        this.numberCompleted = 0;
        this.numberSucceeded = 0;
        this.results = new ConcurrentHashMap<String, Result>();
        normalizeWeightings();
        totalTime = 0;
        CyclicBarrier barrier = new CyclicBarrier(numberOfConcurrentStarts);
        for (int i = 0; i < numberOfRuns; i++) {
            try {
                if (manager != null)
                    manager.doApplicationContextStartStop();
                long startTime = System.currentTimeMillis();
                Thread.sleep(1000);
                String[] beans = new String[numberOfConcurrentStarts];
                BatchTestScenario[] concurrentScenarios = new BatchTestScenario[numberOfConcurrentStarts];
                for (int j = 0; j < numberOfConcurrentStarts; j++) {
                    beans[j] = scenarioName == null ? pickScenarioName() : scenarioName;

                    BatchTestScenario s = (BatchTestScenario) applicationContext.getBean(beans[j]);
                    ((BatchTestScenarioBase) s).setCallCollection(callCollection);
                    ((BatchTestScenarioBase) s).setAudioFileUri(audioFileUri);

                    if (s.getBatchTestScenarioResultListener() == null)
                        s.setBatchTestScenarioResultListener(this);
                    concurrentScenarios[j] = s;
                }

                CountDownLatch latch = new CountDownLatch(numberOfConcurrentStarts);
                for (int j = 0; j < numberOfConcurrentStarts; j++) {
                    if (concurrentScenarios[j] == null)
                        break;
                    RunScenario rs = new RunScenario(concurrentScenarios[j], beans[j], barrier, latch);
                    executorService.execute(rs);
                }
                latch.await();
                barrier.reset();
                totalTime += System.currentTimeMillis() - startTime;
            } catch (Throwable t) {
                log.error(String.format("Test run %d threw an exception", i), t);
            }
        }

        waitForAllToFinish(maximumScenarioCompletionWaitTimeSeconds);
        log.info("Finishing...");
        if (results.size() < 1) {
            log.info("NO scenarios run!");
            return;
        }
        numberSucceeded = 0;
        for (String o : results.keySet()) {
            String scenario = o.split(":")[0];
            Result res = results.get(o);
            Analysis result = analysis.get(scenario);
            if (result != null) {
                if (res.isSucceeded()) {
                    result.setSuccessCount(result.getSuccessCount() + 1);
                } else {
                    result.setFailureCount(result.getFailureCount() + 1);
                }
                log.info(String.format("Scenario %s %s", o, res.toString()));
                System.err.println(String.format("Scenario %s %s", o, res.toString()));
                numberCompleted++;
                if (res.isSucceeded())
                    numberSucceeded++;
                else
                    failedScenarios.add(o);
            } else {
                log.error("unable to find result for scenario " + scenario);
            }
        }
        log.info(numberSucceeded + " successful scenarios, " + successRate() + "% passed");
        log.info(numberCompleted());
        if (manager != null) {
            log.info("Access to ApplicationContext1 #: " + manager.getCountReturnedAppCtx1());
            log.info("Access to ApplicationContext2 #: " + manager.getCountReturnedAppCtx2());
        }
        resetDb();
    }

    protected void resetDb() {
        MaintenanceDao dao = (MaintenanceDao) applicationContext.getBean("maintenanceDaoBean");
        dao.truncateAllTables();
    }

    private void waitForAllToFinish(int totalTime) {
        log.info("waiting for scenarios to finish...");
        int loops = totalTime / 10;
        while (!allSucceeded() && loops > 0) {
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
            }
            loops--;
        }
        log.info("... finished waiting");
    }

    private boolean allSucceeded() {
        for (Result result : results.values()) {
            if (!result.isSucceeded()) {
                return false;
            }
        }
        return true;
    }

    protected String pickScenarioName() {
        double random = Math.random();
        double cumulative = 0;
        String beanName = null;
        for (String key : scenarios.keySet()) {
            cumulative += scenarios.get(key);
            if (cumulative > random) {
                beanName = key;
                break;
            }
        }
        return beanName;
    }

    public String numberCompleted() {
        StringBuffer result = new StringBuffer(this.numberCompleted + " tests run\n");
        result.append(String.format("Average delay between scenarios: %fms\n",
                this.numberOfRuns == 0 ? 0 : this.totalTime / (double) this.numberOfRuns));
        for (String scenario : analysis.keySet()) {
            result.append(scenario + ": " + analysis.get(scenario).toString());
            result.append("\n");
        }
        result.append("Overall success rate: " + successRate() + "%");
        return result.toString();
    }

    public float successRate() {
        return 100 * (1.0f * numberSucceeded / numberCompleted);
    }

    public void runCompleted(String id, boolean successful) {
        if (results.containsKey(id)) {
            results.get(id).setSucceeded(successful);
        } else {
            results.put(id, new Result(successful));
        }
    }

    public void updateRunStatus(String id, String message) {
        if (results.containsKey(id))
            results.get(id).setMessage(message);
        else
            results.putIfAbsent(id, new Result(message));
    }

    public int numberFailed() {
        return this.numberCompleted - this.numberSucceeded;
    }

    // all hard-coded for now, will eventually read from props
    public static void main(String[] args) throws Exception {
        MultistackApplicationContextManager manager = null;
        try {
            manager = new MultistackApplicationContextManager(
                    new String[] { "batchTestApplicationContext.xml", "propertyListenerApplicationContext.1.xml" },
                    null);
        } catch (Exception e) {
            log.error(e);
            e.printStackTrace();
            System.exit(1);
        }

        BatchTest batchTest = (BatchTest) manager.getApplicationContext1().getBean("batchTestBean");
        batchTest.setApplicationContext(manager.getApplicationContext1());
        batchTest.setApplicationContextManager(manager);
        batchTest.init();
        batchTest.assignNewCollectionsToBeans();
        batchTest
                .setExecutorService(Executors.newFixedThreadPool(batchTest.getExecutorServiceNewFixedThreadPool()));
        batchTest.addBatchScenarios();
        batchTest.run();
        log.info(String.format("Number completed: %s", batchTest.numberCompleted()));
        batchTest.destroy();

        int dialogInvocationCount = batchTest.getDialogCollection().getInvocationCounterSize();
        int callInvocationCount = batchTest.getCallCollection().getInvocationCounterSize();
        int conferenceInvocationCount = batchTest.getConferenceCollection().getInvocationCounterSize();
        int dialogExceptionsCount = batchTest.getDialogCollection().getExceptionCounterSize();
        int callExceptionsCount = batchTest.getCallCollection().getExceptionCounterSize();
        int conferenceExceptionsCount = batchTest.getConferenceCollection().getExceptionCounterSize();

        log.debug(String.format(
                "ReplaceInfo invocations at dialog level: %d, at call level: %d, at conference level: %d, combined: %d",
                dialogInvocationCount, callInvocationCount, conferenceInvocationCount,
                dialogInvocationCount + callInvocationCount + conferenceInvocationCount));
        log.debug(String.format(
                "Concurrent Exceptions at dialog level: %d, at call level: %d, at conference level: %d, combined: %d",
                dialogExceptionsCount, callExceptionsCount, conferenceExceptionsCount,
                dialogExceptionsCount + callExceptionsCount + conferenceExceptionsCount));

        //        batchTest.logFailedScenarios();

        System.exit(batchTest.numberFailed());
    }

    public void addBatchScenarios() {
        addBatchTestScenario("createCallTerminateCallScenario,10");
        addBatchTestScenario("twoCallsSharingCallLegScenario,10");
        addBatchTestScenario("announcementCallScenario,2");
        //        addBatchTestScenario("basicCallAndMediaCallSharedDialogScenario,2");
        addBatchTestScenario("firstCallLegFailureScenario,0");
        addBatchTestScenario("secondCallLegFailureScenario,0");
        //        addBatchTestScenario("basicConferenceScenario,2"); // 3
        //        addBatchTestScenario("conferenceScenarioTerminateParticipants,2"); // 3
        addBatchTestScenario("maxCallDurationScenario,10");
        addBatchTestScenario("badAddressScenario,10");
        addBatchTestScenario("callAnswerTimeoutScenario,10");
        //        addBatchTestScenario("promptAndCollectScenario,1");
        //        addBatchTestScenario("promptAndRecordScenario,1");
    }

    protected DialogCollectionImplWithStats getDialogCollection() {
        return dialogCollection;
    }

    protected CallCollectionImplWithStats getCallCollection() {
        return callCollection;
    }

    public ConferenceCollectionImplWithStats getConferenceCollection() {
        return conferenceCollection;
    }

    protected void assignNewCollectionsToBeans() {
        assignNewCollectionsToBeans(applicationContext, dialogCollection, callCollection, conferenceCollection);
    }

    protected void assignNewCollectionsToBeans(ClassPathXmlApplicationContext ac,
            DialogCollectionImplWithStats _dialogCollection, CallCollectionImplWithStats _callCollection,
            ConferenceCollectionImplWithStats _conferenceCollection) {
        ((DialogJainSipListener) ac.getBean("dialogSipListener")).setDialogCollection(_dialogCollection);
        ((OutboundCallLegBeanImpl) ac.getBean("outboundCallLegBean")).setDialogCollection(_dialogCollection);
        ((InboundCallLegBeanImpl) ac.getBean("inboundCallLegBean")).setDialogCollection(_dialogCollection);
        ((CallBeanImpl) ac.getBean("callBean")).setDialogCollection(_dialogCollection);
        ((MediaCallLegBeanImpl) ac.getBean("mediaCallLegBean")).setDialogCollection(_dialogCollection);

        ((CallBeanImpl) ac.getBean("callBean")).setCallCollection(_callCollection);
        ((MediaCallBeanImpl) ac.getBean("mediaCallBean")).setCallCollection(_callCollection);
        ((ConferenceBeanImpl) ac.getBean("conferenceBean")).setConferenceCollection(_conferenceCollection);
    }

    public void logFailedScenarios() {
        for (String scenario : failedScenarios)
            logFailedScenario(scenario);
    }

    public void logFailedScenario(String scenarioId) {
        try {
            if (scenarioId.startsWith("twoCallsSharingCallLegScenario:"))
                twoCallsSharingCallLegScenarioExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("conferenceScenarioTerminateParticipants:"))
                conferenceScenarioTerminateParticipantsLogExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("callAnswerTimeoutScenario:"))
                callAnswerTimeoutScenarioLogExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("basicCallAndMediaCallSharedDialogScenario:"))
                basicCallAndMediaCallSharedDialogScenarioLogExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("promptAndCollectScenario:"))
                promptAndCollectScenarioLogExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("promptAndRecordScenario:"))
                promptAndCollectScenarioLogExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("announcementCallScenario:"))
                announcementCallScenarioLogExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("maxCallDurationScenario:"))
                maxCallDurationScenarioLogExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("basicConferenceScenarioMultiStack:"))
                basicConferenceScenarioMultiStackExtractor.doit(scenarioId);
            else if (scenarioId.startsWith("byeScenario:"))
                loadBalancedByeScenarioMultistackExtractor.doit(scenarioId);
            else
                System.err.println(
                        String.format("Unknown log extractor for scenario %s, please write one", scenarioId));
        } catch (Exception e) {
            log.warn(String.format("Error extracting logs from logfile for scenario %s", scenarioId), e);
        }
    }

    /**
     * @param executorService the executorService to set
     */
    public void setExecutorService(ExecutorService executorService) {
        this.executorService = executorService;
    }

    public int getNumberOfRuns() {
        return numberOfRuns;
    }

    public int getNumberOfConcurrentStarts() {
        return numberOfConcurrentStarts;
    }

    public int getMaximumScenarioCompletionWaitTimeSeconds() {
        return maximumScenarioCompletionWaitTimeSeconds;
    }
}