ca.uhn.hunit.iface.AbstractInterface.java Source code

Java tutorial

Introduction

Here is the source code for ca.uhn.hunit.iface.AbstractInterface.java

Source

/**
 *
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
 * specific language governing rights and limitations under the License.
 *
 * The Initial Developer of the Original Code is University Health Network. Copyright (C)
 * 2001.  All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * GNU General Public License (the  "GPL"), in which case the provisions of the GPL are
 * applicable instead of those above.  If you wish to allow use of your version of this
 * file only under the terms of the GPL and not to allow others to use your version
 * of this file under the MPL, indicate your decision by deleting  the provisions above
 * and replace  them with the notice and other provisions required by the GPL License.
 * If you do not delete the provisions above, a recipient may use your version of
 * this file under either the MPL or the GPL.
 */
package ca.uhn.hunit.iface;

import java.beans.PropertyVetoException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;

import ca.uhn.hunit.ex.InterfaceWontStartException;
import ca.uhn.hunit.ex.InterfaceWontStopException;
import ca.uhn.hunit.ex.TestFailureException;
import ca.uhn.hunit.ex.UnexpectedTestFailureException;
import ca.uhn.hunit.l10n.Strings;
import ca.uhn.hunit.run.IExecutionContext;
import ca.uhn.hunit.test.TestBatteryImpl;
import ca.uhn.hunit.util.AbstractModelClass;
import ca.uhn.hunit.util.log.LogFactory;
import ca.uhn.hunit.xsd.AnyInterface;
import ca.uhn.hunit.xsd.Interface;

/**
 * Base class for an interface
 * 
 * @param <T>
 *            The type of message class this interface is able to send and
 *            receive
 */
public abstract class AbstractInterface<T> extends AbstractModelClass implements Comparable<AbstractInterface<?>> {
    // ~ Static fields/initializers
    // -------------------------------------------------------------------------------------

    public static final String INTERFACE_STARTED_PROPERTY = "INTERFACE_STARTED_PROPERTY";
    public static final String INTERFACE_ID_PROPERTY = "INTERFACE_ID_PROPERTY";

    // ~ Instance fields
    // ------------------------------------------------------------------------------------------------

    private Boolean myAutostart;
    private Boolean myClear;
    private Integer myClearMillis;
    private String myId;
    private final TestBatteryImpl myBattery;
    private final LinkedBlockingQueue<TestMessage<T>> mySendMessage = new LinkedBlockingQueue<TestMessage<T>>();
    private final LinkedBlockingQueue<TestMessage<T>> myReceiveMessage = new LinkedBlockingQueue<TestMessage<T>>();
    private final LinkedBlockingQueue<TestMessage<T>> myReplyMessage = new LinkedBlockingQueue<TestMessage<T>>();
    private boolean myStartedForReceiving;
    private boolean myStartedForSending;

    // ~ Constructors
    // ---------------------------------------------------------------------------------------------------

    /**
     * Constructor
     */
    public AbstractInterface(TestBatteryImpl theBattery, Interface theConfig) {
        myBattery = theBattery;
        myId = theConfig.getId();
        myAutostart = theConfig.isAutostart();
        myClearMillis = theConfig.getClearMillis();
        myClear = theConfig.isClear();

        init();
    }

    /**
     * Constructor
     */
    public AbstractInterface(TestBatteryImpl theBattery, String theId) {
        myBattery = theBattery;
        myId = theId;

        init();
    }

    // ~ Methods
    // --------------------------------------------------------------------------------------------------------

    public int compareTo(AbstractInterface<?> theO) {
        return myId.compareTo(theO.myId);
    }

    /**
     * Subclasses should make use of this method to export AbstractInterface
     * properties into the return value for {@link #exportConfigToXml()}
     */
    protected Interface exportConfig(Interface theConfig) {
        theConfig.setAutostart(myAutostart);
        theConfig.setId(myId);
        theConfig.setClearMillis(myClearMillis);
        theConfig.setClear(myClear);

        return theConfig;
    }

    /**
     * Declare a concrete type for subclass implementations of this method
     */
    @Override
    public abstract AnyInterface exportConfigToXml();

    public int getClearMillis() {
        return myClearMillis;
    }

    public String getId() {
        return myId;
    }

    private void init() {
        if (myAutostart == null) {
            myAutostart = true;
        }

        if (myClearMillis == null) {
            myClearMillis = 100;
        }

        if (myClear == null) {
            myClear = true;
        }
    }

    public boolean isAutostart() {
        return myAutostart;
    }

    public boolean isClear() {
        return myClear;
    }

    /**
     * Returns true if this interface is started for either sending or receiving
     */
    public final boolean isStarted() {
        return myStartedForSending || myStartedForReceiving;
    }

