net.bpelunit.framework.control.run.TestCaseRunner.java Source code

Java tutorial

Introduction

Here is the source code for net.bpelunit.framework.control.run.TestCaseRunner.java

Source

/**
 * This file belongs to the BPELUnit utility and Eclipse plugin set. See enclosed
 * license file for more information.
 * 
 */
package net.bpelunit.framework.control.run;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;

import net.bpelunit.framework.BPELUnitRunner;
import net.bpelunit.framework.control.datasource.WrappedContext;
import net.bpelunit.framework.control.util.BPELUnitConstants;
import net.bpelunit.framework.control.ws.LocalHTTPServer;
import net.bpelunit.framework.exception.DataSourceException;
import net.bpelunit.framework.exception.PartnerNotFoundException;
import net.bpelunit.framework.exception.SynchronousSendException;
import net.bpelunit.framework.model.test.PartnerTrack;
import net.bpelunit.framework.model.test.TestCase;
import net.bpelunit.framework.model.test.activity.VelocityContextProvider;
import net.bpelunit.framework.model.test.report.ITestArtefact;
import net.bpelunit.framework.model.test.wire.IncomingMessage;
import net.bpelunit.framework.model.test.wire.OutgoingMessage;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.log4j.Logger;

/**
 * 
 * The TestCaseRunner handles kicking off the partnerTrack threads; it handles
 * communication between threads as well as network communication and aborting
 * in case of an error, failure, or user interaction.
 * 
 * @author Philip Mayer
 * @author University of Cdiz (Antonio Garca-Domnguez)
 */
public class TestCaseRunner implements VelocityContextProvider {

    private enum PartnerTrackResult {
        RUNNING, COMPLETED
    };

    // Data
    private TestCase fTestCase;

    private Map<PartnerTrack, PartnerTrackResult> fPartnerTracks;

    private List<String> executedActivities;

    private boolean fProblemOccurred;

    // Thread communication
    private BlackBoard<PartnerTrack, IncomingMessage> fIncomingBlackboard;

    private BlackBoard<PartnerTrack, OutgoingMessage> fOutgoingBlackboard;

    private BlackBoard<OutgoingMessage, Boolean> fSentBlackBoard;

    // Connection stuff
    private LocalHTTPServer fServer;

    // Other stuff
    private Logger fLogger;

    private boolean fAbortedByUser;

    private HttpClient fClient;

    public TestCaseRunner(LocalHTTPServer localServer, TestCase caseToRun) {
        fTestCase = caseToRun;
        fServer = localServer;

        fProblemOccurred = false;
        fAbortedByUser = false;

        initializePartnerTracks(caseToRun);

        fIncomingBlackboard = new BlackBoard<PartnerTrack, IncomingMessage>();
        fOutgoingBlackboard = new BlackBoard<PartnerTrack, OutgoingMessage>();
        fSentBlackBoard = new BlackBoard<OutgoingMessage, Boolean>();

        executedActivities = new ArrayList<String>();

        fLogger = Logger.getLogger(getClass());
    }

    private void initializePartnerTracks(TestCase caseToRun) {
        List<PartnerTrack> partnerTracks = caseToRun.getPartnerTracks();
        for (PartnerTrack partnerTrack : partnerTracks) {
            partnerTrack.initialize(this);
        }
        fPartnerTracks = new HashMap<PartnerTrack, PartnerTrackResult>();
        for (PartnerTrack head : partnerTracks) {
            fPartnerTracks.put(head, PartnerTrackResult.RUNNING);
        }
    }

    public void run() {
        // Pool connections to avoid socket leaks
        MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
        // Increase maximum per host, as most will use the same host (localhost)
        connectionManager.getParams().setDefaultMaxConnectionsPerHost(10);

        fClient = new HttpClient(connectionManager);

        try {
            fLogger.info("Initiating testCase " + fTestCase.getName());
            fServer.startTest(this);

            final List<Thread> threads = new ArrayList<Thread>();
            startPartnerTracks(threads);
            waitForPartnerTracksOrError();

            if (fProblemOccurred || fAbortedByUser) {
                checkPartnerTracksForProblems();
                interruptAllThreads(threads);
                waitForPartnerTracks();
            } else {
                fLogger.info("Test case passed.");
            }
            fLogger.debug("All threads returned.");

            fLogger.info("Stopping testCase " + fTestCase.getName());
            fServer.stopTest(this);
        } finally {
            connectionManager.shutdown();
        }
    }

