com.cognifide.aet.runner.distribution.SuiteExecutor.java Source code

Java tutorial

Introduction

Here is the source code for com.cognifide.aet.runner.distribution.SuiteExecutor.java

Source

/**
 * Automated Exploratory Tests
 *
 * Copyright (C) 2013 Cognifide Limited
 *
 * 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 com.cognifide.aet.runner.distribution;

import com.google.inject.Inject;
import com.google.inject.name.Named;

import com.cognifide.aet.communication.api.ProcessingError;
import com.cognifide.aet.communication.api.exceptions.AETException;
import com.cognifide.aet.communication.api.messages.ProgressMessage;
import com.cognifide.aet.communication.api.util.ExecutionTimer;
import com.cognifide.aet.runner.conversion.SuiteIndexWrapper;
import com.cognifide.aet.runner.distribution.dispatch.CollectDispatcher;
import com.cognifide.aet.runner.distribution.dispatch.CollectionResultsRouter;
import com.cognifide.aet.runner.distribution.dispatch.ComparisonResultsRouter;
import com.cognifide.aet.runner.distribution.dispatch.MetadataPersister;
import com.cognifide.aet.runner.distribution.progress.ProgressLog;
import com.cognifide.aet.runner.distribution.watch.TimeoutWatch;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

import javax.jms.JMSException;

/**
 * SuiteExecutor - executes suite, execution consists of three steps: preparation, validation and
 * processing
 */
class SuiteExecutor {

    private static final Logger LOGGER = LoggerFactory.getLogger(SuiteExecutor.class);

    private static final String SYSTEM_IN_MAINTENANCE_MESSAGE = "The AET System is currently down for maintenance. Well be back soon!";

    private static final String SYSTEM_RUNNING_NORMALLY_MESSAGE = "Maintenance mode is switched off. Please run the test in normal mode.";

    private static final String RUNNING_TASK_IN_MAINTENANCE_MODE = "Running task in maintenance mode!";

    private final TimeoutWatch timeoutWatch;

    private final SuiteIndexWrapper suite;

    private final CollectDispatcher collectDispatcher;

    private final CollectionResultsRouter collectionResultsRouter;

    private final ComparisonResultsRouter comparisonResultsRouter;

    private final SuiteAgent suiteAgent;

    private final ProgressMessageObserver progressMessageObserver;

    private final Long runTimeoutInSeconds;

    private final SuiteExecutionSettings suiteExecutionSettings;

    private final MetadataPersister metadataPersister;

    private boolean processed;

    @Inject
    SuiteExecutor(SuiteExecutionSettings suiteExecutionSettings, TimeoutWatch timeoutWatch, SuiteIndexWrapper suite,
            CollectDispatcher collectDispatcher, CollectionResultsRouter collectionResultsRouter,
            ComparisonResultsRouter comparisonResultsRouter, SuiteAgent suiteAgent,
            ProgressMessageObserver progressMessageObserver, MetadataPersister metadataPersister,
            @Named("failureTimeout") long runTimeoutInSeconds) {
        this.suiteExecutionSettings = suiteExecutionSettings;
        this.timeoutWatch = timeoutWatch;
        this.suite = suite;
        this.collectDispatcher = collectDispatcher;
        this.collectionResultsRouter = collectionResultsRouter;
        this.comparisonResultsRouter = comparisonResultsRouter;
        this.suiteAgent = suiteAgent;
        this.progressMessageObserver = progressMessageObserver;
        this.metadataPersister = metadataPersister;
        this.runTimeoutInSeconds = runTimeoutInSeconds;

        suiteAgent.addProcessingErrorMessagesObservable(collectionResultsRouter);
        suiteAgent.addProcessingErrorMessagesObservable(comparisonResultsRouter);
        collectionResultsRouter.addChangeObserver(comparisonResultsRouter);
        comparisonResultsRouter.setMetadataPersister(metadataPersister);
        metadataPersister.addObserver(suiteAgent);
    }

    void execute() {
        if (suiteExecutionSettings.shouldExecute()) {
            executeSuite();
        } else {
            finishWithoutExecution();
        }
    }

    void cleanup() {
        comparisonResultsRouter.closeConnections();
        collectionResultsRouter.closeConnections();
        collectDispatcher.closeConnections();
        suiteAgent.close();
    }

