package org.marketcetera.marketdata;
import static org.marketcetera.marketdata.TestMessages.EXPECTED_EXCEPTION;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.marketcetera.core.NoMoreIDsException;
import org.marketcetera.event.Event;
import org.marketcetera.event.MockEventTranslator;
import org.marketcetera.util.misc.ClassVersion;
/* $License$ */
/**
* Test implementation of <code>AbstractMarketDataFeed</code>.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: MockMarketDataFeed.java 16154 2012-07-14 16:34:05Z colin $
* @since 0.5.0
*/
@ClassVersion("$Id: MockMarketDataFeed.java 16154 2012-07-14 16:34:05Z colin $")
public class MockMarketDataFeed
extends AbstractMarketDataFeed<MockMarketDataFeedToken,
MockMarketDataFeedCredentials,
MockDataRequestTranslator,
MockEventTranslator,
String,
MockMarketDataFeed>
{
public static MockMarketDataFeed instance;
private final int mDelay;
private Set<Capability> capabilities = EnumSet.noneOf(Capability.class);
private Set<AssetClass> assetClasses = EnumSet.allOf(AssetClass.class);
private int mCounter = 0;
private enum State {
logged_out, logged_in;
private boolean isLoggedIn()
{
return this.equals(State.logged_in);
}
};
private State mState;
private boolean mLoginFails = false;
private boolean mInitFails = false;
private boolean mExecutionFails = false;
private boolean mCancelFails = false;
private boolean mExecuteReturnsNothing = false;
private boolean mExecuteReturnsNull = false;
private boolean mIsLoggedInThrows = false;
private boolean mLoginThrows = false;
private boolean mInitThrows = false;
private boolean mBeforeExecuteThrows = false;
private boolean mBeforeExecuteReturnsFalse = false;
private boolean mGenerateTokenThrows = false;
private boolean mGetEventTranslatorThrows = false;
private boolean mGetMessageTranslatorThrows = false;
private boolean mAfterExecuteThrows = false;
private boolean mShouldTimeout = false;
private long mTimeout = 60;
private static final Random sRandom = new Random(System.nanoTime());
private Set<String> mCanceledHandles = new LinkedHashSet<String>();
private Set<String> mCreatedHandles = new LinkedHashSet<String>();
private final List<Event> eventsToReturn = new ArrayList<Event>();
public MockMarketDataFeed()
throws NoMoreIDsException
{
this(FeedType.SIMULATED);
}
/**
* Create a new <code>TestMarketDataFeed</code> instance.
*
* @param inFeedType
* @throws NoMoreIDsException
*/
public MockMarketDataFeed(FeedType inFeedType)
throws NoMoreIDsException
{
this(inFeedType,
MockMarketDataFeed.class.toString(),
0);
}
public MockMarketDataFeed(FeedType inFeedType,
String inProviderName,
int inDelay)
throws NoMoreIDsException
{
super(inFeedType,
inProviderName);
mDelay = inDelay;
setState(State.logged_out);
instance = this;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.MarketDataFeed#getCapabilities()
*/
@Override
public Set<Capability> getCapabilities()
{
return capabilities;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.MarketDataFeed#getSupportedAssetClasses()
*/
@Override
public Set<AssetClass> getSupportedAssetClasses()
{
return assetClasses;
}
/**
* Sets the capabilities to use for this feed.
*
* @param inCapabilities a <code>Set<Capability></code> value
*/
public void setCapabilities(Set<Capability> inCapabilities)
{
capabilities = inCapabilities;
}
/**
* Sets the supported asset classes for this feed.
*
* @param inAssetClasses a <code>Set<AssetClass></code> value
*/
public void setAssetClasses(Set<AssetClass> inAssetClasses)
{
assetClasses = inAssetClasses;
}
/**
* Sets the events to return for a market data request.
*
* @param inEvents a <code>List<Event></code> value
*/
public void setEventsToReturn(List<Event> inEvents)
{
eventsToReturn.clear();
eventsToReturn.addAll(inEvents);
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#generateToken(quickfix.Message)
*/
@Override
protected MockMarketDataFeedToken generateToken(MarketDataFeedTokenSpec inTokenSpec)
throws FeedException
{
if(getGenerateTokenThrows()) {
throw new NullPointerException("This exception is expected");
}
return MockMarketDataFeedToken.getToken(inTokenSpec,
this);
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#doLogin(org.marketcetera.marketdata.AbstractMarketDataFeedCredentials)
*/
protected boolean doLogin(MockMarketDataFeedCredentials inCredentials)
{
if(getLoginThrows()) {
throw new NullPointerException("This exception is expected");
}
if(getLoginFails()) {
return false;
}
setState(State.logged_in);
return true;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#doLogout()
*/
protected void doLogout()
{
setState(State.logged_out);
}
private final ConcurrentLinkedQueue<String> mQueue = new ConcurrentLinkedQueue<String>();
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#afterDoExecute()
*/
@Override
protected void afterDoExecute(MockMarketDataFeedToken inToken,
List<String> inHandles,
Exception inException)
{
if(getAfterExecuteThrows()) {
throw new NullPointerException("This exception is expected");
}
String handle = mQueue.poll();
if(inToken != null) {
inToken.setHandle(handle);
}
if(handle != null) {
if(eventsToReturn.isEmpty()) {
dataReceived(handle,
inToken.getTokenSpec().getDataRequest());
} else {
for(Event event : eventsToReturn) {
dataReceived(handle,
event);
}
eventsToReturn.clear();
}
}
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#getMessageTranslator()
*/
protected MockDataRequestTranslator getMessageTranslator()
{
if(getGetMessageTranslatorThrows()) {
throw new NullPointerException("This exception is expected");
}
return new MockDataRequestTranslator();
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#isLoggedIn()
*/
protected boolean isLoggedIn()
{
if(getShouldTimeout()) {
try {
Thread.sleep((getTimeout() + 5) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(getIsLoggedInThrows()) {
throw new NullPointerException("This exception is expected");
}
return getState().isLoggedIn();
}
/**
* Get the state value.
*
* @return a <code>TestMarketDataFeed</code> value
*/
public State getState()
{
return mState;
}
/**
* Sets the state value.
*
* @param a <code>TestMarketDataFeed</code> value
*/
private void setState(State inState)
{
mState = inState;
}
/**
* Get the allowLogin value.
*
* @return a <code>TestMarketDataFeed</code> value
*/
public boolean getLoginFails()
{
return mLoginFails;
}
/**
* Sets the allowLogin value.
*
* @param a <code>TestMarketDataFeed</code> value
*/
public void setLoginFails(boolean inAllowLogin)
{
mLoginFails = inAllowLogin;
}
/**
* Get the initFails value.
*
* @return a <code>TestMarketDataFeed</code> value
*/
public boolean isInitFails()
{
return mInitFails;
}
/**
* Sets the initFails value.
*
* @param a <code>TestMarketDataFeed</code> value
*/
public void setInitFails(boolean inInitFails)
{
mInitFails = inInitFails;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#doInitialize(org.marketcetera.marketdata.AbstractMarketDataFeedToken)
*/
protected boolean doInitialize(MockMarketDataFeedToken inToken)
throws InterruptedException
{
if(getInitThrows()) {
throw new NullPointerException("This exception is expected");
}
if(isInitFails()) {
super.doInitialize();
return false;
}
return super.doInitialize();
}
/**
* Get the executionFails value.
*
* @return a <code>TestMarketDataFeed</code> value
*/
public boolean getExecutionFails()
{
return mExecutionFails;
}
/**
* Sets the executionFails value.
*
* @param a <code>TestMarketDataFeed</code> value
*/
public void setExecutionFails(boolean inExecutionFails)
{
mExecutionFails = inExecutionFails;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#doCancel(java.lang.String)
*/
protected void doCancel(String inHandle)
{
mCanceledHandles.add(inHandle);
if(isCancelFails()) {
throw new NullPointerException("This exception is expected");
}
}
public List<String> getCanceledHandles()
{
return new ArrayList<String>(mCanceledHandles);
}
public List<String> getCreatedHandles()
{
return new ArrayList<String>(mCreatedHandles);
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#getEventTranslator()
*/
protected MockEventTranslator getEventTranslator()
{
if(getGetEventTranslatorThrows()) {
throw new NullPointerException("This exception is expected");
}
return MockEventTranslator.getTestEventTranslator();
}
/**
* Get the cancelFails value.
*
* @return a <code>TestMarketDataFeed</code> value
*/
public boolean isCancelFails()
{
return mCancelFails;
}
/**
* Sets the cancelFails value.
*
* @param a <code>TestMarketDataFeed</code> value
*/
public void setCancelFails(boolean inCancelFails)
{
mCancelFails = inCancelFails;
}
@Override
protected boolean beforeDoExecute(MockMarketDataFeedToken inToken)
{
if(getBeforeExecuteThrows()) {
throw new NullPointerException("This exception is expected");
}
if(getBeforeExecuteReturnsFalse()) {
return false;
}
if(inToken != null &&
inToken.getShouldFail()) {
throw new NullPointerException("This exception is expected");
}
return super.beforeDoExecute(inToken);
}
public boolean getExecuteReturnsNothing()
{
return mExecuteReturnsNothing;
}
public void setExecuteReturnsNothing(boolean executeReturnsNothing)
{
mExecuteReturnsNothing = executeReturnsNothing;
}
public boolean getIsLoggedInThrows()
{
return mIsLoggedInThrows;
}
public void setIsLoggedInThrows(boolean inIsLoggedInThrows)
{
mIsLoggedInThrows = inIsLoggedInThrows;
}
public boolean getLoginThrows()
{
return mLoginThrows;
}
public void setLoginThrows(boolean inLoginThrows)
{
mLoginThrows = inLoginThrows;
}
public boolean getInitThrows()
{
return mInitThrows;
}
public void setInitThrows(boolean inInitThrows)
{
mInitThrows = inInitThrows;
}
public boolean getBeforeExecuteThrows()
{
return mBeforeExecuteThrows;
}
public void setBeforeExecuteThrows(boolean inBeforeExecuteThrows)
{
mBeforeExecuteThrows = inBeforeExecuteThrows;
}
public boolean getGenerateTokenThrows()
{
return mGenerateTokenThrows;
}
public void setGenerateTokenThrows(boolean inGenerateTokenThrows)
{
mGenerateTokenThrows = inGenerateTokenThrows;
}
public boolean getGetEventTranslatorThrows()
{
return mGetEventTranslatorThrows;
}
public void setGetEventTranslatorThrows(boolean inGetEventTranslatorThrows)
{
mGetEventTranslatorThrows = inGetEventTranslatorThrows;
}
public boolean getBeforeExecuteReturnsFalse()
{
return mBeforeExecuteReturnsFalse;
}
public void setBeforeExecuteReturnsFalse(boolean inBeforeExecuteReturnsFalse)
{
mBeforeExecuteReturnsFalse = inBeforeExecuteReturnsFalse;
}
public boolean getGetMessageTranslatorThrows()
{
return mGetMessageTranslatorThrows;
}
public void setGetMessageTranslatorThrows(boolean inGetMessageTranslatorThrows)
{
mGetMessageTranslatorThrows = inGetMessageTranslatorThrows;
}
public boolean getAfterExecuteThrows()
{
return mAfterExecuteThrows;
}
public void setAfterExecuteThrows(boolean inAfterExecuteThrows)
{
mAfterExecuteThrows = inAfterExecuteThrows;
}
public boolean getExecuteReturnsNull()
{
return mExecuteReturnsNull;
}
public void setExecuteReturnsNull(boolean inExecuteReturnsNull)
{
mExecuteReturnsNull = inExecuteReturnsNull;
}
/**
* Causes the given data to be submitted in reference to the given handle.
*
* <p>This method can be used to simulate a repeatedly-updated subscription.
*
* @param inHandle a <code>String</code> value
* @param inData an <code>Object</code> value
*/
public void submitData(String inHandle,
Object inData)
{
dataReceived(inHandle,
inData);
}
/**
* Get the timeout value.
*
* @return a <code>TestMarketDataFeed</code> value
*/
public boolean getShouldTimeout()
{
return mShouldTimeout;
}
/**
* Sets the timeout value.
*
* @param a <code>TestMarketDataFeed</code> value
*/
public void setShouldTimeout(boolean inTimeout)
{
mShouldTimeout = inTimeout;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#getTimeout()
*/
@Override
protected long getTimeout()
{
if(getShouldTimeout()) {
mTimeout = 10;
return mTimeout;
}
return super.getTimeout();
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.AbstractMarketDataFeed#doMarketDataRequest(java.lang.Object)
*/
@Override
protected List<String> doMarketDataRequest(String inData)
throws FeedException
{
if(getExecutionFails()) {
throw new FeedException(EXPECTED_EXCEPTION);
}
if(mDelay > 0) {
try {
Thread.sleep(sRandom.nextInt(mDelay));
} catch (InterruptedException e) {
throw new FeedException(e);
}
}
String handle = String.format("%d",
++mCounter);
if(!getExecuteReturnsNothing() &&
!getExecuteReturnsNull()) {
mCreatedHandles.add(handle);
mQueue.add(handle);
}
if(getExecuteReturnsNull()) {
return null;
}
return getExecuteReturnsNothing() ? new ArrayList<String>() : Arrays.asList(handle);
}
}