    private void startPartnerTracks(final List<Thread> threads) {
        for (PartnerTrack partnerTrack : fPartnerTracks.keySet()) {
            Thread trackThread = new Thread(partnerTrack, partnerTrack.getPartnerName());
            fLogger.debug("Now starting thread for partner " + partnerTrack.getPartnerName());
            trackThread.start();
            threads.add(trackThread);
        }
        fLogger.info("TestCase was started.");
    }

    private void checkPartnerTracksForProblems() {
        for (PartnerTrack partnerTrack : fPartnerTracks.keySet()) {
            if (partnerTrack.getStatus().isError() || partnerTrack.getStatus().isFailure()) {
                fLogger.info(String.format("A test failure or error occurred on %s: %s", partnerTrack.getName(),
                        partnerTrack.getStatus().getMessage()));
            }
        }
    }

    private void interruptAllThreads(final List<Thread> threads) {
        fLogger.debug("Trying to interrupt all threads...");
        for (Thread t : threads) {
            if (t.isAlive()) {
                t.interrupt();
            }
        }
        fLogger.debug("All threads interrupted. Waiting for threads...");
    }

    public synchronized PartnerTrack findPartnerTrackForName(String name) throws PartnerNotFoundException {
        for (PartnerTrack partnerTrack : fPartnerTracks.keySet()) {
            if (partnerTrack.getPartnerName().equalsIgnoreCase(name)) {
                // found the partner track - but does she have activities?
                if (partnerTrack.getActivityCount() != 0) {
                    return partnerTrack;
                } else {
                    throw new PartnerNotFoundException("Partner found, but zero activities for name " + name);
                }
            }
        }
        throw new PartnerNotFoundException("No partner found for name " + name);
    }

    // ********************* Messaging ******************************

    /**
     * Wait for an incoming message at the port and address registered to the
     * given partner track.
     * 
     * This call will not return until either a message as been received, a
     * timeout has occurred, or the thread was interrupted.
     * 
     * Note that the Web Service Handler expects an answer to every such
     * incoming message. Use
     * {@link #sendBackMessage(PartnerTrack, OutgoingMessage)} to send back a
     * message.
     * 
     * @param partnerTrack
     * @return
     * @throws TimeoutException
     * @throws InterruptedException
     */
    public IncomingMessage receiveMessage(PartnerTrack partnerTrack) throws TimeoutException, InterruptedException {
        return fIncomingBlackboard.getObject(partnerTrack);
    }

    /**
     * 
     * Sends back a message as an answer to a previously-received incoming
     * message via {@link #receiveMessage(PartnerTrack)}. The message will be
     * sent back using the still-open HTTP Connection.
     * 
     * This call will not return until either the message has been delivered, or
     * a timeout has occurred, or the thread was interrupted.
     * 
     * @param partnerTrack
     * @param message
     * @throws TimeoutException
     * @throws InterruptedException
     */
    public void sendBackMessage(PartnerTrack partnerTrack, OutgoingMessage message)
            throws TimeoutException, InterruptedException {
        fOutgoingBlackboard.putObject(partnerTrack, message);
        fSentBlackBoard.getObject(message);
    }