    public TestMessage<T> receiveMessage(long theTimeout, TestMessage<T> theReply) throws TestFailureException {
        start(false, true);

        TestMessage<T> receivedMessage = null;
        long waitUntil = System.currentTimeMillis() + theTimeout;
        while (System.currentTimeMillis() < waitUntil && receivedMessage == null) {
            try {
                receivedMessage = myReceiveMessage.poll(waitUntil - System.currentTimeMillis(),
                        TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                // ignore
            }
        }

        if (receivedMessage == null) {
            return null;
        }

        if (isProducesReply()) {
            if (theReply == null) {
                theReply = generateDefaultReply(receivedMessage);
            }

            try {
                myReplyMessage.put(theReply);
            } catch (InterruptedException e) {
                throw new UnexpectedTestFailureException(e);
            }
        }

        return receivedMessage;
    }

    public abstract TestMessage<T> generateDefaultReply(TestMessage<T> theTestMessage) throws TestFailureException;

    public void setAutostart(boolean theAutostart) {
        myAutostart = theAutostart;
    }

    public void setClear(boolean theClear) {
        myClear = theClear;
    }

    public void setClearMillis(int theClearMillis) {
        myClearMillis = theClearMillis;
    }

    public void setId(String theId) throws PropertyVetoException {
        if (StringUtils.equals(theId, myId)) {
            return;
        }

        if (StringUtils.isEmpty(theId)) {
            throw new PropertyVetoException(Strings.getInstance().getString("interface.id.empty"), null);
        }

        if (myBattery.getInterfaceIds().contains(theId)) {
            throw new PropertyVetoException(Strings.getInstance().getString("interface.id.duplicate"), null);
        }

        String oldValue = myId;
        firePropertyChange(INTERFACE_ID_PROPERTY, oldValue, theId);
        myId = theId;
    }

    /**
     * {@inheritDoc }
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final AbstractInterface<?> other = (AbstractInterface<?>) obj;
        if ((this.myId == null) ? (other.myId != null) : !this.myId.equals(other.myId)) {
            return false;
        }
        return true;
    }

    /**
     * {@inheritDoc }
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 41 * hash + (this.myId != null ? this.myId.hashCode() : 0);
        return hash;
    }

    /**
     * Send a message to this interface, either without expecting or ignoring
     * any response. The appropriate behaviour (not expect / ignore) is
     * determined by the specific type of interface this is.
     */
    public void sendMessageOnly(IExecutionContext theCtx, TestMessage<T> theMessage) throws TestFailureException {
        start(true, false);
        internalSendMessage(theMessage);
    }

    /**
     * Subclasses must override this method to send a message
     * 
     * @param theMessage
     *            The message to send
     * @throws TestFailureException
     */
    protected abstract TestMessage<T> internalSendMessage(TestMessage<T> theMessage) throws TestFailureException;

    /**
     * Subclasses must invoke this method when receiving a message to provide
     * the message that was received. This method will provide the response, if
     * any
     * 
     * @param theMessage
     *            The message received
     * @return The response message
     */
    protected TestMessage<T> internalReceiveMessage(TestMessage<T> theMessage) {
        myReceiveMessage.add(theMessage);
        while (isStarted() && isProducesReply()) {
            try {
                TestMessage<T> take = myReplyMessage.take();
                return take;
            } catch (InterruptedException e) {
                // nothing
            }
        }

        return null;
    }

    /**
     * Defaults to true, subclasses may override to indicate that they don't
     * produce message replies
     */
    protected boolean isProducesReply() {
        return true;
    }

    public void stop() throws InterfaceWontStopException {
        if (!isStarted()) {
            return;
        }

        LogFactory.INSTANCE.get(this).info("Stopping interface");

        if (myStartedForReceiving) {
            doStopReceiving();
            myStartedForReceiving = false;
        }

        if (myStartedForSending) {
            doStopSending();
            myStartedForSending = false;
        }

        doStop();

        firePropertyChange(INTERFACE_STARTED_PROPERTY, true, false);
    }

    public void start(boolean theForSending, boolean theForReceiving) throws InterfaceWontStartException {
        // TODO: check if we're started for the right activity
        if (isStarted()) {
            return;
        }

        doStart();

        if (theForSending) {
            doStartSending();
        }
        if (theForReceiving) {
            doStartReceiving();
        }

        myStartedForReceiving = theForReceiving;
        myStartedForSending = theForSending;

        if (isClear()) {
            long readUntil = System.currentTimeMillis() + getClearMillis();
            int cleared = 0;

            while (System.currentTimeMillis() < readUntil) {
                try {
                    TestMessage<T> message = receiveMessage(getClearMillis(), null);

                    if ((message == null) || (message.getRawMessage().length() == 0)) {
                        try {
                            Thread.sleep(250);
                        } catch (InterruptedException e) {
                            // nothing
                        }

                        continue;
                    }

                    cleared++;
                    LogFactory.INSTANCE.get(this).info("Cleared message");

                    try {
                        Thread.sleep(250);
                    } catch (InterruptedException e) {
                        // nothing
                    }

                    readUntil = System.currentTimeMillis() + getClearMillis();
                } catch (TestFailureException e) {
                    LogFactory.INSTANCE.get(this).warn("Error while clearing queue: " + e.getMessage());
                }
            }

            LogFactory.INSTANCE.get(this).info("Cleared " + cleared + " messages from interface before starting");
        }

        firePropertyChange(INTERFACE_STARTED_PROPERTY, false, true);
        LogFactory.INSTANCE.get(this).info("Started interface successfully");
    }

    /**
     * Starts the interface receiving messages
     */
    protected abstract void doStartReceiving() throws InterfaceWontStartException;

    /**
     * Starts the interface sending messages
     */
    protected abstract void doStartSending() throws InterfaceWontStartException;

    /**
     * Starts the interface sending messages
     */
    protected abstract void doStart() throws InterfaceWontStartException;

    /**
     * Stops the interface
     */
    protected abstract void doStop() throws InterfaceWontStopException;

    /**
     * Stops the interface
     */
    protected abstract void doStopReceiving() throws InterfaceWontStopException;

    /**
     * Stops the interface
     */
    protected abstract void doStopSending() throws InterfaceWontStopException;

    /**
     * Does this interface support replying to an incoming message, or receiving
     * a reply to an outgoing message?
     */
    protected abstract boolean getCapabilitySupportsReply();

    /**
     * @return Returns true. Specific interface implementations may override if they
     * want to specify they they do not support "clear" (waiting for stale messages to
     * drain from the interface before starting)
     */
    public boolean isSupportsClear() {
        return true;
    }

}