    /**
     * Immediately aborts all AET tasks for current life cycle.
     */
    synchronized void abort() {
        if (processed) {
            if (!comparisonResultsRouter.isFinished()) {
                LOGGER.warn("Consumer removed - aborting TestLifeCycle for task: {}",
                        suite.get().getCorrelationId());
            }
            comparisonResultsRouter.abort();
            collectDispatcher.cancel(suite.get().getCorrelationId());
        } else {
            processed = true;
            LOGGER.warn("Consumer removed - aborting TestLifeCycle for task: {} before processing",
                    suite.get().getCorrelationId());
        }
    }

    private void executeSuite() {
        if (suiteExecutionSettings.isMaintenanceMessage()) {
            LOGGER.info(RUNNING_TASK_IN_MAINTENANCE_MODE);
            progressMessageObserver.update(null, new ProgressMessage(RUNNING_TASK_IN_MAINTENANCE_MODE));
        }
        try {
            ExecutionTimer timer = ExecutionTimer.createAndRun("RUNNER");
            LOGGER.info("Start lifecycle of test suite run: {}", suite.get());
            timeoutWatch.update();
            process(timer);
        } catch (JMSException | AETException e) {
            LOGGER.error("Can't run TestLifeCycle!", e);
            suiteAgent.sendFailMessage(Collections.singletonList(e.getMessage()));
        }
    }

    private void finishWithoutExecution() {
        String failMessage;
        if (suiteExecutionSettings.getRunnerMode() == RunnerMode.MAINTENANCE) {
            failMessage = SYSTEM_IN_MAINTENANCE_MESSAGE;
        } else {
            failMessage = SYSTEM_RUNNING_NORMALLY_MESSAGE;
        }
        LOGGER.warn(failMessage);
        suiteAgent.sendFailMessage(Collections.singletonList(failMessage));
    }

    private void process(ExecutionTimer timer) throws JMSException, AETException {
        if (tryProcess()) {
            checkStatusUntilFinishedOrTimedOut();
            if (comparisonResultsRouter.isFinished()) {
                timer.finish();
                LOGGER.info("Finished lifecycle of test run: {}. Task finished in {} ms ({}).",
                        suite.get().getCorrelationId(), timer.getExecutionTimeInMillis(),
                        timer.getExecutionTimeInMMSS());
                LOGGER.info("Total tasks finished in steps: collect: {}; compare: {}.",
                        collectionResultsRouter.getTotalTasksCount(), comparisonResultsRouter.getTotalTasksCount());
            } else if (suiteIsTimedOut()) {
                timer.finish();
                LOGGER.warn(
                        "Lifecycle of run {} interrupted after {} ms ({}). Last message received: {} seconds ago... Trying to force report generation.",
                        suite.get().getCorrelationId(), timer.getExecutionTimeInMillis(),
                        timer.getExecutionTimeInMMSS(),
                        TimeUnit.NANOSECONDS.toSeconds(timeoutWatch.getLastUpdateDifference()));
                forceFinishSuite();
                collectDispatcher.cancel(suite.get().getCorrelationId());
            }
        }
    }

    private boolean suiteIsTimedOut() {
        return timeoutWatch.isTimedOut(runTimeoutInSeconds);
    }

    private void checkStatusUntilFinishedOrTimedOut() {
        String logMessage = "";
        while (!comparisonResultsRouter.isFinished() && !timeoutWatch.isTimedOut(runTimeoutInSeconds)) {
            try {
                String currentLog = composeProgressLog();
                if (!currentLog.equals(logMessage)) {
                    logMessage = currentLog;
                    LOGGER.info("[{}]: {}", suite.get().getCorrelationId(), logMessage);
                    progressMessageObserver.update(null, new ProgressMessage(logMessage));
                }
                Thread.sleep(1000);

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private String composeProgressLog() {
        ProgressLog compareLog = collectionResultsRouter.getProgress();
        ProgressLog reportLog = comparisonResultsRouter.getProgress();
        return StringUtils.join(Arrays.asList(compareLog, reportLog), " ::: ");
    }

    private synchronized boolean tryProcess() throws JMSException, AETException {
        boolean result = !processed;
        if (!processed) {
            collectDispatcher.process();
            processed = true;
        }
        return result;
    }

    private void forceFinishSuite() {
        suiteAgent.onError(ProcessingError
                .reportingError("Report will be generated after timeout - some results might be missing!"));
        suiteAgent.enforceSuiteFinish();
        timeoutWatch.update();
        metadataPersister.persistMetadata();
        checkStatusUntilFinishedOrTimedOut();
        if (!comparisonResultsRouter.isFinished()) {
            suiteAgent.sendFailMessage(
                    Collections.singletonList("Failed to generate reports because it took too much time!"));
        }
    }

}