    /**
     * 
     * Sends a synchronous message, waits for the result, and returns the
     * result. This method blocks until either a result has been retrieved, a
     * send error has occurred, or the thread was interrupted.
     * 
     * @param message
     *            the message to be sent
     * @return the resulting incoming message
     * @throws SynchronousSendException
     * @throws InterruptedException
     */
    public IncomingMessage sendMessageSynchronous(OutgoingMessage message)
            throws SynchronousSendException, InterruptedException {
        PostMethod method = new PostMethod(message.getTargetURL());

        // Set parameters:
        // -> Do not retry
        // -> Socket timeout to default timeout value.
        method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                new DefaultHttpMethodRetryHandler(1, false));
        method.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, Integer.valueOf(BPELUnitRunner.getTimeout()));

        method.addRequestHeader("SOAPAction", "\"" + message.getSOAPHTTPAction() + "\"");
        RequestEntity entity;
        try {
            entity = new StringRequestEntity(message.getMessageAsString(), BPELUnitConstants.TEXT_XML_CONTENT_TYPE,
                    BPELUnitConstants.DEFAULT_HTTP_CHARSET);
        } catch (UnsupportedEncodingException e) {
            // cannot happen since we use the default HTTP Encoding.
            throw new SynchronousSendException("Unsupported encoding when trying to post message to web service.",
                    e);
        }

        for (String option : message.getProtocolOptionNames()) {
            method.addRequestHeader(option, message.getProtocolOption(option));
        }
        method.setRequestEntity(entity);

        try {
            // Execute the method.

            int statusCode = fClient.executeMethod(method);
            InputStream in = method.getResponseBodyAsStream();

            IncomingMessage returnMsg = new IncomingMessage();
            returnMsg.setStatusCode(statusCode);
            returnMsg.setMessage(in);

            return returnMsg;

        } catch (Exception e) {
            if (isAborting()) {
                throw new InterruptedException();
            } else {
                throw new SynchronousSendException(e);
            }
        } finally {
            // Release the connection.
            method.releaseConnection();
        }

    }

    // ************* Accessor functions for partner tracks

    /**
     * This method signals that the given partner track has completed its
     * actions without any fault. The partner track thread should terminate
     * directly after calling this method.
     * 
     * @param track
     */
    public synchronized void done(PartnerTrack track) {
        fPartnerTracks.put(track, PartnerTrackResult.COMPLETED);
        notifyAll();
    }

    /**
     * This method signals that the given partner track has completed its
     * actions with an error or fault. The partner track thread should terminate
     * directly after calling this method.
     * 
     * @param track
     */
    public synchronized void doneWithFault(PartnerTrack track) {
        fProblemOccurred = true;
        fPartnerTracks.put(track, PartnerTrackResult.COMPLETED);
        notifyAll();
    }

    // ********** Accessor functions for the web service handler *************

    public OutgoingMessage getWSOutgoingMessage(PartnerTrack head) throws TimeoutException, InterruptedException {
        return fOutgoingBlackboard.getObject(head);
    }

    public void putWSIncomingMessage(PartnerTrack head, IncomingMessage message) throws InterruptedException {
        fIncomingBlackboard.putObject(head, message);
    }

    public void putWSOutgoingMessageSent(OutgoingMessage message) throws InterruptedException {
        fSentBlackBoard.putObject(message, true);
    }

    // ********************* Waiting *******************************

    private synchronized void waitForPartnerTracksOrError() {
        while (!allPartnerTracksCompleted() && (!fProblemOccurred) && (!fAbortedByUser)) {
            try {
                wait(BPELUnitConstants.TIMEOUT_SLEEP_TIME);
            } catch (InterruptedException e) {
            }
        }
    }

    private synchronized void waitForPartnerTracks() {
        while (!allPartnerTracksCompleted()) {
            try {
                wait(BPELUnitConstants.TIMEOUT_SLEEP_TIME);
            } catch (InterruptedException e) {
            }
        }
    }

    private synchronized boolean allPartnerTracksCompleted() {
        for (PartnerTrack head : fPartnerTracks.keySet()) {
            if (fPartnerTracks.get(head).equals(PartnerTrackResult.RUNNING)) {
                return false;
            }
        }

        return true;
    }

    private boolean isAborting() {
        return fProblemOccurred || fAbortedByUser;
    }

    public void abortTest() {
        /*
         * Set aborted flag. Will get picked up by the runner in the right
         * thread.
         */
        fAbortedByUser = true;
    }

    public void markActivityAsExecuted(String activityId) {
        if (activityId != null) {
            executedActivities.add(activityId);
        }
    }

    public List<String> getExecutedActivities() {
        return new ArrayList<String>(this.executedActivities);
    }

    // ********************* Velocity contexts *********************

    public WrappedContext createVelocityContext(ITestArtefact artefact) throws DataSourceException {
        return fTestCase.createVelocityContext(artefact);
    }
}